View Javadoc

1   package org.apache.jcs.auxiliary.remote.server;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.io.Serializable;
24  import java.net.MalformedURLException;
25  import java.rmi.Naming;
26  import java.rmi.NotBoundException;
27  import java.rmi.RemoteException;
28  import java.rmi.registry.LocateRegistry;
29  import java.rmi.registry.Registry;
30  import java.rmi.server.RMISocketFactory;
31  import java.util.Properties;
32  import java.util.concurrent.Executors;
33  import java.util.concurrent.ScheduledExecutorService;
34  import java.util.concurrent.ThreadFactory;
35  import java.util.concurrent.TimeUnit;
36  
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.apache.jcs.auxiliary.AuxiliaryCacheConfigurator;
40  import org.apache.jcs.auxiliary.remote.RemoteUtils;
41  import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
42  import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheServiceAdmin;
43  import org.apache.jcs.engine.logging.behavior.ICacheEventLogger;
44  import org.apache.jcs.utils.config.OptionConverter;
45  import org.apache.jcs.utils.config.PropertySetter;
46  
47  /**
48   * Provides remote cache services. This creates remote cache servers and can proxy command line
49   * requests to a running server.
50   */
51  public class RemoteCacheServerFactory
52      implements IRemoteCacheConstants
53  {
54      /** The logger */
55      private final static Log log = LogFactory.getLog( RemoteCacheServerFactory.class );
56  
57      /** The single instance of the RemoteCacheServer object. */
58      private static RemoteCacheServer<? extends Serializable, ? extends Serializable> remoteCacheServer;
59  
60      /** The name of the service. */
61      private static String serviceName = IRemoteCacheConstants.REMOTE_CACHE_SERVICE_VAL;
62  
63      /** Executes the registry keep alive. */
64      private static ScheduledExecutorService keepAliveDaemon;
65  
66      /** Constructor for the RemoteCacheServerFactory object. */
67      private RemoteCacheServerFactory()
68      {
69          super();
70      }
71  
72      /**
73       * This will allow you to get stats from the server, etc. Perhaps we should provide methods on
74       * the factory to do this instead.
75       * <p>
76       * A remote cache is either a local cache or a cluster cache.
77       * </p>
78       * @return Returns the remoteCacheServer.
79       */
80      @SuppressWarnings("unchecked") // Need cast to specific RemoteCacheServer
81      public static <K extends Serializable, V extends Serializable> RemoteCacheServer<K, V> getRemoteCacheServer()
82      {
83          return (RemoteCacheServer<K, V>)remoteCacheServer;
84      }
85  
86      // ///////////////////// Startup/shutdown methods. //////////////////
87      /**
88       * Starts up the remote cache server on this JVM, and binds it to the registry on the given host
89       * and port.
90       * <p>
91       * A remote cache is either a local cache or a cluster cache.
92       * <p>
93       * @param host
94       * @param port
95       * @param propFile
96       * @throws IOException
97       */
98      public static void startup( String host, int port, String propFile )
99          throws IOException
100     {
101         if ( remoteCacheServer != null )
102         {
103             throw new IllegalArgumentException( "Server already started." );
104         }
105 
106         synchronized ( RemoteCacheServer.class )
107         {
108             if ( remoteCacheServer != null )
109             {
110                 return;
111             }
112             if ( log.isInfoEnabled() )
113             {
114                 log.info( "ConfigFileName = [" + propFile + "]" );
115             }
116             Properties props = RemoteUtils.loadProps( propFile );
117             startup(host, port, props);
118         }
119     }
120 
121     /**
122      * Starts up the remote cache server on this JVM, and binds it to the registry on the given host
123      * and port.
124      * <p>
125      * A remote cache is either a local cache or a cluster cache.
126      * <p>
127      * @param host
128      * @param port
129      * @param props
130      * @throws IOException
131      */
132     public static void startup( String host, int port, Properties props )
133         throws IOException
134     {
135         if ( remoteCacheServer != null )
136         {
137             throw new IllegalArgumentException( "Server already started." );
138         }
139 
140         synchronized ( RemoteCacheServer.class )
141         {
142             if ( remoteCacheServer != null )
143             {
144                 return;
145             }
146             if ( host == null )
147             {
148                 host = "";
149             }
150 
151             RemoteCacheServerAttributes rcsa = configureRemoteCacheServerAttributes(props);
152             //rcsa.setConfigFileName( propFile );
153 
154             // These should come from the file!
155             rcsa.setRemotePort( port );
156             rcsa.setRemoteHost( host );
157             if ( log.isInfoEnabled() )
158             {
159                 log.info( "Creating server with these attributes: " + rcsa );
160             }
161 
162             setServiceName( rcsa.getRemoteServiceName() );
163 
164             RMISocketFactory customRMISocketFactory = configureObjectSpecificCustomFactory( props );
165 
166             RemoteUtils.configureGlobalCustomSocketFactory( rcsa.getRmiSocketFactoryTimeoutMillis() );
167 
168             // CONFIGURE THE EVENT LOGGER
169             ICacheEventLogger cacheEventLogger = configureCacheEventLogger( props );
170 
171             // CREATE SERVER
172             if ( customRMISocketFactory != null )
173             {
174                 remoteCacheServer = new RemoteCacheServer<Serializable, Serializable>( rcsa, customRMISocketFactory );
175             }
176             else
177             {
178                 remoteCacheServer = new RemoteCacheServer<Serializable, Serializable>( rcsa );
179             }
180             remoteCacheServer.setCacheEventLogger( cacheEventLogger );
181 
182             // START THE REGISTRY
183             startTheRegistry( port, rcsa );
184 
185             // REGISTER THE SERVER
186             registerServer( host, port, serviceName );
187 
188             // KEEP THE REGISTRY ALIVE
189             if ( rcsa.isUseRegistryKeepAlive() )
190             {
191                 if ( keepAliveDaemon == null )
192                 {
193                     keepAliveDaemon = Executors.newScheduledThreadPool(1, new MyThreadFactory());
194                 }
195                 RegistryKeepAliveRunner runner = new RegistryKeepAliveRunner( host, port, serviceName );
196                 runner.setCacheEventLogger( cacheEventLogger );
197                 keepAliveDaemon.scheduleAtFixedRate(runner, 0, rcsa.getRegistryKeepAliveDelayMillis(), TimeUnit.MILLISECONDS);
198             }
199         }
200     }
201 
202     /**
203      * Tries to get the event logger by new and old config styles.
204      * <p>
205      * @param props
206      * @return ICacheEventLogger
207      */
208     protected static ICacheEventLogger configureCacheEventLogger( Properties props )
209     {
210         ICacheEventLogger cacheEventLogger = AuxiliaryCacheConfigurator
211             .parseCacheEventLogger( props, IRemoteCacheConstants.CACHE_SERVER_PREFIX );
212 
213         // try the old way
214         if ( cacheEventLogger == null )
215         {
216             cacheEventLogger = AuxiliaryCacheConfigurator.parseCacheEventLogger( props,
217                                                                                  IRemoteCacheConstants.PROPERTY_PREFIX );
218         }
219         return cacheEventLogger;
220     }
221 
222     /**
223      * This configures an object specific custom factory. This will be configured for just this
224      * object in the registry. This can be null.
225      * <p>
226      * @param props
227      * @return RMISocketFactory
228      */
229     protected static RMISocketFactory configureObjectSpecificCustomFactory( Properties props )
230     {
231         RMISocketFactory customRMISocketFactory =
232             OptionConverter.instantiateByKey( props, CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX, null );
233 
234         if ( customRMISocketFactory != null )
235         {
236             PropertySetter.setProperties( customRMISocketFactory, props, CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX
237                 + "." );
238             if ( log.isInfoEnabled() )
239             {
240                 log.info( "Will use server specific custom socket factory. " + customRMISocketFactory );
241             }
242         }
243         else
244         {
245             if ( log.isInfoEnabled() )
246             {
247                 log.info( "No server specific custom socket factory defined." );
248             }
249         }
250         return customRMISocketFactory;
251     }
252 
253     /**
254      * Starts the registry if needed
255      * <p>
256      * @param registryPort
257      * @param rcsa
258      */
259     private static void startTheRegistry( int registryPort, RemoteCacheServerAttributes rcsa )
260     {
261         if ( rcsa.isStartRegistry() )
262         {
263             try
264             {
265                 LocateRegistry.createRegistry( registryPort );
266             }
267             catch ( RemoteException e )
268             {
269                 log.warn( "Problem creating registry.  It may already be started. " + e.getMessage() );
270             }
271             catch ( Throwable t )
272             {
273                 log.error( "Problem creating registry.", t );
274             }
275         }
276     }
277 
278     /**
279      * Registers the server with the registry. I broke this off because we might want to have code
280      * that will restart a dead registry. It will need to rebind the server.
281      * <p>
282      * @param host
283      * @param port
284      * @param serviceName
285      * @throws RemoteException
286      */
287     protected static void registerServer( String host, int port, String serviceName )
288         throws RemoteException
289     {
290         if ( remoteCacheServer == null )
291         {
292             String message = "Cannot register the server until it is created.  Please start the server first.";
293             log.error( message );
294             throw new RemoteException( message );
295         }
296 
297         if ( log.isInfoEnabled() )
298         {
299             log.info( "Binding server to " + host + ":" + port + " with the name " + serviceName );
300         }
301         try
302         {
303             Naming.rebind( "//" + host + ":" + port + "/" + serviceName, remoteCacheServer );
304         }
305         catch ( MalformedURLException ex )
306         {
307             // impossible case.
308             throw new IllegalArgumentException( ex.getMessage() + "; host=" + host + ", port=" + port );
309         }
310     }
311 
312     /**
313      * Configure.
314      * <p>
315      * jcs.remotecache.serverattributes.ATTRIBUTENAME=ATTRIBUTEVALUE
316      * <p>
317      * @param prop
318      * @return RemoteCacheServerAttributesconfigureRemoteCacheServerAttributes
319      */
320     protected static RemoteCacheServerAttributes configureRemoteCacheServerAttributes( Properties prop )
321     {
322         RemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
323 
324         // configure automatically
325         PropertySetter.setProperties( rcsa, prop, CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + "." );
326 
327         configureManuallyIfValuesArePresent( prop, rcsa );
328 
329         return rcsa;
330     }
331 
332     /**
333      * This looks for the old config values.
334      * <p>
335      * @param prop
336      * @param rcsa
337      */
338     private static void configureManuallyIfValuesArePresent( Properties prop, RemoteCacheServerAttributes rcsa )
339     {
340         // DEPRECATED CONFIG
341         String servicePortStr = prop.getProperty( REMOTE_CACHE_SERVICE_PORT );
342         if ( servicePortStr != null )
343         {
344             try
345             {
346                 int servicePort = Integer.parseInt( servicePortStr );
347                 rcsa.setServicePort( servicePort );
348                 log.debug( "Remote cache service uses port number " + servicePort + "." );
349             }
350             catch ( NumberFormatException ignore )
351             {
352                 log.debug( "Remote cache service port property " + REMOTE_CACHE_SERVICE_PORT
353                     + " not specified.  An anonymous port will be used." );
354             }
355         }
356 
357         String socketTimeoutMillisStr = prop.getProperty( SOCKET_TIMEOUT_MILLIS );
358         if ( socketTimeoutMillisStr != null )
359         {
360             try
361             {
362                 int rmiSocketFactoryTimeoutMillis = Integer.parseInt( socketTimeoutMillisStr );
363                 rcsa.setRmiSocketFactoryTimeoutMillis( rmiSocketFactoryTimeoutMillis );
364                 log.debug( "Remote cache socket timeout " + rmiSocketFactoryTimeoutMillis + "ms." );
365             }
366             catch ( NumberFormatException ignore )
367             {
368                 log.debug( "Remote cache socket timeout property " + SOCKET_TIMEOUT_MILLIS
369                     + " not specified.  The default will be used." );
370             }
371         }
372 
373         String lccStr = prop.getProperty( REMOTE_LOCAL_CLUSTER_CONSISTENCY );
374         if ( lccStr != null )
375         {
376             boolean lcc = Boolean.valueOf( lccStr ).booleanValue();
377             rcsa.setLocalClusterConsistency( lcc );
378         }
379 
380         String acgStr = prop.getProperty( REMOTE_ALLOW_CLUSTER_GET );
381         if ( acgStr != null )
382         {
383             boolean acg = Boolean.valueOf( acgStr ).booleanValue();
384             rcsa.setAllowClusterGet( acg );
385         }
386 
387         // Register the RemoteCacheServer remote object in the registry.
388         rcsa.setRemoteServiceName( prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim() );
389     }
390 
391     /**
392      * Unbinds the remote server.
393      * <p>
394      * @param host
395      * @param port
396      * @exception IOException
397      */
398     static void shutdownImpl( String host, int port )
399         throws IOException
400     {
401         synchronized ( RemoteCacheServer.class )
402         {
403             if ( remoteCacheServer == null )
404             {
405                 return;
406             }
407             log.info( "Unbinding host=" + host + ", port=" + port + ", serviceName=" + getServiceName() );
408             try
409             {
410                 Naming.unbind( "//" + host + ":" + port + "/" + getServiceName() );
411             }
412             catch ( MalformedURLException ex )
413             {
414                 // impossible case.
415                 throw new IllegalArgumentException( ex.getMessage() + "; host=" + host + ", port=" + port
416                     + ", serviceName=" + getServiceName() );
417             }
418             catch ( NotBoundException ex )
419             {
420                 // ignore.
421             }
422             remoteCacheServer.release();
423             remoteCacheServer = null;
424             // TODO: safer exit ?
425             try
426             {
427                 Thread.sleep( 2000 );
428             }
429             catch ( InterruptedException ex )
430             {
431                 // swallow
432             }
433             System.exit( 0 );
434         }
435     }
436 
437     /**
438      * Creates an local RMI registry on the default port, starts up the remote cache server, and
439      * binds it to the registry.
440      * <p>
441      * A remote cache is either a local cache or a cluster cache.
442      * <p>
443      * @param args The command line arguments
444      * @throws Exception
445      */
446     public static void main( String[] args )
447         throws Exception
448     {
449         Properties prop = args.length > 0 ? RemoteUtils.loadProps( args[args.length - 1] ) : new Properties();
450 
451         int port;
452         try
453         {
454             port = Integer.parseInt( prop.getProperty( "registry.port" ) );
455         }
456         catch ( NumberFormatException ex )
457         {
458             port = Registry.REGISTRY_PORT;
459         }
460 
461         // shutdown
462         if ( args.length > 0 && args[0].toLowerCase().indexOf( "-shutdown" ) != -1 )
463         {
464             String serviceName = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim();
465             String registry = "//:" + port + "/" + serviceName;
466 
467             if ( log.isDebugEnabled() )
468             {
469                 log.debug( "looking up server " + registry );
470             }
471             Object obj = Naming.lookup( registry );
472             if ( log.isDebugEnabled() )
473             {
474                 log.debug( "server found" );
475             }
476             IRemoteCacheServiceAdmin admin = (IRemoteCacheServiceAdmin) obj;
477             try
478             {
479                 admin.shutdown();
480             }
481             catch ( Exception ex )
482             {
483                 log.error( "Problem calling shutdown.", ex );
484             }
485             log.debug( "done." );
486             System.exit( 0 );
487         }
488 
489         // STATS
490         if ( args.length > 0 && args[0].toLowerCase().indexOf( "-stats" ) != -1 )
491         {
492 
493             log.debug( "getting cache stats" );
494 
495             try
496             {
497                 String serviceName = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim();
498                 String registry = "//:" + port + "/" + serviceName;
499                 log.debug( "looking up server " + registry );
500                 Object obj = Naming.lookup( registry );
501                 log.debug( "server found" );
502 
503                 log.debug( "obj = " + obj );
504                 IRemoteCacheServiceAdmin admin = (IRemoteCacheServiceAdmin) obj;
505 
506                 try
507                 {
508                     System.out.println( admin.getStats().toString() );
509                     log.debug( admin.getStats() );
510                 }
511                 catch ( Exception es )
512                 {
513                     log.error( es );
514                 }
515 
516             }
517             catch ( Exception ex )
518             {
519                 log.error( "Problem getting stats.", ex );
520             }
521             log.debug( "done." );
522             System.exit( 0 );
523         }
524 
525         // startup.
526         String host = prop.getProperty( "registry.host" );
527 
528         if ( host == null || host.trim().equals( "" ) || host.trim().equals( "localhost" ) )
529         {
530             log.debug( "main> creating registry on the localhost" );
531             port = RemoteUtils.createRegistry( port );
532         }
533         log.debug( "main> starting up RemoteCacheServer" );
534         RemoteCacheServerFactory.startup( host, port, args.length > 0 ? args[0] : null );
535         log.debug( "main> done" );
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 
554     /**
555      * Allows us to set the daemon status on the clockdaemon
556      */
557     protected static class MyThreadFactory
558         implements ThreadFactory
559     {
560         /**
561          * @param runner
562          * @return a new thread for the given Runnable
563          */
564         public Thread newThread( Runnable runner )
565         {
566             Thread t = new Thread( runner );
567             String oldName = t.getName();
568             t.setName( "JCS-RemoteCacheServerFactory-" + oldName );
569             t.setDaemon( true );
570             t.setPriority( Thread.MIN_PRIORITY );
571             return t;
572         }
573     }
574 }