| package org.apache.commons.jcs.auxiliary.remote.server; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator; |
| import org.apache.commons.jcs.auxiliary.remote.RemoteUtils; |
| import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants; |
| import org.apache.commons.jcs.engine.behavior.ICacheServiceAdmin; |
| import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger; |
| import org.apache.commons.jcs.utils.config.OptionConverter; |
| import org.apache.commons.jcs.utils.config.PropertySetter; |
| import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.net.MalformedURLException; |
| import java.rmi.Naming; |
| import java.rmi.NotBoundException; |
| import java.rmi.Remote; |
| import java.rmi.RemoteException; |
| import java.rmi.registry.Registry; |
| import java.rmi.server.RMISocketFactory; |
| import java.rmi.server.UnicastRemoteObject; |
| import java.util.Properties; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Provides remote cache services. This creates remote cache servers and can proxy command line |
| * requests to a running server. |
| */ |
| public class RemoteCacheServerFactory |
| implements IRemoteCacheConstants |
| { |
| /** The logger */ |
| private static final Log log = LogFactory.getLog( RemoteCacheServerFactory.class ); |
| |
| /** The single instance of the RemoteCacheServer object. */ |
| private static RemoteCacheServer<? extends Serializable, ? extends Serializable> remoteCacheServer; |
| |
| /** The name of the service. */ |
| private static String serviceName = IRemoteCacheConstants.REMOTE_CACHE_SERVICE_VAL; |
| |
| /** Executes the registry keep alive. */ |
| private static ScheduledExecutorService keepAliveDaemon; |
| |
| /** A reference to the registry. */ |
| private static Registry registry = null; |
| |
| /** Constructor for the RemoteCacheServerFactory object. */ |
| private RemoteCacheServerFactory() |
| { |
| super(); |
| } |
| |
| /** |
| * This will allow you to get stats from the server, etc. Perhaps we should provide methods on |
| * the factory to do this instead. |
| * <p> |
| * A remote cache is either a local cache or a cluster cache. |
| * </p> |
| * @return Returns the remoteCacheServer. |
| */ |
| @SuppressWarnings("unchecked") // Need cast to specific RemoteCacheServer |
| public static <K, V> RemoteCacheServer<K, V> getRemoteCacheServer() |
| { |
| return (RemoteCacheServer<K, V>)remoteCacheServer; |
| } |
| |
| // ///////////////////// Startup/shutdown methods. ////////////////// |
| /** |
| * Starts up the remote cache server on this JVM, and binds it to the registry on the given host |
| * and port. |
| * <p> |
| * A remote cache is either a local cache or a cluster cache. |
| * <p> |
| * @param host |
| * @param port |
| * @param propFile |
| * @throws IOException |
| */ |
| public static void startup( String host, int port, String propFile ) |
| throws IOException |
| { |
| if ( remoteCacheServer != null ) |
| { |
| throw new IllegalArgumentException( "Server already started." ); |
| } |
| |
| synchronized ( RemoteCacheServer.class ) |
| { |
| if ( remoteCacheServer != null ) |
| { |
| return; |
| } |
| if ( log.isInfoEnabled() ) |
| { |
| log.info( "ConfigFileName = [" + propFile + "]" ); |
| } |
| Properties props = RemoteUtils.loadProps( propFile ); |
| startup(host, port, props); |
| } |
| } |
| |
| /** |
| * Starts up the remote cache server on this JVM, and binds it to the registry on the given host |
| * and port. |
| * <p> |
| * A remote cache is either a local cache or a cluster cache. |
| * <p> |
| * @param host |
| * @param port |
| * @param props |
| * @throws IOException |
| */ |
| public static void startup( String host, int port, Properties props ) |
| throws IOException |
| { |
| if ( remoteCacheServer != null ) |
| { |
| throw new IllegalArgumentException( "Server already started." ); |
| } |
| |
| synchronized ( RemoteCacheServer.class ) |
| { |
| if ( remoteCacheServer != null ) |
| { |
| return; |
| } |
| if ( host == null ) |
| { |
| host = ""; |
| } |
| |
| RemoteCacheServerAttributes rcsa = configureRemoteCacheServerAttributes(props); |
| //rcsa.setConfigFileName( propFile ); |
| |
| // These should come from the file! |
| rcsa.setRemotePort( port ); |
| rcsa.setRemoteHost( host ); |
| if ( log.isInfoEnabled() ) |
| { |
| log.info( "Creating server with these attributes: " + rcsa ); |
| } |
| |
| setServiceName( rcsa.getRemoteServiceName() ); |
| |
| RMISocketFactory customRMISocketFactory = configureObjectSpecificCustomFactory( props ); |
| |
| RemoteUtils.configureGlobalCustomSocketFactory( rcsa.getRmiSocketFactoryTimeoutMillis() ); |
| |
| // CONFIGURE THE EVENT LOGGER |
| ICacheEventLogger cacheEventLogger = configureCacheEventLogger( props ); |
| |
| // CREATE SERVER |
| if ( customRMISocketFactory != null ) |
| { |
| remoteCacheServer = new RemoteCacheServer<Serializable, Serializable>( rcsa, customRMISocketFactory ); |
| } |
| else |
| { |
| remoteCacheServer = new RemoteCacheServer<Serializable, Serializable>( rcsa ); |
| } |
| |
| remoteCacheServer.setCacheEventLogger( cacheEventLogger ); |
| |
| // START THE REGISTRY |
| if (rcsa.isStartRegistry()) |
| { |
| registry = RemoteUtils.createRegistry(port); |
| } |
| |
| // REGISTER THE SERVER |
| registerServer( RemoteUtils.getNamingURL(host, port, serviceName), remoteCacheServer ); |
| |
| // KEEP THE REGISTRY ALIVE |
| if ( rcsa.isUseRegistryKeepAlive() ) |
| { |
| if ( keepAliveDaemon == null ) |
| { |
| keepAliveDaemon = Executors.newScheduledThreadPool(1, |
| new DaemonThreadFactory("JCS-RemoteCacheServerFactory-")); |
| } |
| RegistryKeepAliveRunner runner = new RegistryKeepAliveRunner( host, port, serviceName ); |
| runner.setCacheEventLogger( cacheEventLogger ); |
| keepAliveDaemon.scheduleAtFixedRate(runner, 0, rcsa.getRegistryKeepAliveDelayMillis(), TimeUnit.MILLISECONDS); |
| } |
| } |
| } |
| |
| /** |
| * Tries to get the event logger by new and old config styles. |
| * <p> |
| * @param props |
| * @return ICacheEventLogger |
| */ |
| protected static ICacheEventLogger configureCacheEventLogger( Properties props ) |
| { |
| ICacheEventLogger cacheEventLogger = AuxiliaryCacheConfigurator |
| .parseCacheEventLogger( props, IRemoteCacheConstants.CACHE_SERVER_PREFIX ); |
| |
| // try the old way |
| if ( cacheEventLogger == null ) |
| { |
| cacheEventLogger = AuxiliaryCacheConfigurator.parseCacheEventLogger( props, |
| IRemoteCacheConstants.PROPERTY_PREFIX ); |
| } |
| return cacheEventLogger; |
| } |
| |
| /** |
| * This configures an object specific custom factory. This will be configured for just this |
| * object in the registry. This can be null. |
| * <p> |
| * @param props |
| * @return RMISocketFactory |
| */ |
| protected static RMISocketFactory configureObjectSpecificCustomFactory( Properties props ) |
| { |
| RMISocketFactory customRMISocketFactory = |
| OptionConverter.instantiateByKey( props, CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX, null ); |
| |
| if ( customRMISocketFactory != null ) |
| { |
| PropertySetter.setProperties( customRMISocketFactory, props, CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX |
| + "." ); |
| if ( log.isInfoEnabled() ) |
| { |
| log.info( "Will use server specific custom socket factory. " + customRMISocketFactory ); |
| } |
| } |
| else |
| { |
| if ( log.isInfoEnabled() ) |
| { |
| log.info( "No server specific custom socket factory defined." ); |
| } |
| } |
| return customRMISocketFactory; |
| } |
| |
| /** |
| * Registers the server with the registry. I broke this off because we might want to have code |
| * that will restart a dead registry. It will need to rebind the server. |
| * <p> |
| * @param namingURL |
| * @param server |
| * @throws RemoteException |
| */ |
| protected static void registerServer( String namingURL, Remote server ) |
| throws RemoteException |
| { |
| if ( server == null ) |
| { |
| throw new RemoteException( "Cannot register the server until it is created." ); |
| } |
| |
| if ( log.isInfoEnabled() ) |
| { |
| log.info( "Binding server to " + namingURL ); |
| } |
| |
| try |
| { |
| Naming.rebind( namingURL, server ); |
| } |
| catch ( MalformedURLException ex ) |
| { |
| // impossible case. |
| throw new IllegalArgumentException( ex.getMessage() + "; url=" + namingURL ); |
| } |
| } |
| |
| /** |
| * Configure. |
| * <p> |
| * jcs.remotecache.serverattributes.ATTRIBUTENAME=ATTRIBUTEVALUE |
| * <p> |
| * @param prop |
| * @return RemoteCacheServerAttributesconfigureRemoteCacheServerAttributes |
| */ |
| protected static RemoteCacheServerAttributes configureRemoteCacheServerAttributes( Properties prop ) |
| { |
| RemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes(); |
| |
| // configure automatically |
| PropertySetter.setProperties( rcsa, prop, CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + "." ); |
| |
| configureManuallyIfValuesArePresent( prop, rcsa ); |
| |
| return rcsa; |
| } |
| |
| /** |
| * This looks for the old config values. |
| * <p> |
| * @param prop |
| * @param rcsa |
| */ |
| private static void configureManuallyIfValuesArePresent( Properties prop, RemoteCacheServerAttributes rcsa ) |
| { |
| // DEPRECATED CONFIG |
| String servicePortStr = prop.getProperty( REMOTE_CACHE_SERVICE_PORT ); |
| if ( servicePortStr != null ) |
| { |
| try |
| { |
| int servicePort = Integer.parseInt( servicePortStr ); |
| rcsa.setServicePort( servicePort ); |
| log.debug( "Remote cache service uses port number " + servicePort + "." ); |
| } |
| catch ( NumberFormatException ignore ) |
| { |
| log.debug( "Remote cache service port property " + REMOTE_CACHE_SERVICE_PORT |
| + " not specified. An anonymous port will be used." ); |
| } |
| } |
| |
| String socketTimeoutMillisStr = prop.getProperty( SOCKET_TIMEOUT_MILLIS ); |
| if ( socketTimeoutMillisStr != null ) |
| { |
| try |
| { |
| int rmiSocketFactoryTimeoutMillis = Integer.parseInt( socketTimeoutMillisStr ); |
| rcsa.setRmiSocketFactoryTimeoutMillis( rmiSocketFactoryTimeoutMillis ); |
| log.debug( "Remote cache socket timeout " + rmiSocketFactoryTimeoutMillis + "ms." ); |
| } |
| catch ( NumberFormatException ignore ) |
| { |
| log.debug( "Remote cache socket timeout property " + SOCKET_TIMEOUT_MILLIS |
| + " not specified. The default will be used." ); |
| } |
| } |
| |
| String lccStr = prop.getProperty( REMOTE_LOCAL_CLUSTER_CONSISTENCY ); |
| if ( lccStr != null ) |
| { |
| boolean lcc = Boolean.valueOf( lccStr ).booleanValue(); |
| rcsa.setLocalClusterConsistency( lcc ); |
| } |
| |
| String acgStr = prop.getProperty( REMOTE_ALLOW_CLUSTER_GET ); |
| if ( acgStr != null ) |
| { |
| boolean acg = Boolean.valueOf( acgStr ).booleanValue(); |
| rcsa.setAllowClusterGet( acg ); |
| } |
| |
| // Register the RemoteCacheServer remote object in the registry. |
| rcsa.setRemoteServiceName( prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim() ); |
| } |
| |
| /** |
| * Unbinds the remote server. |
| * <p> |
| * @param host |
| * @param port |
| * @throws IOException |
| */ |
| static void shutdownImpl( String host, int port ) |
| throws IOException |
| { |
| synchronized ( RemoteCacheServer.class ) |
| { |
| if ( remoteCacheServer == null ) |
| { |
| return; |
| } |
| log.info( "Unbinding host=" + host + ", port=" + port + ", serviceName=" + getServiceName() ); |
| try |
| { |
| Naming.unbind( RemoteUtils.getNamingURL(host, port, getServiceName()) ); |
| } |
| catch ( MalformedURLException ex ) |
| { |
| // impossible case. |
| throw new IllegalArgumentException( ex.getMessage() + "; host=" + host + ", port=" + port |
| + ", serviceName=" + getServiceName() ); |
| } |
| catch ( NotBoundException ex ) |
| { |
| // ignore. |
| } |
| remoteCacheServer.release(); |
| remoteCacheServer = null; |
| |
| // Shut down keepalive scheduler |
| if ( keepAliveDaemon != null ) |
| { |
| keepAliveDaemon.shutdownNow(); |
| keepAliveDaemon = null; |
| } |
| |
| // Try to release registry |
| if (registry != null) |
| { |
| UnicastRemoteObject.unexportObject(registry, true); |
| registry = null; |
| } |
| } |
| } |
| |
| /** |
| * Creates an local RMI registry on the default port, starts up the remote cache server, and |
| * binds it to the registry. |
| * <p> |
| * A remote cache is either a local cache or a cluster cache. |
| * <p> |
| * @param args The command line arguments |
| * @throws Exception |
| */ |
| public static void main( String[] args ) |
| throws Exception |
| { |
| Properties prop = args.length > 0 ? RemoteUtils.loadProps( args[args.length - 1] ) : new Properties(); |
| |
| int port; |
| try |
| { |
| port = Integer.parseInt( prop.getProperty( "registry.port" ) ); |
| } |
| catch ( NumberFormatException ex ) |
| { |
| port = Registry.REGISTRY_PORT; |
| } |
| |
| // shutdown |
| if ( args.length > 0 && args[0].toLowerCase().indexOf( "-shutdown" ) != -1 ) |
| { |
| String remoteServiceName = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim(); |
| String registry = RemoteUtils.getNamingURL("", port, remoteServiceName); |
| |
| if ( log.isDebugEnabled() ) |
| { |
| log.debug( "looking up server " + registry ); |
| } |
| Object obj = Naming.lookup( registry ); |
| if ( log.isDebugEnabled() ) |
| { |
| log.debug( "server found" ); |
| } |
| ICacheServiceAdmin admin = (ICacheServiceAdmin) obj; |
| try |
| { |
| admin.shutdown(); |
| } |
| catch ( Exception ex ) |
| { |
| log.error( "Problem calling shutdown.", ex ); |
| } |
| log.debug( "done." ); |
| System.exit( 0 ); |
| } |
| |
| // STATS |
| if ( args.length > 0 && args[0].toLowerCase().indexOf( "-stats" ) != -1 ) |
| { |
| |
| log.debug( "getting cache stats" ); |
| |
| try |
| { |
| String sz = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim(); |
| String registry = RemoteUtils.getNamingURL("", port, sz); |
| log.debug( "looking up server " + registry ); |
| Object obj = Naming.lookup( registry ); |
| log.debug( "server found" ); |
| |
| log.debug( "obj = " + obj ); |
| ICacheServiceAdmin admin = (ICacheServiceAdmin) obj; |
| |
| try |
| { |
| // System.out.println( admin.getStats().toString() ); |
| log.debug( admin.getStats() ); |
| } |
| catch ( Exception es ) |
| { |
| log.error( es ); |
| } |
| |
| } |
| catch ( Exception ex ) |
| { |
| log.error( "Problem getting stats.", ex ); |
| } |
| log.debug( "done." ); |
| System.exit( 0 ); |
| } |
| |
| // startup. |
| String host = prop.getProperty( "registry.host" ); |
| |
| if ( host == null || host.trim().equals( "" ) || host.trim().equals( "localhost" ) ) |
| { |
| log.debug( "main> creating registry on the localhost" ); |
| RemoteUtils.createRegistry( port ); |
| } |
| log.debug( "main> starting up RemoteCacheServer" ); |
| RemoteCacheServerFactory.startup( host, port, args.length > 0 ? args[0] : null ); |
| log.debug( "main> done" ); |
| } |
| |
| /** |
| * @param serviceName the serviceName to set |
| */ |
| protected static void setServiceName( String serviceName ) |
| { |
| RemoteCacheServerFactory.serviceName = serviceName; |
| } |
| |
| /** |
| * @return the serviceName |
| */ |
| protected static String getServiceName() |
| { |
| return serviceName; |
| } |
| } |