* I2PTunnel:

- More client options cleanups
   - Options changes now propagate to running
     socket managers and sessions, and through to the router
 * SocketManager:
   - Simplify factory, use 4-arg constructor,
     make fields final, deprecate 0-arg constructor
   - Improve how options are updated
   - Javadocs
This commit is contained in:
zzz
2012-06-14 19:44:47 +00:00
parent 64221fb3fb
commit e522ffad4e
10 changed files with 255 additions and 115 deletions

View File

@@ -193,7 +193,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
}
/** @return non-null */
/** @return A copy, non-null */
List<I2PSession> getSessions() {
return new ArrayList(_sessions);
}
@@ -208,6 +208,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
_sessions.remove(session);
}
/**
* Generic options used for clients and servers
* @return not a copy
*/
public Properties getClientOptions() { return _clientOptions; }
private void addtask(I2PTunnelTask tsk) {
@@ -327,11 +331,13 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
/**
* Configure the extra I2CP options to use in any subsequent I2CP sessions.
* Generic options used for clients and servers
* Usage: "clientoptions[ key=value]*" .
*
* Sets the event "clientoptions_onResult" = "ok" after completion.
*
* Deprecated use setClientOptions()
* Deprecated To be made private, use setClientOptions().
* This does NOT update a running TunnelTask.
*
* @param args each args[i] is a key=value pair to add to the options
* @param l logger to receive events and output
@@ -351,17 +357,22 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
}
/**
* A more efficient runClientOptions()
* Generic options used for clients and servers.
* This DOES update a running TunnelTask, but NOT the session.
* A more efficient runClientOptions().
*
* @param opts non-null
* @since 0.9.1
*/
public void setClientOptions(Properties opts) {
_clientOptions.clear();
for (Map.Entry e : opts.entrySet()) {
String key = (String) e.getKey();
String val = (String) e.getValue();
_clientOptions.setProperty(key, val);
for (Iterator iter = _clientOptions.keySet().iterator(); iter.hasNext();) {
Object key = iter.next();
if (!opts.containsKey(key))
iter.remove();
}
_clientOptions.putAll(opts);
for (I2PTunnelTask task : tasks) {
task.optionsUpdated(this);
}
notifyEvent("clientoptions_onResult", "ok");
}

View File

@@ -495,6 +495,22 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
return opts;
}
/**
* Update the I2PSocketManager.
*
* @since 0.9.1
*/
@Override
public void optionsUpdated(I2PTunnel tunnel) {
if (getTunnel() != tunnel)
return;
I2PSocketManager sm = _ownDest ? sockMgr : socketManager;
if (sm == null)
return;
Properties props = tunnel.getClientOptions();
sm.setDefaultOptions(sockMgr.buildOptions(props));
}
/**
* Create a new I2PSocket towards to the specified destination,
* adding it to the list of connections actually managed by this

View File

@@ -295,6 +295,19 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
}
}
/**
* Update the I2PSocketManager.
*
* @since 0.9.1
*/
@Override
public void optionsUpdated(I2PTunnel tunnel) {
if (getTunnel() != tunnel || sockMgr == null)
return;
Properties props = tunnel.getClientOptions();
sockMgr.setDefaultOptions(sockMgr.buildOptions(props));
}
protected int getHandlerCount() {
int rv = DEFAULT_HANDLER_COUNT;
String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT);

View File

@@ -58,6 +58,15 @@ public abstract class I2PTunnelTask extends EventDispatcherImpl {
public abstract boolean close(boolean forced);
/**
* Notify the task that I2PTunnel's options have been updated.
* Extending classes should override and call I2PTunnel.getClientOptions(),
* then update the I2PSocketManager.
*
* @since 0.9.1
*/
public void optionsUpdated(I2PTunnel tunnel) {}
/**
* For tasks that don't call I2PTunnel.addSession() directly
* @since 0.8.13

View File

@@ -328,6 +328,7 @@ public class TunnelController implements Logging {
_log.info("Releasing session " + s);
TunnelControllerGroup.getInstance().release(this, s);
}
// _sessions.clear() ????
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("No sessions to release? for " + getName());
@@ -386,7 +387,13 @@ public class TunnelController implements Logging {
}
}
private void setSessionOptions() {
/**
* These are the ones stored with a prefix of "option."
*
* @return keys with the "option." prefix stripped
* @since 0.9.1 Much better than getClientOptions()
*/
public Properties getClientOptionProps() {
Properties opts = new Properties();
for (Map.Entry e : _config.entrySet()) {
String key = (String) e.getKey();
@@ -396,7 +403,11 @@ public class TunnelController implements Logging {
opts.setProperty(key, val);
}
}
_tunnel.setClientOptions(opts);
return opts;
}
private void setSessionOptions() {
_tunnel.setClientOptions(getClientOptionProps());
}
private void setI2CPOptions() {
@@ -430,25 +441,37 @@ public class TunnelController implements Logging {
startTunnel();
}
/**
* As of 0.9.1, updates the options on an existing session
*/
public void setConfig(Properties config, String prefix) {
Properties props = new Properties();
for (Iterator iter = config.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = config.getProperty(key);
for (Map.Entry e : config.entrySet()) {
String key = (String) e.getKey();
if (key.startsWith(prefix)) {
key = key.substring(prefix.length());
String val = (String) e.getValue();
props.setProperty(key, val);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Set prop [" + key + "] to [" + val + "]");
}
}
_config = props;
// tell i2ptunnel, who will tell the TunnelTask, who will tell the SocketManager
setSessionOptions();
if (_running && _sessions != null) {
for (I2PSession s : _sessions) {
// tell the router via the session
if (!s.isClosed()) {
s.updateOptions(_tunnel.getClientOptions());
}
}
}
}
public Properties getConfig(String prefix) {
Properties rv = new Properties();
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = _config.getProperty(key);
for (Map.Entry e : _config.entrySet()) {
String key = (String) e.getKey();
String val = (String) e.getValue();
rv.setProperty(prefix + key, val);
}
return rv;
@@ -459,19 +482,27 @@ public class TunnelController implements Logging {
public String getDescription() { return _config.getProperty("description"); }
public String getI2CPHost() { return _config.getProperty("i2cpHost"); }
public String getI2CPPort() { return _config.getProperty("i2cpPort"); }
/**
* These are the ones with a prefix of "option."
*
* @return one big string of "key=val key=val ..."
* @deprecated why would you want this? Use getClientOptionProps() instead
*/
public String getClientOptions() {
StringBuilder opts = new StringBuilder(64);
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = _config.getProperty(key);
for (Map.Entry e : _config.entrySet()) {
String key = (String) e.getKey();
if (key.startsWith("option.")) {
key = key.substring("option.".length());
String val = (String) e.getValue();
if (opts.length() > 0) opts.append(' ');
opts.append(key).append('=').append(val);
}
}
return opts.toString();
}
public String getListenOnInterface() { return _config.getProperty("interface"); }
public String getTargetHost() { return _config.getProperty("targetHost"); }
public String getTargetPort() { return _config.getProperty("targetPort"); }
@@ -485,6 +516,7 @@ public class TunnelController implements Logging {
/** default true */
public boolean getStartOnLoad() { return Boolean.valueOf(_config.getProperty("startOnLoad", "true")).booleanValue(); }
public boolean getPersistentClientKey() { return Boolean.valueOf(_config.getProperty("option.persistentClientKey")).booleanValue(); }
public String getMyDestination() {
if (_tunnel != null) {
List<I2PSession> sessions = _tunnel.getSessions();

View File

@@ -376,18 +376,6 @@ public class EditBean extends IndexBean {
*/
private static Properties getOptions(TunnelController controller) {
if (controller == null) return null;
String opts = controller.getClientOptions();
StringTokenizer tok = new StringTokenizer(opts);
Properties props = new Properties();
while (tok.hasMoreTokens()) {
String pair = tok.nextToken();
int eq = pair.indexOf('=');
if ( (eq <= 0) || (eq >= pair.length()) )
continue;
String key = pair.substring(0, eq);
String val = pair.substring(eq+1);
props.setProperty(key, val);
}
return props;
return controller.getClientOptionProps();
}
}

View File

@@ -38,11 +38,32 @@ public interface I2PSocketManager {
*/
public void setAcceptTimeout(long ms);
public long getAcceptTimeout();
/**
* Update the options on a running socket manager.
* Parameters in the I2PSocketOptions interface may be changed directly
* with the setters; no need to use this method for those.
* This does NOT update the underlying I2CP or tunnel options; use getSession().updateOptions() for that.
* @param options as created from a call to buildOptions(properties), non-null
*/
public void setDefaultOptions(I2PSocketOptions options);
/**
* Current options, not a copy, setters may be used to make changes.
*/
public I2PSocketOptions getDefaultOptions();
public I2PServerSocket getServerSocket();
/**
* Create a copy of the current options, to be used in a setDefaultOptions() call.
*/
public I2PSocketOptions buildOptions();
/**
* Create a modified copy of the current options, to be used in a setDefaultOptions() call.
* @param opts The new options, may be null
*/
public I2PSocketOptions buildOptions(Properties opts);
/**
@@ -102,6 +123,10 @@ public interface I2PSocketManager {
public String getName();
public void setName(String name);
/**
* Deprecated - Factory will initialize.
* @throws UnsupportedOperationException always
*/
public void init(I2PAppContext context, I2PSession session, Properties opts, String name);
public void addDisconnectListener(DisconnectListener lsnr);

View File

@@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.Iterator;
import java.util.Properties;
@@ -161,40 +162,19 @@ public class I2PSocketManagerFactory {
}
private static I2PSocketManager createManager(I2PSession session, Properties opts, String name) {
//if (false) {
//I2PSocketManagerImpl mgr = new I2PSocketManagerImpl();
//mgr.setSession(session);
//mgr.setDefaultOptions(new I2PSocketOptions());
//return mgr;
//} else {
String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER);
if (classname != null) {
try {
Class cls = Class.forName(classname);
Object obj = cls.newInstance();
if (obj instanceof I2PSocketManager) {
I2PSocketManager mgr = (I2PSocketManager)obj;
I2PAppContext context = I2PAppContext.getGlobalContext();
mgr.init(context, session, opts, name);
return mgr;
} else {
throw new IllegalStateException("Invalid manager class [" + classname + "]");
}
} catch (ClassNotFoundException cnfe) {
_log.error("Error loading " + classname, cnfe);
throw new IllegalStateException("Invalid manager class [" + classname + "] - not found");
} catch (InstantiationException ie) {
_log.error("Error loading " + classname, ie);
throw new IllegalStateException("Invalid manager class [" + classname + "] - unable to instantiate");
} catch (IllegalAccessException iae) {
_log.error("Error loading " + classname, iae);
throw new IllegalStateException("Invalid manager class [" + classname + "] - illegal access");
}
} else {
throw new IllegalStateException("No manager class specified");
}
//}
I2PAppContext context = I2PAppContext.getGlobalContext();
String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER);
try {
Class cls = Class.forName(classname);
Constructor<I2PSocketManager> con = (Constructor<I2PSocketManager>)
cls.getConstructor(new Class[] {I2PAppContext.class, I2PSession.class, Properties.class, String.class});
I2PSocketManager mgr = con.newInstance(new Object[] {context, session, opts, name});
return mgr;
} catch (Throwable t) {
_log.log(Log.CRIT, "Error loading " + classname, t);
throw new IllegalStateException(t);
}
}
private static String getHost() {

View File

@@ -237,7 +237,33 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
*/
public ConnectionOptions(ConnectionOptions opts) {
super(opts);
if (opts != null) {
if (opts != null)
update(opts);
}
/**
* Update everything by copying over from opts
* @param opts non-null
* @since 0.9.1
*/
public void updateAll(ConnectionOptions opts) {
// user is unlikely to change these 6 between buildOptions() and setDefaultOptions(),
// since they may be updated directly, but just in case...
setConnectTimeout(opts.getConnectTimeout());
setReadTimeout(opts.getReadTimeout());
setWriteTimeout(opts.getWriteTimeout());
setMaxBufferSize(opts.getMaxBufferSize());
setLocalPort(opts.getLocalPort());
setPort(opts.getPort());
update(opts);
}
/**
* Update everything (except super) by copying over from opts
* @param opts non-null
* @since 0.9.1
*/
private void update(ConnectionOptions opts) {
setMaxWindowSize(opts.getMaxWindowSize());
setConnectDelay(opts.getConnectDelay());
setProfile(opts.getProfile());
@@ -265,7 +291,6 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
_maxTotalConnsPerMinute = opts.getMaxTotalConnsPerMinute();
_maxTotalConnsPerHour = opts.getMaxTotalConnsPerHour();
_maxTotalConnsPerDay = opts.getMaxTotalConnsPerDay();
}
}
@Override
@@ -301,6 +326,9 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
_maxTotalConnsPerDay = getInt(opts, PROP_MAX_TOTAL_CONNS_DAY, 0);
}
/**
* Note: NOT part of the interface
*/
@Override
public void setProperties(Properties opts) {
super.setProperties(opts);
@@ -611,30 +639,35 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
private void initLists(Properties opts) {
_accessListEnabled = getBool(opts, PROP_ENABLE_ACCESS_LIST, false);
_blackListEnabled = getBool(opts, PROP_ENABLE_BLACKLIST, false);
// Don't think these would ever be accessed simultaneously,
// but avoid concurrent modification just in case
Set<Hash> accessList, blackList;
if (_accessListEnabled)
_accessList = new HashSet();
accessList = new HashSet();
else
_accessList = Collections.EMPTY_SET;
accessList = Collections.EMPTY_SET;
if (_blackListEnabled)
_blackList = new HashSet();
blackList = new HashSet();
else
_blackList = Collections.EMPTY_SET;
if (!(_accessListEnabled || _blackListEnabled))
return;
String hashes = opts.getProperty(PROP_ACCESS_LIST);
if (hashes == null)
return;
StringTokenizer tok = new StringTokenizer(hashes, ", ");
while (tok.hasMoreTokens()) {
String hashstr = tok.nextToken();
Hash h = ConvertToHash.getHash(hashstr);
if (h == null)
error("bad list hash: " + hashstr);
else if (_blackListEnabled)
_blackList.add(h);
else
_accessList.add(h);
blackList = Collections.EMPTY_SET;
if (_accessListEnabled || _blackListEnabled) {
String hashes = opts.getProperty(PROP_ACCESS_LIST);
if (hashes == null)
return;
StringTokenizer tok = new StringTokenizer(hashes, ", ");
while (tok.hasMoreTokens()) {
String hashstr = tok.nextToken();
Hash h = ConvertToHash.getHash(hashstr);
if (h == null)
error("bad list hash: " + hashstr);
else if (_blackListEnabled)
blackList.add(h);
else
accessList.add(h);
}
}
_accessList = accessList;
_blackList = blackList;
if (_accessListEnabled && _accessList.isEmpty())
error("Connection access list enabled but no valid entries; no peers can connect");
else if (_blackListEnabled && _blackList.isEmpty())
@@ -647,9 +680,10 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
log.error(s);
}
@Override
/** doesn't include everything */
@Override
public String toString() {
StringBuilder buf = new StringBuilder(128);
StringBuilder buf = new StringBuilder(256);
buf.append("conDelay=").append(_connectDelay);
buf.append(" maxSize=").append(_maxMessageSize);
buf.append(" rtt=").append(_rtt);
@@ -663,6 +697,14 @@ class ConnectionOptions extends I2PSocketOptionsImpl {
buf.append(" inactivityTimeout=").append(_inactivityTimeout);
buf.append(" inboundBuffer=").append(_inboundBufferSize);
buf.append(" maxWindowSize=").append(_maxWindowSize);
buf.append(" blacklistSize=").append(_blackList.size());
buf.append(" whitelistSize=").append(_accessList.size());
buf.append(" maxConns=").append(_maxConnsPerMinute).append('/')
.append(_maxConnsPerHour).append('/')
.append(_maxConnsPerDay);
buf.append(" maxTotalConns=").append(_maxTotalConnsPerMinute).append('/')
.append(_maxTotalConnsPerHour).append('/')
.append(_maxTotalConnsPerDay);
return buf.toString();
}

View File

@@ -28,28 +28,46 @@ import net.i2p.util.Log;
* Direct instantiation by others is deprecated.
*/
public class I2PSocketManagerFull implements I2PSocketManager {
private I2PAppContext _context;
private Log _log;
private I2PSession _session;
private I2PServerSocketFull _serverSocket;
private final I2PAppContext _context;
private final Log _log;
private final I2PSession _session;
private final I2PServerSocketFull _serverSocket;
private StandardServerSocket _realServerSocket;
private ConnectionOptions _defaultOptions;
private final ConnectionOptions _defaultOptions;
private long _acceptTimeout;
private String _name;
private int _maxStreams;
private static int __managerId = 0;
private ConnectionManager _connectionManager;
private final ConnectionManager _connectionManager;
/**
* How long to wait for the client app to accept() before sending back CLOSE?
* This includes the time waiting in the queue. Currently set to 5 seconds.
*/
private static final long ACCEPT_TIMEOUT_DEFAULT = 5*1000;
/**
* @deprecated use 4-arg constructor
* @throws UnsupportedOperationException always
*/
public I2PSocketManagerFull() {
throw new UnsupportedOperationException();
}
/** how many streams will we allow at once? */
public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
/**
* @deprecated use 4-arg constructor
* @throws UnsupportedOperationException always
*/
public void init(I2PAppContext context, I2PSession session, Properties opts, String name) {
throw new UnsupportedOperationException();
}
/**
* This is what I2PSocketManagerFactory.createManager() returns.
* Direct instantiation by others is deprecated.
*
* @param context
* @param session
@@ -57,22 +75,6 @@ public class I2PSocketManagerFull implements I2PSocketManager {
* @param name
*/
public I2PSocketManagerFull(I2PAppContext context, I2PSession session, Properties opts, String name) {
this();
init(context, session, opts, name);
}
/** how many streams will we allow at once? */
public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
/**
*
*
* @param context
* @param session
* @param opts
* @param name
*/
public void init(I2PAppContext context, I2PSession session, Properties opts, String name) {
_context = context;
_session = session;
_log = _context.logManager().getLog(I2PSocketManagerFull.class);
@@ -98,7 +100,15 @@ public class I2PSocketManagerFull implements I2PSocketManager {
}
}
/**
* Create a copy of the current options, to be used in a setDefaultOptions() call.
*/
public I2PSocketOptions buildOptions() { return buildOptions(null); }
/**
* Create a modified copy of the current options, to be used in a setDefaultOptions() call.
* @param opts The new options, may be null
*/
public I2PSocketOptions buildOptions(Properties opts) {
ConnectionOptions curOpts = new ConnectionOptions(_defaultOptions);
curOpts.setProperties(opts);
@@ -159,10 +169,24 @@ public class I2PSocketManagerFull implements I2PSocketManager {
public void setAcceptTimeout(long ms) { _acceptTimeout = ms; }
public long getAcceptTimeout() { return _acceptTimeout; }
/**
* Update the options on a running socket manager.
* Parameters in the I2PSocketOptions interface may be changed directly
* with the setters; no need to use this method for those.
* This does NOT update the underlying I2CP or tunnel options; use getSession().updateOptions() for that.
* @param options as created from a call to buildOptions(properties), non-null
*/
public void setDefaultOptions(I2PSocketOptions options) {
_defaultOptions = new ConnectionOptions((ConnectionOptions) options);
if (!(options instanceof ConnectionOptions))
throw new IllegalArgumentException();
if (_log.shouldLog(Log.WARN))
_log.warn("Changing options from:\n " + _defaultOptions + "\nto:\n " + options);
_defaultOptions.updateAll((ConnectionOptions) options);
}
/**
* Current options, not a copy, setters may be used to make changes.
*/
public I2PSocketOptions getDefaultOptions() {
return _defaultOptions;
}