* ClientAppManager: Add method to look up clients by class and args

* Console: Implement stopping of clients using the ClientApp interface
            (ticket #347)
This commit is contained in:
zzz
2013-04-16 14:59:18 +00:00
parent 7d0f626fd5
commit a3aee79e9c
7 changed files with 137 additions and 30 deletions

View File

@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 4;
public final static long BUILD = 5;
/** for example "-test" */
public final static String EXTRA = "";

View File

@@ -87,8 +87,18 @@ public class LoadClientAppsJob extends JobImpl {
}
}
/**
* Parse arg string into an array of args.
* Spaces or tabs separate args.
* Args may be single- or double-quoted if they contain spaces or tabs.
* There is no provision for escaping quotes.
* A quoted string may not contain a quote of any kind.
*
* @param args may be null
* @return non-null, 0-length if args is null
*/
public static String[] parseArgs(String args) {
List argList = new ArrayList(4);
List<String> argList = new ArrayList(4);
if (args != null) {
char data[] = args.toCharArray();
StringBuilder buf = new StringBuilder(32);
@@ -130,8 +140,9 @@ public class LoadClientAppsJob extends JobImpl {
}
}
String rv[] = new String[argList.size()];
for (int i = 0; i < argList.size(); i++)
rv[i] = (String)argList.get(i);
for (int i = 0; i < argList.size(); i++) {
rv[i] = argList.get(i);
}
return rv;
}
@@ -151,6 +162,7 @@ public class LoadClientAppsJob extends JobImpl {
/**
* Run client in this thread.
* Used for plugin sub-clients only. Does not register with the ClientAppManager.
*
* @param clientName can be null
* @param args can be null
@@ -163,6 +175,7 @@ public class LoadClientAppsJob extends JobImpl {
/**
* Run client in this thread.
* Used for plugin sub-clients only. Does not register with the ClientAppManager.
*
* @param clientName can be null
* @param args can be null
@@ -249,13 +262,13 @@ public class LoadClientAppsJob extends JobImpl {
RouterAppManager mgr = _ctx.clientAppManager();
Object[] conArgs = new Object[] {_ctx, _ctx.clientAppManager(), _args};
RouterApp app = (RouterApp) con.newInstance(conArgs);
mgr.addAndStart(app);
mgr.addAndStart(app, _args);
} else if (isClientApp(cls)) {
Constructor con = cls.getConstructor(I2PAppContext.class, ClientAppManager.class, String[].class);
RouterAppManager mgr = _ctx.clientAppManager();
Object[] conArgs = new Object[] {_ctx, _ctx.clientAppManager(), _args};
ClientApp app = (ClientApp) con.newInstance(conArgs);
mgr.addAndStart(app);
mgr.addAndStart(app, _args);
} else {
Method method = cls.getMethod("main", new Class[] { String[].class });
method.invoke(cls, new Object[] { _args });

View File

@@ -1,12 +1,12 @@
package net.i2p.router.startup;
import java.util.Set;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.app.*;
import static net.i2p.app.ClientAppState.*;
import net.i2p.router.RouterContext;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log;
/**
@@ -19,26 +19,59 @@ public class RouterAppManager implements ClientAppManager {
private final RouterContext _context;
private final Log _log;
private final Set<ClientApp> _clients;
// client to args
// this assumes clients do not override equals()
private final ConcurrentHashMap<ClientApp, String[]> _clients;
// registered name to client
private final ConcurrentHashMap<String, ClientApp> _registered;
public RouterAppManager(RouterContext ctx) {
_context = ctx;
_log = ctx.logManager().getLog(RouterAppManager.class);
_clients = new ConcurrentHashSet(16);
_clients = new ConcurrentHashMap(16);
_registered = new ConcurrentHashMap(8);
}
public void addAndStart(ClientApp app) {
_clients.add(app);
/**
* @param args the args that were used to instantiate the app, non-null, may be zero-length
* @return success
* @throws IllegalArgumentException if already added
*/
public boolean addAndStart(ClientApp app, String[] args) {
if (_log.shouldLog(Log.INFO))
_log.info("Adding and starting " + app + " with class " + app.getClass().getName() + " and args " + Arrays.toString(args));
String[] old = _clients.put(app, args);
if (old != null)
throw new IllegalArgumentException("already added");
try {
app.startup();
return true;
} catch (Throwable t) {
_clients.remove(app);
_log.error("Client " + app + " failed to start", t);
return false;
}
}
/**
* Get the first known ClientApp with this class name and exact arguments.
* Caller may then retrieve or control the state of the returned client.
* A client will generally be found only if it is running or transitioning;
* after it is stopped it will not be tracked by the manager.
*
* @param args non-null, may be zero-length
* @return client app or null
* @since 0.9.6
*/
public ClientApp getClientApp(String className, String[] args) {
for (Map.Entry<ClientApp, String[]> e : _clients.entrySet()) {
if (e.getKey().getClass().getName().equals(className) &&
Arrays.equals(e.getValue(), args))
return e.getKey();
}
return null;
}
// ClientAppManager methods
/**
@@ -97,7 +130,7 @@ public class RouterAppManager implements ClientAppManager {
* @return true if successful, false if duplicate name
*/
public boolean register(ClientApp app) {
if (!_clients.contains(app))
if (!_clients.containsKey(app))
return false;
// TODO if old app in there is not running and != this app, allow replacement
return _registered.putIfAbsent(app.getName(), app) == null;