diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index f169d0573..ddf3fd155 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -193,7 +193,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { } } - /** @return non-null */ + /** @return A copy, non-null */ List 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"); } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index aca83e490..916550dcf 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -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 diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java index 60d6abf3f..059823980 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -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); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelTask.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelTask.java index 02d384850..4931168b3 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelTask.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelTask.java @@ -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 diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 2386a9dbe..5baef65c2 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -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 sessions = _tunnel.getSessions(); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java index 935abdd2f..172e3eb57 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -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(); } } diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java index 1651d40d2..eaa5b667f 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java @@ -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); diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java index 57f5f7bb4..113d1e7ff 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java @@ -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 con = (Constructor) + 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() { diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java index e6ec6fa42..7f8616b3e 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java @@ -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 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(); } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java index eddfec953..cfc0bf783 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java @@ -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; }