/*=============================================================================# # Copyright (c) 2009-2016 Stephan Wahlbrink (WalWare.de) and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of either (per the licensee's choosing) # - the Eclipse Public License v1.0 # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html, or # - the GNU Lesser General Public License v2.1 or newer # which accompanies this distribution, and is available at # http://www.gnu.org/licenses/lgpl.html # # Contributors: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.rj.server; import java.io.Externalizable; import java.io.File; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.rmi.Remote; import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMISocketFactory; import java.security.AccessControlException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import de.walware.rj.RjException; import de.walware.rj.data.RObjectFactory; public class RjsComConfig { public static final String RJ_COM_S2C_ID_PROPERTY_ID = "rj.com.s2c.id"; public static final String RJ_DATA_STRUCTS_LISTS_MAX_LENGTH_PROPERTY_ID = "rj.data.structs.lists.max_length"; public static final String RJ_DATA_STRUCTS_ENVS_MAX_LENGTH_PROPERTY_ID = "rj.data.structs.envs.max_length"; private static final Map<String, Object> PROPERTIES = new ConcurrentHashMap<>(); public static interface PathResolver { File resolve(Remote client, String path) throws RjException; } public static void setServerPathResolver(final RjsComConfig.PathResolver resolver) { BinExchange.gSPathResolver = resolver; } public static int registerClientComHandler(final ComHandler handler) { if (handler == null) { throw new NullPointerException(); } final int id = MainCmdS2CList.gComHandlers.put(handler); if (id < 0xffff) { return id; } MainCmdS2CList.gComHandlers.remove(id); throw new UnsupportedOperationException("Too much open clients"); } public static void unregisterClientComHandler(final int id) { MainCmdS2CList.gComHandlers.remove(id); } /** * Registers an additional RObject factory * * Factory registration is valid for the current VM. */ public static final void registerRObjectFactory(final String id, final RObjectFactory factory) { if (id == null || factory == null) { throw new NullPointerException(); } if (id.equals(DataCmdItem.DEFAULT_FACTORY_ID)) { throw new IllegalArgumentException(); } DataCmdItem.gFactories.put(id, factory); } /** * Sets the default RObject factory * * Factory registration is valid for the current VM. */ public static final void setDefaultRObjectFactory(final RObjectFactory factory) { if (factory == null) { throw new NullPointerException(); } DataCmdItem.gDefaultFactory = factory; DataCmdItem.gFactories.put(DataCmdItem.DEFAULT_FACTORY_ID, factory); } public static final void setProperty(final String key, final Object value) { PROPERTIES.put(key, value); } public static final Object getProperty(final String key) { return PROPERTIES.get(key); } private static final ThreadLocal<RMIClientSocketFactory> gRMIClientSocketFactoriesInit = new ThreadLocal<>(); private static final ConcurrentHashMap<String, RMIClientSocketFactory> gRMIClientSocketFactories = new ConcurrentHashMap<>(); private static RMIClientSocketFactory getSystemRMIClientSocketFactory() { RMIClientSocketFactory factory = RMISocketFactory.getSocketFactory(); if (factory == null) { factory = RMISocketFactory.getDefaultSocketFactory(); } return factory; } private static final class RjRMIClientSocketFactory implements RMIClientSocketFactory, Externalizable { private static final long serialVersionUID = -2470426070934072117L; private static String getLocalHostName() { try { return InetAddress.getLocalHost().getCanonicalHostName(); } catch (final UnknownHostException e) {} catch (final ArrayIndexOutOfBoundsException e) { /* JVM bug */ } return "unknown"; } private String id; private RMIClientSocketFactory resolvedFactory; public RjRMIClientSocketFactory() { } public RjRMIClientSocketFactory(final String init) { final StringBuilder sb = new StringBuilder(init); sb.append(getLocalHostName()); sb.append('/').append(System.nanoTime()).append('/').append(Math.random()); this.id = sb.toString(); } @Override public void writeExternal(final ObjectOutput out) throws IOException { out.writeUTF(this.id); } @Override public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { this.id = in.readUTF(); } @Override public Socket createSocket(final String host, final int port) throws IOException { RMIClientSocketFactory factory = null; factory = gRMIClientSocketFactoriesInit.get(); if (factory != null) { this.resolvedFactory = factory; gRMIClientSocketFactories.put(this.id, factory); } else { factory = this.resolvedFactory; if (factory == null) { factory = gRMIClientSocketFactories.get(this.id); if (factory != null) { this.resolvedFactory = factory; } else { factory = getSystemRMIClientSocketFactory(); } } } return factory.createSocket(host, port); } @Override public int hashCode() { return this.id.hashCode(); } @Override public boolean equals(final Object obj) { return (this == obj || (obj instanceof RjRMIClientSocketFactory && this.id.equals(((RjRMIClientSocketFactory) obj).id) )); } } private final static boolean RMISERVER_CLIENTSOCKET_FACTORY_ENABLED; private static RMIClientSocketFactory RMISERVER_CLIENTSOCKET_FACTORY; static { { boolean enabled = true; try { if ("true".equalsIgnoreCase(System.getProperty("de.walware.rj.rmi.disableSocketFactory"))) { enabled = false; } } catch (final AccessControlException e) { // in RMI registry } RMISERVER_CLIENTSOCKET_FACTORY_ENABLED = enabled; } } public static synchronized final RMIClientSocketFactory getRMIServerClientSocketFactory() { if (RMISERVER_CLIENTSOCKET_FACTORY_ENABLED && RMISERVER_CLIENTSOCKET_FACTORY == null) { RMISERVER_CLIENTSOCKET_FACTORY = new RjRMIClientSocketFactory("S/"); } return RMISERVER_CLIENTSOCKET_FACTORY; } public static final void setRMIClientSocketFactory(RMIClientSocketFactory factory) { if (factory == null) { factory = getSystemRMIClientSocketFactory(); } gRMIClientSocketFactoriesInit.set(factory); } public static final void clearRMIClientSocketFactory() { gRMIClientSocketFactoriesInit.set(null); } }