package org.apache.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 java.io.IOException;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.registry.Registry;
import java.rmi.server.RMISocketFactory;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jcs.auxiliary.remote.RemoteUtils;
import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheServiceAdmin;
/**
* Provides remote cache services. This creates remote cache servers and can proxy command line
* requests to a running server.
*/
public class RemoteCacheServerFactory
implements IRemoteCacheConstants
{
private final static Log log = LogFactory.getLog( RemoteCacheServerFactory.class );
/** The single instance of the RemoteCacheServer object. */
private static RemoteCacheServer remoteCacheServer;
private static String serviceName;
private static int DEFAULT_RMI_SOCKET_FACTORY_TIMEOUT_MS = 10000;
/** 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.
* A remote cache is either a local cache or a cluster cache
* @return Returns the remoteCacheServer.
*/
public static RemoteCacheServer getRemoteCacheServer()
{
return remoteCacheServer;
}
// ///////////////////// Statup/shutdown methods. //////////////////
/**
* Starts up the remote cache server on this JVM, and binds it to the registry on the given host
* and port.
* A remote cache is either a local cache or a cluster cache
* @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 + "]" );
}
try
{
// TODO make configurable.
// use this socket factory to add a timeout.
RMISocketFactory.setSocketFactory( new RMISocketFactory()
{
public Socket createSocket( String host, int port )
throws IOException
{
Socket socket = new Socket( host, port );
socket.setSoTimeout( DEFAULT_RMI_SOCKET_FACTORY_TIMEOUT_MS );
socket.setSoLinger( false, 0 );
return socket;
}
public ServerSocket createServerSocket( int port )
throws IOException
{
return new ServerSocket( port );
}
} );
}
catch ( Exception e )
{
log.error( "Problem setting custom RMI Socket Factory.", e );
}
// TODO: make automatic
RemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
rcsa.setConfigFileName( propFile );
Properties prop = RemoteUtils.loadProps( propFile );
// Properties prop = PropertyLoader.loadProperties( propFile );
String servicePortStr = prop.getProperty( REMOTE_CACHE_SERVICE_PORT );
int servicePort = -1;
try
{
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 lccStr = prop.getProperty( REMOTE_LOCAL_CLUSTER_CONSISTENCY );
if ( lccStr == null )
{
lccStr = "true";
}
boolean lcc = Boolean.valueOf( lccStr ).booleanValue();
rcsa.setLocalClusterConsistency( lcc );
String acgStr = prop.getProperty( REMOTE_ALLOW_CLUSTER_GET );
if ( acgStr == null )
{
acgStr = "true";
}
boolean acg = Boolean.valueOf( acgStr ).booleanValue();
rcsa.setAllowClusterGet( acg );
if ( log.isInfoEnabled() )
{
log.info( "Creating server with these attributes " + rcsa );
}
// CREATE SERVER
remoteCacheServer = new RemoteCacheServer( rcsa );
if ( host == null )
{
host = "";
}
// Register the RemoteCacheServer remote object in the registry.
serviceName = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim();
if ( log.isInfoEnabled() )
{
log.info( "Binding server to " + host + ":" + port + " with the name " + serviceName );
}
try
{
Naming.rebind( "//" + host + ":" + port + "/" + serviceName, remoteCacheServer );
}
catch ( MalformedURLException ex )
{
// impossible case.
throw new IllegalArgumentException( ex.getMessage() + "; host=" + host + ", port=" + port );
}
}
}
/**
* Unbinds the remote server.
* <p>
* @param host
* @param port
* @exception IOException
*/
static void shutdownImpl( String host, int port )
throws IOException
{
if ( remoteCacheServer == null )
{
return;
}
synchronized ( RemoteCacheServer.class )
{
if ( remoteCacheServer == null )
{
return;
}
log.info( "Unbinding host=" + host + ", port=" + port + ", serviceName=" + serviceName );
try
{
Naming.unbind( "//" + host + ":" + port + "/" + serviceName );
}
catch ( MalformedURLException ex )
{
// impossible case.
throw new IllegalArgumentException( ex.getMessage() + "; host=" + host + ", port=" + port
+ ", serviceName=" + serviceName );
}
catch ( NotBoundException ex )
{
// ignore.
}
remoteCacheServer.release();
remoteCacheServer = null;
// TODO: safer exit ?
try
{
Thread.sleep( 2000 );
}
catch ( InterruptedException ex )
{
// swallow
}
System.exit( 0 );
}
}
/**
* Creates an local RMI registry on the default port, starts up the remote cache server, and
* binds it to the registry.
* A remote cache is either a local cache or a cluster cache
* @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 serviceName = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim();
String registry = "//:" + port + "/" + serviceName;
if ( log.isDebugEnabled() )
{
log.debug( "looking up server " + registry );
}
Object obj = Naming.lookup( registry );
if ( log.isDebugEnabled() )
{
log.debug( "server found" );
}
IRemoteCacheServiceAdmin admin = (IRemoteCacheServiceAdmin) 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 serviceName = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim();
String registry = "//:" + port + "/" + serviceName;
log.debug( "looking up server " + registry );
Object obj = Naming.lookup( registry );
log.debug( "server found" );
log.debug( "obj = " + obj );
IRemoteCacheServiceAdmin admin = (IRemoteCacheServiceAdmin) 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" );
port = RemoteUtils.createRegistry( port );
}
log.debug( "main> starting up RemoteCacheServer" );
RemoteCacheServerFactory.startup( host, port, args.length > 0 ? args[0] : null );
log.debug( "main> done" );
}
}