001package org.apache.commons.jcs.auxiliary.remote.server; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.IOException; 023import java.io.Serializable; 024import java.net.MalformedURLException; 025import java.rmi.Naming; 026import java.rmi.NotBoundException; 027import java.rmi.Remote; 028import java.rmi.RemoteException; 029import java.rmi.registry.Registry; 030import java.rmi.server.RMISocketFactory; 031import java.rmi.server.UnicastRemoteObject; 032import java.util.Properties; 033import java.util.concurrent.Executors; 034import java.util.concurrent.ScheduledExecutorService; 035import java.util.concurrent.TimeUnit; 036 037import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator; 038import org.apache.commons.jcs.auxiliary.remote.RemoteUtils; 039import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants; 040import org.apache.commons.jcs.engine.behavior.ICacheServiceAdmin; 041import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger; 042import org.apache.commons.jcs.utils.config.OptionConverter; 043import org.apache.commons.jcs.utils.config.PropertySetter; 044import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory; 045import org.apache.commons.logging.Log; 046import org.apache.commons.logging.LogFactory; 047 048/** 049 * Provides remote cache services. This creates remote cache servers and can proxy command line 050 * requests to a running server. 051 */ 052public class RemoteCacheServerFactory 053 implements IRemoteCacheConstants 054{ 055 /** The logger */ 056 private static final Log log = LogFactory.getLog( RemoteCacheServerFactory.class ); 057 058 /** The single instance of the RemoteCacheServer object. */ 059 private static RemoteCacheServer<?, ?> remoteCacheServer; 060 061 /** The name of the service. */ 062 private static String serviceName = IRemoteCacheConstants.REMOTE_CACHE_SERVICE_VAL; 063 064 /** Executes the registry keep alive. */ 065 private static ScheduledExecutorService keepAliveDaemon; 066 067 /** A reference to the registry. */ 068 private static Registry registry = null; 069 070 /** Constructor for the RemoteCacheServerFactory object. */ 071 private RemoteCacheServerFactory() 072 { 073 super(); 074 } 075 076 /** 077 * This will allow you to get stats from the server, etc. Perhaps we should provide methods on 078 * the factory to do this instead. 079 * <p> 080 * A remote cache is either a local cache or a cluster cache. 081 * </p> 082 * @return Returns the remoteCacheServer. 083 */ 084 @SuppressWarnings("unchecked") // Need cast to specific RemoteCacheServer 085 public static <K, V> RemoteCacheServer<K, V> getRemoteCacheServer() 086 { 087 return (RemoteCacheServer<K, V>)remoteCacheServer; 088 } 089 090 // ///////////////////// Startup/shutdown methods. ////////////////// 091 /** 092 * Starts up the remote cache server on this JVM, and binds it to the registry on the given host 093 * and port. 094 * <p> 095 * A remote cache is either a local cache or a cluster cache. 096 * <p> 097 * @param host the host name 098 * @param port the port number 099 * @param propFile the remote cache hub configuration file 100 * @throws IOException 101 * 102 * @deprecated Use startup(String, int, Properties) instead 103 */ 104 @Deprecated 105 public static void startup( String host, int port, String propFile ) 106 throws IOException 107 { 108 if ( log.isInfoEnabled() ) 109 { 110 log.info( "ConfigFileName = [" + propFile + "]" ); 111 } 112 Properties props = RemoteUtils.loadProps( propFile ); 113 startup(host, port, props); 114 } 115 116 /** 117 * Starts up the remote cache server on this JVM, and binds it to the registry on the given host 118 * and port. 119 * <p> 120 * A remote cache is either a local cache or a cluster cache. 121 * <p> 122 * @param host the host name 123 * @param port the port number 124 * @param props the remote cache hub configuration 125 * @param propFile the remote cache hub configuration file 126 * @throws IOException 127 * 128 * @deprecated Use startup(String, int, Properties) instead 129 */ 130 @Deprecated 131 public static void startup( String host, int port, Properties props, String propFile ) 132 throws IOException 133 { 134 if ( log.isWarnEnabled() ) 135 { 136 log.warn( "ConfigFileName = [" + propFile + "] ignored" ); 137 } 138 startup(host, port, props); 139 } 140 141 /** 142 * Starts up the remote cache server on this JVM, and binds it to the registry on the given host 143 * and port. 144 * <p> 145 * A remote cache is either a local cache or a cluster cache. 146 * <p> 147 * @param host 148 * @param port 149 * @param props 150 * @throws IOException 151 */ 152 public static void startup( String host, int port, Properties props) 153 throws IOException 154 { 155 if ( remoteCacheServer != null ) 156 { 157 throw new IllegalArgumentException( "Server already started." ); 158 } 159 160 synchronized ( RemoteCacheServer.class ) 161 { 162 if ( remoteCacheServer != null ) 163 { 164 return; 165 } 166 if ( host == null ) 167 { 168 host = ""; 169 } 170 171 RemoteCacheServerAttributes rcsa = configureRemoteCacheServerAttributes(props); 172 173 // These should come from the file! 174 rcsa.setRemoteLocation( host, port ); 175 if ( log.isInfoEnabled() ) 176 { 177 log.info( "Creating server with these attributes: " + rcsa ); 178 } 179 180 setServiceName( rcsa.getRemoteServiceName() ); 181 182 RMISocketFactory customRMISocketFactory = configureObjectSpecificCustomFactory( props ); 183 184 RemoteUtils.configureGlobalCustomSocketFactory( rcsa.getRmiSocketFactoryTimeoutMillis() ); 185 186 // CONFIGURE THE EVENT LOGGER 187 ICacheEventLogger cacheEventLogger = configureCacheEventLogger( props ); 188 189 // CREATE SERVER 190 if ( customRMISocketFactory != null ) 191 { 192 remoteCacheServer = new RemoteCacheServer<Serializable, Serializable>( rcsa, props, customRMISocketFactory ); 193 } 194 else 195 { 196 remoteCacheServer = new RemoteCacheServer<Serializable, Serializable>( rcsa, props ); 197 } 198 199 remoteCacheServer.setCacheEventLogger( cacheEventLogger ); 200 201 // START THE REGISTRY 202 registry = RemoteUtils.createRegistry(port); 203 204 // REGISTER THE SERVER 205 registerServer( serviceName, remoteCacheServer ); 206 207 // KEEP THE REGISTRY ALIVE 208 if ( rcsa.isUseRegistryKeepAlive() ) 209 { 210 if ( keepAliveDaemon == null ) 211 { 212 keepAliveDaemon = Executors.newScheduledThreadPool(1, 213 new DaemonThreadFactory("JCS-RemoteCacheServerFactory-")); 214 } 215 RegistryKeepAliveRunner runner = new RegistryKeepAliveRunner( host, port, serviceName ); 216 runner.setCacheEventLogger( cacheEventLogger ); 217 keepAliveDaemon.scheduleAtFixedRate(runner, 0, rcsa.getRegistryKeepAliveDelayMillis(), TimeUnit.MILLISECONDS); 218 } 219 } 220 } 221 222 /** 223 * Tries to get the event logger by new and old config styles. 224 * <p> 225 * @param props 226 * @return ICacheEventLogger 227 */ 228 protected static ICacheEventLogger configureCacheEventLogger( Properties props ) 229 { 230 ICacheEventLogger cacheEventLogger = AuxiliaryCacheConfigurator 231 .parseCacheEventLogger( props, IRemoteCacheConstants.CACHE_SERVER_PREFIX ); 232 233 // try the old way 234 if ( cacheEventLogger == null ) 235 { 236 cacheEventLogger = AuxiliaryCacheConfigurator.parseCacheEventLogger( props, 237 IRemoteCacheConstants.PROPERTY_PREFIX ); 238 } 239 return cacheEventLogger; 240 } 241 242 /** 243 * This configures an object specific custom factory. This will be configured for just this 244 * object in the registry. This can be null. 245 * <p> 246 * @param props 247 * @return RMISocketFactory 248 */ 249 protected static RMISocketFactory configureObjectSpecificCustomFactory( Properties props ) 250 { 251 RMISocketFactory customRMISocketFactory = 252 OptionConverter.instantiateByKey( props, CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX, null ); 253 254 if ( customRMISocketFactory != null ) 255 { 256 PropertySetter.setProperties( customRMISocketFactory, props, CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX 257 + "." ); 258 if ( log.isInfoEnabled() ) 259 { 260 log.info( "Will use server specific custom socket factory. " + customRMISocketFactory ); 261 } 262 } 263 else 264 { 265 if ( log.isInfoEnabled() ) 266 { 267 log.info( "No server specific custom socket factory defined." ); 268 } 269 } 270 return customRMISocketFactory; 271 } 272 273 /** 274 * Registers the server with the registry. I broke this off because we might want to have code 275 * that will restart a dead registry. It will need to rebind the server. 276 * <p> 277 * @param serviceName the name of the service 278 * @param server the server object to bind 279 * @throws RemoteException 280 */ 281 protected static void registerServer(String serviceName, Remote server ) 282 throws RemoteException 283 { 284 if ( server == null ) 285 { 286 throw new RemoteException( "Cannot register the server until it is created." ); 287 } 288 289 if ( registry == null ) 290 { 291 throw new RemoteException( "Cannot register the server: Registry is null." ); 292 } 293 294 if ( log.isInfoEnabled() ) 295 { 296 log.info( "Binding server to " + serviceName ); 297 } 298 299 registry.rebind( serviceName, server ); 300 } 301 302 /** 303 * Configure. 304 * <p> 305 * jcs.remotecache.serverattributes.ATTRIBUTENAME=ATTRIBUTEVALUE 306 * <p> 307 * @param prop 308 * @return RemoteCacheServerAttributesconfigureRemoteCacheServerAttributes 309 */ 310 protected static RemoteCacheServerAttributes configureRemoteCacheServerAttributes( Properties prop ) 311 { 312 RemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes(); 313 314 // configure automatically 315 PropertySetter.setProperties( rcsa, prop, CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + "." ); 316 317 configureManuallyIfValuesArePresent( prop, rcsa ); 318 319 return rcsa; 320 } 321 322 /** 323 * This looks for the old config values. 324 * <p> 325 * @param prop 326 * @param rcsa 327 */ 328 private static void configureManuallyIfValuesArePresent( Properties prop, RemoteCacheServerAttributes rcsa ) 329 { 330 // DEPRECATED CONFIG 331 String servicePortStr = prop.getProperty( REMOTE_CACHE_SERVICE_PORT ); 332 if ( servicePortStr != null ) 333 { 334 try 335 { 336 int servicePort = Integer.parseInt( servicePortStr ); 337 rcsa.setServicePort( servicePort ); 338 log.debug( "Remote cache service uses port number " + servicePort + "." ); 339 } 340 catch ( NumberFormatException ignore ) 341 { 342 log.debug( "Remote cache service port property " + REMOTE_CACHE_SERVICE_PORT 343 + " not specified. An anonymous port will be used." ); 344 } 345 } 346 347 String socketTimeoutMillisStr = prop.getProperty( SOCKET_TIMEOUT_MILLIS ); 348 if ( socketTimeoutMillisStr != null ) 349 { 350 try 351 { 352 int rmiSocketFactoryTimeoutMillis = Integer.parseInt( socketTimeoutMillisStr ); 353 rcsa.setRmiSocketFactoryTimeoutMillis( rmiSocketFactoryTimeoutMillis ); 354 log.debug( "Remote cache socket timeout " + rmiSocketFactoryTimeoutMillis + "ms." ); 355 } 356 catch ( NumberFormatException ignore ) 357 { 358 log.debug( "Remote cache socket timeout property " + SOCKET_TIMEOUT_MILLIS 359 + " not specified. The default will be used." ); 360 } 361 } 362 363 String lccStr = prop.getProperty( REMOTE_LOCAL_CLUSTER_CONSISTENCY ); 364 if ( lccStr != null ) 365 { 366 boolean lcc = Boolean.parseBoolean( lccStr ); 367 rcsa.setLocalClusterConsistency( lcc ); 368 } 369 370 String acgStr = prop.getProperty( REMOTE_ALLOW_CLUSTER_GET ); 371 if ( acgStr != null ) 372 { 373 boolean acg = Boolean.parseBoolean( lccStr ); 374 rcsa.setAllowClusterGet( acg ); 375 } 376 377 // Register the RemoteCacheServer remote object in the registry. 378 rcsa.setRemoteServiceName( prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim() ); 379 } 380 381 /** 382 * Unbinds the remote server. 383 * <p> 384 * @param host 385 * @param port 386 * @throws IOException 387 */ 388 static void shutdownImpl( String host, int port ) 389 throws IOException 390 { 391 synchronized ( RemoteCacheServer.class ) 392 { 393 if ( remoteCacheServer == null ) 394 { 395 return; 396 } 397 log.info( "Unbinding host=" + host + ", port=" + port + ", serviceName=" + getServiceName() ); 398 try 399 { 400 Naming.unbind( RemoteUtils.getNamingURL(host, port, getServiceName()) ); 401 } 402 catch ( MalformedURLException ex ) 403 { 404 // impossible case. 405 throw new IllegalArgumentException( ex.getMessage() + "; host=" + host + ", port=" + port 406 + ", serviceName=" + getServiceName() ); 407 } 408 catch ( NotBoundException ex ) 409 { 410 // ignore. 411 } 412 remoteCacheServer.release(); 413 remoteCacheServer = null; 414 415 // Shut down keepalive scheduler 416 if ( keepAliveDaemon != null ) 417 { 418 keepAliveDaemon.shutdownNow(); 419 keepAliveDaemon = null; 420 } 421 422 // Try to release registry 423 if (registry != null) 424 { 425 UnicastRemoteObject.unexportObject(registry, true); 426 registry = null; 427 } 428 } 429 } 430 431 /** 432 * Creates an local RMI registry on the default port, starts up the remote cache server, and 433 * binds it to the registry. 434 * <p> 435 * A remote cache is either a local cache or a cluster cache. 436 * <p> 437 * @param args The command line arguments 438 * @throws Exception 439 */ 440 public static void main( String[] args ) 441 throws Exception 442 { 443 Properties prop = args.length > 0 ? RemoteUtils.loadProps( args[args.length - 1] ) : new Properties(); 444 445 int port; 446 try 447 { 448 port = Integer.parseInt( prop.getProperty( "registry.port" ) ); 449 } 450 catch ( NumberFormatException ex ) 451 { 452 port = Registry.REGISTRY_PORT; 453 } 454 455 // shutdown 456 if ( args.length > 0 && args[0].toLowerCase().indexOf( "-shutdown" ) != -1 ) 457 { 458 try 459 { 460 ICacheServiceAdmin admin = lookupCacheServiceAdmin(prop, port); 461 admin.shutdown(); 462 } 463 catch ( Exception ex ) 464 { 465 log.error( "Problem calling shutdown.", ex ); 466 } 467 log.debug( "done." ); 468 System.exit( 0 ); 469 } 470 471 // STATS 472 if ( args.length > 0 && args[0].toLowerCase().indexOf( "-stats" ) != -1 ) 473 { 474 log.debug( "getting cache stats" ); 475 476 try 477 { 478 ICacheServiceAdmin admin = lookupCacheServiceAdmin(prop, port); 479 480 try 481 { 482// System.out.println( admin.getStats().toString() ); 483 log.debug( admin.getStats() ); 484 } 485 catch ( IOException es ) 486 { 487 log.error( es ); 488 } 489 } 490 catch ( Exception ex ) 491 { 492 log.error( "Problem getting stats.", ex ); 493 } 494 log.debug( "done." ); 495 System.exit( 0 ); 496 } 497 498 // startup. 499 String host = prop.getProperty( "registry.host" ); 500 501 if ( host == null || host.trim().equals( "" ) || host.trim().equals( "localhost" ) ) 502 { 503 log.debug( "main> creating registry on the localhost" ); 504 RemoteUtils.createRegistry( port ); 505 } 506 log.debug( "main> starting up RemoteCacheServer" ); 507 startup( host, port, prop); 508 log.debug( "main> done" ); 509 } 510 511 /** 512 * Look up the remote cache service admin instance 513 * 514 * @param config the configuration properties 515 * @param port the local port 516 * @return the admin object instance 517 * 518 * @throws Exception if lookup fails 519 */ 520 private static ICacheServiceAdmin lookupCacheServiceAdmin(Properties config, int port) throws Exception 521 { 522 String remoteServiceName = config.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim(); 523 String registry = RemoteUtils.getNamingURL("", port, remoteServiceName); 524 525 if ( log.isDebugEnabled() ) 526 { 527 log.debug( "looking up server " + registry ); 528 } 529 Object obj = Naming.lookup( registry ); 530 if ( log.isDebugEnabled() ) 531 { 532 log.debug( "server found" ); 533 } 534 535 return (ICacheServiceAdmin) obj; 536 } 537 538 /** 539 * @param serviceName the serviceName to set 540 */ 541 protected static void setServiceName( String serviceName ) 542 { 543 RemoteCacheServerFactory.serviceName = serviceName; 544 } 545 546 /** 547 * @return the serviceName 548 */ 549 protected static String getServiceName() 550 { 551 return serviceName; 552 } 553}