forked from I2P_Developers/i2p.i2p
* New interface for clients started via clients.config, and a new
manager to track the lifecycle and start/stop clients on demand.
Not hooked in to console yet, untested.
(ticket #347)
This commit is contained in:
58
core/java/src/net/i2p/app/ClientApp.java
Normal file
58
core/java/src/net/i2p/app/ClientApp.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package net.i2p.app;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a class started via clients.config implements this interface,
|
||||||
|
* it will be used to manage the client, instead of starting with main()
|
||||||
|
*
|
||||||
|
* Clients implementing this interface MUST provide the following constructor:
|
||||||
|
*
|
||||||
|
* public MyClientApp(I2PAppContext context, ClientAppManager listener, String[] args) {...}
|
||||||
|
*
|
||||||
|
* All parameters are non-null.
|
||||||
|
* This constructor is for instantiation only.
|
||||||
|
* Do not take a long time. Do not block. Never start threads or processes in it.
|
||||||
|
* The ClientAppState of the returned object must be INITIALIZED,
|
||||||
|
* or else throw something.
|
||||||
|
* The startup() method will be called next.
|
||||||
|
*
|
||||||
|
* Never ever hold a static reference to the context or anything derived from it.
|
||||||
|
*
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public interface ClientApp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not take a long time. Do not block. Start threads here if necessary.
|
||||||
|
* Client must call ClientAppManager.notify() at least once within this
|
||||||
|
* method to change the state from INITIALIZED to something else.
|
||||||
|
* Will not be called multiple times on the same object.
|
||||||
|
*/
|
||||||
|
public void startup() throws Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not take a long time. Do not block. Use a thread if necessary.
|
||||||
|
* If previously running, client must call ClientAppManager.notify() at least once within this
|
||||||
|
* method to change the state to STOPPING or STOPPED.
|
||||||
|
* May be called multiple times on the same object, in any state.
|
||||||
|
*/
|
||||||
|
public void shutdown(String[] args) throws Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current state of the ClientApp.
|
||||||
|
*/
|
||||||
|
public ClientAppState getState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The generic name of the ClientApp, used for registration,
|
||||||
|
* e.g. "console". Do not translate.
|
||||||
|
*/
|
||||||
|
public String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dislplay name of the ClientApp, used in user interfaces.
|
||||||
|
* The app must translate.
|
||||||
|
*/
|
||||||
|
public String getDisplayName();
|
||||||
|
}
|
||||||
51
core/java/src/net/i2p/app/ClientAppManager.java
Normal file
51
core/java/src/net/i2p/app/ClientAppManager.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package net.i2p.app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the router of events, and provide methods for
|
||||||
|
* client apps to find each other.
|
||||||
|
*
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public interface ClientAppManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be called on all state transitions except
|
||||||
|
* from UNINITIALIZED to INITIALIZED.
|
||||||
|
*
|
||||||
|
* @param app non-null
|
||||||
|
* @param state non-null
|
||||||
|
* @param message may be null
|
||||||
|
* @param e may be null
|
||||||
|
*/
|
||||||
|
public void notify(ClientApp app, ClientAppState state, String message, Exception e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register with the manager under the given name,
|
||||||
|
* so that other clients may find it.
|
||||||
|
* Only required for apps used by other apps.
|
||||||
|
*
|
||||||
|
* @param app non-null
|
||||||
|
* @param name non-null
|
||||||
|
* @return true if successful, false if duplicate name
|
||||||
|
*/
|
||||||
|
public boolean register(ClientApp app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister with the manager. Name must be the same as that from register().
|
||||||
|
* Only required for apps used by other apps.
|
||||||
|
*
|
||||||
|
* @param app non-null
|
||||||
|
* @param name non-null
|
||||||
|
*/
|
||||||
|
public void unregister(ClientApp app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a registered app.
|
||||||
|
* Only used for apps finding other apps.
|
||||||
|
*
|
||||||
|
* @param app non-null
|
||||||
|
* @param name non-null
|
||||||
|
* @return client app or null
|
||||||
|
*/
|
||||||
|
public ClientApp getRegisteredApp(String name);
|
||||||
|
}
|
||||||
25
core/java/src/net/i2p/app/ClientAppState.java
Normal file
25
core/java/src/net/i2p/app/ClientAppState.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package net.i2p.app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status of a client application.
|
||||||
|
* ClientAppManager.notify() must be called on all state transitions except
|
||||||
|
* from UNINITIALIZED to INITIALIZED.
|
||||||
|
*
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public enum ClientAppState {
|
||||||
|
/** initial value */
|
||||||
|
UNINITIALIZED,
|
||||||
|
/** after constructor is complete */
|
||||||
|
INITIALIZED,
|
||||||
|
STARTING,
|
||||||
|
START_FAILED,
|
||||||
|
RUNNING,
|
||||||
|
STOPPING,
|
||||||
|
/** stopped normally */
|
||||||
|
STOPPED,
|
||||||
|
/** stopped abnormally */
|
||||||
|
CRASHED,
|
||||||
|
/** forked as a new process, status unknown from now on */
|
||||||
|
FORKED
|
||||||
|
}
|
||||||
18
core/java/src/net/i2p/app/package.html
Normal file
18
core/java/src/net/i2p/app/package.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Interfaces for classes to be started and stopped via clients.config.
|
||||||
|
Classes implementing the ClientApp interface will be controlled with
|
||||||
|
the that interface instead of being started with main().
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The benefits for clients using this interface:
|
||||||
|
<ul>
|
||||||
|
<li>Get the current context via the constructor
|
||||||
|
<li>Complete life cycle management by the router
|
||||||
|
<li>Avoid the need for static references
|
||||||
|
<li>Ability to find other clients without using static references
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -18,6 +18,7 @@ import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
|||||||
import net.i2p.router.peermanager.PeerManagerFacadeImpl;
|
import net.i2p.router.peermanager.PeerManagerFacadeImpl;
|
||||||
import net.i2p.router.peermanager.ProfileManagerImpl;
|
import net.i2p.router.peermanager.ProfileManagerImpl;
|
||||||
import net.i2p.router.peermanager.ProfileOrganizer;
|
import net.i2p.router.peermanager.ProfileOrganizer;
|
||||||
|
import net.i2p.router.startup.RouterAppManager;
|
||||||
import net.i2p.router.transport.CommSystemFacadeImpl;
|
import net.i2p.router.transport.CommSystemFacadeImpl;
|
||||||
import net.i2p.router.transport.FIFOBandwidthLimiter;
|
import net.i2p.router.transport.FIFOBandwidthLimiter;
|
||||||
import net.i2p.router.transport.OutboundMessageRegistry;
|
import net.i2p.router.transport.OutboundMessageRegistry;
|
||||||
@@ -58,14 +59,22 @@ public class RouterContext extends I2PAppContext {
|
|||||||
private MessageValidator _messageValidator;
|
private MessageValidator _messageValidator;
|
||||||
//private MessageStateMonitor _messageStateMonitor;
|
//private MessageStateMonitor _messageStateMonitor;
|
||||||
private RouterThrottle _throttle;
|
private RouterThrottle _throttle;
|
||||||
|
private RouterAppManager _appManager;
|
||||||
private final Set<Runnable> _finalShutdownTasks;
|
private final Set<Runnable> _finalShutdownTasks;
|
||||||
// split up big lock on this to avoid deadlocks
|
// split up big lock on this to avoid deadlocks
|
||||||
|
private volatile boolean _initialized;
|
||||||
private final Object _lock1 = new Object(), _lock2 = new Object();
|
private final Object _lock1 = new Object(), _lock2 = new Object();
|
||||||
|
|
||||||
private static final List<RouterContext> _contexts = new CopyOnWriteArrayList();
|
private static final List<RouterContext> _contexts = new CopyOnWriteArrayList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller MUST call initAll() after instantiation.
|
||||||
|
*/
|
||||||
public RouterContext(Router router) { this(router, null); }
|
public RouterContext(Router router) { this(router, null); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller MUST call initAll() after instantiation.
|
||||||
|
*/
|
||||||
public RouterContext(Router router, Properties envProps) {
|
public RouterContext(Router router, Properties envProps) {
|
||||||
super(filterProps(envProps));
|
super(filterProps(envProps));
|
||||||
_router = router;
|
_router = router;
|
||||||
@@ -141,7 +150,9 @@ public class RouterContext extends I2PAppContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void initAll() {
|
public synchronized void initAll() {
|
||||||
|
if (_initialized)
|
||||||
|
throw new IllegalStateException();
|
||||||
if (getBooleanProperty("i2p.dummyClientFacade"))
|
if (getBooleanProperty("i2p.dummyClientFacade"))
|
||||||
System.err.println("i2p.dummyClientFacade currently unsupported");
|
System.err.println("i2p.dummyClientFacade currently unsupported");
|
||||||
_clientManagerFacade = new ClientManagerFacadeImpl(this);
|
_clientManagerFacade = new ClientManagerFacadeImpl(this);
|
||||||
@@ -182,6 +193,8 @@ public class RouterContext extends I2PAppContext {
|
|||||||
_messageValidator = new MessageValidator(this);
|
_messageValidator = new MessageValidator(this);
|
||||||
_throttle = new RouterThrottleImpl(this);
|
_throttle = new RouterThrottleImpl(this);
|
||||||
//_throttle = new RouterDoSThrottle(this);
|
//_throttle = new RouterDoSThrottle(this);
|
||||||
|
_appManager = new RouterAppManager(this);
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -495,4 +508,13 @@ public class RouterContext extends I2PAppContext {
|
|||||||
public InternalClientManager internalClientManager() {
|
public InternalClientManager internalClientManager() {
|
||||||
return _clientManagerFacade;
|
return _clientManagerFacade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The RouterAppManager.
|
||||||
|
* @return the manager
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public RouterAppManager clientAppManager() {
|
||||||
|
return _appManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
router/java/src/net/i2p/router/app/RouterApp.java
Normal file
24
router/java/src/net/i2p/router/app/RouterApp.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package net.i2p.router.app;
|
||||||
|
|
||||||
|
import net.i2p.app.ClientApp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a class started via clients.config implements this interface,
|
||||||
|
* it will be used to manage the client, instead of starting with main()
|
||||||
|
*
|
||||||
|
* Clients implementing this interface MUST provide the following constructor:
|
||||||
|
*
|
||||||
|
* public MyClientApp(RouterContext context, ClientAppManager listener, String[] args) {...}
|
||||||
|
*
|
||||||
|
* All parameters are non-null.
|
||||||
|
* This constructor is for instantiation only.
|
||||||
|
* Do not take a long time. Do not block. Never start threads or processes in it.
|
||||||
|
* The ClientAppState of the returned object must be INITIALIZED,
|
||||||
|
* or else throw something.
|
||||||
|
* The startup() method will be called next.
|
||||||
|
*
|
||||||
|
* Never ever hold a static reference to the context or anything derived from it.
|
||||||
|
*
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public interface RouterApp extends ClientApp {}
|
||||||
18
router/java/src/net/i2p/router/app/package.html
Normal file
18
router/java/src/net/i2p/router/app/package.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Interface for classes to be started and stopped via clients.config.
|
||||||
|
Classes implementing the RouterApp interface will be controlled with
|
||||||
|
the that interface instead of being started with main().
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The benefits for clients using this interface:
|
||||||
|
<ul>
|
||||||
|
<li>Get the current context via the constructor
|
||||||
|
<li>Complete life cycle management by the router
|
||||||
|
<li>Avoid the need for static references
|
||||||
|
<li>Ability to find other clients without using static references
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
package net.i2p.router.startup;
|
package net.i2p.router.startup;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.app.ClientApp;
|
||||||
|
import net.i2p.app.ClientAppManager;
|
||||||
import net.i2p.router.JobImpl;
|
import net.i2p.router.JobImpl;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.router.app.RouterApp;
|
||||||
import net.i2p.util.I2PThread;
|
import net.i2p.util.I2PThread;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@@ -24,6 +29,7 @@ public class LoadClientAppsJob extends JobImpl {
|
|||||||
super(ctx);
|
super(ctx);
|
||||||
_log = ctx.logManager().getLog(LoadClientAppsJob.class);
|
_log = ctx.logManager().getLog(LoadClientAppsJob.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
synchronized (LoadClientAppsJob.class) {
|
synchronized (LoadClientAppsJob.class) {
|
||||||
if (_loaded) return;
|
if (_loaded) return;
|
||||||
@@ -42,7 +48,7 @@ public class LoadClientAppsJob extends JobImpl {
|
|||||||
String argVal[] = parseArgs(app.args);
|
String argVal[] = parseArgs(app.args);
|
||||||
if (app.delay <= 0) {
|
if (app.delay <= 0) {
|
||||||
// run this guy now
|
// run this guy now
|
||||||
runClient(app.className, app.clientName, argVal, _log);
|
runClient(app.className, app.clientName, argVal, getContext(), _log);
|
||||||
} else {
|
} else {
|
||||||
// wait before firing it up
|
// wait before firing it up
|
||||||
getContext().jobQueue().addJob(new DelayedRunClient(getContext(), app.className, app.clientName, argVal, app.delay));
|
getContext().jobQueue().addJob(new DelayedRunClient(getContext(), app.className, app.clientName, argVal, app.delay));
|
||||||
@@ -73,9 +79,11 @@ public class LoadClientAppsJob extends JobImpl {
|
|||||||
_cl = cl;
|
_cl = cl;
|
||||||
getTiming().setStartAfter(getContext().clock().now() + delay);
|
getTiming().setStartAfter(getContext().clock().now() + delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return "Delayed client job"; }
|
public String getName() { return "Delayed client job"; }
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
runClient(_className, _clientName, _args, _log, _threadGroup, _cl);
|
runClient(_className, _clientName, _args, getContext(), _log, _threadGroup, _cl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,8 +187,8 @@ public class LoadClientAppsJob extends JobImpl {
|
|||||||
* @param clientName can be null
|
* @param clientName can be null
|
||||||
* @param args can be null
|
* @param args can be null
|
||||||
*/
|
*/
|
||||||
public static void runClient(String className, String clientName, String args[], Log log) {
|
public static void runClient(String className, String clientName, String args[], RouterContext ctx, Log log) {
|
||||||
runClient(className, clientName, args, log, null, null);
|
runClient(className, clientName, args, ctx, log, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,15 +200,15 @@ public class LoadClientAppsJob extends JobImpl {
|
|||||||
* @param cl can be null
|
* @param cl can be null
|
||||||
* @since 0.7.13
|
* @since 0.7.13
|
||||||
*/
|
*/
|
||||||
public static void runClient(String className, String clientName, String args[], Log log,
|
public static void runClient(String className, String clientName, String args[], RouterContext ctx, Log log,
|
||||||
ThreadGroup threadGroup, ClassLoader cl) {
|
ThreadGroup threadGroup, ClassLoader cl) {
|
||||||
if (log.shouldLog(Log.INFO))
|
if (log.shouldLog(Log.INFO))
|
||||||
log.info("Loading up the client application " + clientName + ": " + className + " " + Arrays.toString(args));
|
log.info("Loading up the client application " + clientName + ": " + className + " " + Arrays.toString(args));
|
||||||
I2PThread t;
|
I2PThread t;
|
||||||
if (threadGroup != null)
|
if (threadGroup != null)
|
||||||
t = new I2PThread(threadGroup, new RunApp(className, clientName, args, log, cl));
|
t = new I2PThread(threadGroup, new RunApp(className, clientName, args, ctx, log, cl));
|
||||||
else
|
else
|
||||||
t = new I2PThread(new RunApp(className, clientName, args, log, cl));
|
t = new I2PThread(new RunApp(className, clientName, args, ctx, log, cl));
|
||||||
if (clientName == null)
|
if (clientName == null)
|
||||||
clientName = className + " client";
|
clientName = className + " client";
|
||||||
t.setName(clientName);
|
t.setName(clientName);
|
||||||
@@ -214,16 +222,18 @@ public class LoadClientAppsJob extends JobImpl {
|
|||||||
private final String _className;
|
private final String _className;
|
||||||
private final String _appName;
|
private final String _appName;
|
||||||
private final String _args[];
|
private final String _args[];
|
||||||
|
private final RouterContext _ctx;
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
private final ClassLoader _cl;
|
private final ClassLoader _cl;
|
||||||
|
|
||||||
public RunApp(String className, String appName, String args[], Log log, ClassLoader cl) {
|
public RunApp(String className, String appName, String args[], RouterContext ctx, Log log, ClassLoader cl) {
|
||||||
_className = className;
|
_className = className;
|
||||||
_appName = appName;
|
_appName = appName;
|
||||||
if (args == null)
|
if (args == null)
|
||||||
_args = new String[0];
|
_args = new String[0];
|
||||||
else
|
else
|
||||||
_args = args;
|
_args = args;
|
||||||
|
_ctx = ctx;
|
||||||
_log = log;
|
_log = log;
|
||||||
if (cl == null)
|
if (cl == null)
|
||||||
_cl = ClassLoader.getSystemClassLoader();
|
_cl = ClassLoader.getSystemClassLoader();
|
||||||
@@ -234,14 +244,53 @@ public class LoadClientAppsJob extends JobImpl {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Class cls = Class.forName(_className, true, _cl);
|
Class cls = Class.forName(_className, true, _cl);
|
||||||
Method method = cls.getMethod("main", new Class[] { String[].class });
|
if (isRouterApp(cls)) {
|
||||||
method.invoke(cls, new Object[] { _args });
|
Constructor con = cls.getConstructor(RouterContext.class, ClientAppManager.class, String[].class);
|
||||||
|
RouterAppManager mgr = _ctx.clientAppManager();
|
||||||
|
Object[] conArgs = new Object[] {_ctx, _ctx.clientAppManager(), _args};
|
||||||
|
RouterApp app = (RouterApp) con.newInstance(conArgs);
|
||||||
|
mgr.addAndStart(app);
|
||||||
|
} 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);
|
||||||
|
} else {
|
||||||
|
Method method = cls.getMethod("main", new Class[] { String[].class });
|
||||||
|
method.invoke(cls, new Object[] { _args });
|
||||||
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
_log.log(Log.CRIT, "Error starting up the client class " + _className, t);
|
_log.log(Log.CRIT, "Error starting up the client class " + _className, t);
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info("Done running client application " + _appName);
|
_log.info("Done running client application " + _appName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isRouterApp(Class cls) {
|
||||||
|
return isInterface(cls, RouterApp.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isClientApp(Class cls) {
|
||||||
|
return isInterface(cls, ClientApp.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInterface(Class cls, Class intfc) {
|
||||||
|
try {
|
||||||
|
Class[] intfcs = cls.getInterfaces();
|
||||||
|
for (int i = 0; i < intfcs.length; i++) {
|
||||||
|
if (intfcs[i] == intfc)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return "Load up any client applications"; }
|
public String getName() { return "Load up any client applications"; }
|
||||||
|
|||||||
131
router/java/src/net/i2p/router/startup/RouterAppManager.java
Normal file
131
router/java/src/net/i2p/router/startup/RouterAppManager.java
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package net.i2p.router.startup;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the router of events, and provide methods for
|
||||||
|
* client apps to find each other.
|
||||||
|
*
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
public class RouterAppManager implements ClientAppManager {
|
||||||
|
|
||||||
|
private final RouterContext _context;
|
||||||
|
private final Log _log;
|
||||||
|
private final Set<ClientApp> _clients;
|
||||||
|
private final ConcurrentHashMap<String, ClientApp> _registered;
|
||||||
|
|
||||||
|
public RouterAppManager(RouterContext ctx) {
|
||||||
|
_context = ctx;
|
||||||
|
_log = ctx.logManager().getLog(RouterAppManager.class);
|
||||||
|
_clients = new ConcurrentHashSet(16);
|
||||||
|
_registered = new ConcurrentHashMap(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAndStart(ClientApp app) {
|
||||||
|
_clients.add(app);
|
||||||
|
try {
|
||||||
|
app.startup();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
_clients.remove(app);
|
||||||
|
_log.error("Client " + app + " failed to start");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientAppManager methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be called on all state transitions except
|
||||||
|
* from UNINITIALIZED to INITIALIZED.
|
||||||
|
*
|
||||||
|
* @param app non-null
|
||||||
|
* @param state non-null
|
||||||
|
* @param message may be null
|
||||||
|
* @param e may be null
|
||||||
|
*/
|
||||||
|
public void notify(ClientApp app, ClientAppState state, String message, Exception e) {
|
||||||
|
switch(state) {
|
||||||
|
case UNINITIALIZED:
|
||||||
|
case INITIALIZED:
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Client " + app.getDisplayName() + " called notify for" + state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STARTING:
|
||||||
|
case RUNNING:
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Client " + app.getDisplayName() + " called notify for" + state);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FORKED:
|
||||||
|
case STOPPING:
|
||||||
|
case STOPPED:
|
||||||
|
_clients.remove(app);
|
||||||
|
_registered.remove(app.getName(), app);
|
||||||
|
if (message == null)
|
||||||
|
message = "";
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info("Client " + app.getDisplayName() + " called notify for" + state +
|
||||||
|
' ' + message, e);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CRASHED:
|
||||||
|
case START_FAILED:
|
||||||
|
_clients.remove(app);
|
||||||
|
_registered.remove(app.getName(), app);
|
||||||
|
if (message == null)
|
||||||
|
message = "";
|
||||||
|
_log.log(Log.CRIT, "Client " + app.getDisplayName() + ' ' + state +
|
||||||
|
' ' + message, e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register with the manager under the given name,
|
||||||
|
* so that other clients may find it.
|
||||||
|
* Only required for apps used by other apps.
|
||||||
|
*
|
||||||
|
* @param app non-null
|
||||||
|
* @param name non-null
|
||||||
|
* @return true if successful, false if duplicate name
|
||||||
|
*/
|
||||||
|
public boolean register(ClientApp app) {
|
||||||
|
if (!_clients.contains(app))
|
||||||
|
return false;
|
||||||
|
// TODO if old app in there is not running and != this app, allow replacement
|
||||||
|
return _registered.putIfAbsent(app.getName(), app) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister with the manager. Name must be the same as that from register().
|
||||||
|
* Only required for apps used by other apps.
|
||||||
|
*
|
||||||
|
* @param app non-null
|
||||||
|
* @param name non-null
|
||||||
|
*/
|
||||||
|
public void unregister(ClientApp app) {
|
||||||
|
_registered.remove(app.getName(), app);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a registered app.
|
||||||
|
* Only used for apps finding other apps.
|
||||||
|
* Do not hold a static reference.
|
||||||
|
* If you only need to find a port, use the PortMapper instead.
|
||||||
|
*
|
||||||
|
* @param app non-null
|
||||||
|
* @param name non-null
|
||||||
|
* @return client app or null
|
||||||
|
*/
|
||||||
|
public ClientApp getRegisteredApp(String name) {
|
||||||
|
return _registered.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user