Coverage report

  %line %branch
org.apache.jcs.auxiliary.remote.RemoteCacheManager
41% 
54% 

 1  
 package org.apache.jcs.auxiliary.remote;
 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.rmi.Naming;
 24  
 import java.rmi.registry.Registry;
 25  
 import java.util.HashMap;
 26  
 import java.util.Iterator;
 27  
 import java.util.Map;
 28  
 
 29  
 import org.apache.commons.logging.Log;
 30  
 import org.apache.commons.logging.LogFactory;
 31  
 import org.apache.jcs.auxiliary.AuxiliaryCache;
 32  
 import org.apache.jcs.auxiliary.AuxiliaryCacheManager;
 33  
 import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
 34  
 import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheClient;
 35  
 import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
 36  
 import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheObserver;
 37  
 import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheService;
 38  
 import org.apache.jcs.engine.behavior.ICache;
 39  
 import org.apache.jcs.engine.behavior.ICompositeCacheManager;
 40  
 import org.apache.jcs.engine.behavior.IShutdownObserver;
 41  
 import org.apache.jcs.engine.control.CompositeCacheManager;
 42  
 
 43  
 /**
 44  
  * An instance of RemoteCacheManager corresponds to one remote connection of a specific host and
 45  
  * port. All RemoteCacheManager instances are monitored by the singleton RemoteCacheMonitor
 46  
  * monitoring daemon for error detection and recovery.
 47  
  * <p>
 48  
  * Getting an instance of the remote cache has the effect of getting a handle on the remote server.
 49  
  * Listeners are not registered with the server until a cache is requested from the manager.
 50  
  */
 51  
 public class RemoteCacheManager
 52  
     implements AuxiliaryCacheManager, IShutdownObserver
 53  
 {
 54  
     private static final long serialVersionUID = 798077557166389498L;
 55  
 
 56  14
     private final static Log log = LogFactory.getLog( RemoteCacheManager.class );
 57  
 
 58  
     // Contains mappings of Location instance to RemoteCacheManager instance.
 59  7
     final static Map instances = new HashMap();
 60  
 
 61  
     private static RemoteCacheMonitor monitor;
 62  
 
 63  
     private int clients;
 64  
 
 65  
     // Contains instances of RemoteCacheNoWait managed by a RemoteCacheManager
 66  
     // instance.
 67  7
     final Map caches = new HashMap();
 68  
 
 69  
     final String host;
 70  
 
 71  
     final int port;
 72  
 
 73  
     final String service;
 74  
 
 75  
     private IRemoteCacheAttributes irca;
 76  
 
 77  
     /**
 78  
      * Handle to the remote cache service; or a zombie handle if failed to connect.
 79  
      */
 80  
     private IRemoteCacheService remoteService;
 81  
 
 82  
     /**
 83  
      * Wrapper of the remote cache watch service; or wrapper of a zombie service if failed to
 84  
      * connect.
 85  
      */
 86  
     private RemoteCacheWatchRepairable remoteWatch;
 87  
 
 88  
     /**
 89  
      * The cache manager listeners will need to use to get a cache.
 90  
      */
 91  
     private ICompositeCacheManager cacheMgr;
 92  
 
 93  
     private String registry;
 94  
 
 95  
     /**
 96  
      * Constructs an instance to with the given remote connection parameters. If the connection
 97  
      * cannot be made, "zombie" services will be temporarily used until a successful re-connection
 98  
      * is made by the monitoring daemon.
 99  
      * <p>
 100  
      * @param host
 101  
      * @param port
 102  
      * @param service
 103  
      * @param cacheMgr
 104  
      */
 105  
     private RemoteCacheManager( String host, int port, String service, ICompositeCacheManager cacheMgr )
 106  7
     {
 107  7
         this.host = host;
 108  7
         this.port = port;
 109  7
         this.service = service;
 110  7
         this.cacheMgr = cacheMgr;
 111  
 
 112  
         // register shutdown observer
 113  
         // TODO add the shutdown observable methods to the interface
 114  7
         if ( this.cacheMgr instanceof CompositeCacheManager )
 115  
         {
 116  0
             ( (CompositeCacheManager) this.cacheMgr ).registerShutdownObserver( class="keyword">this );
 117  
         }
 118  
 
 119  7
         this.registry = "//" + host + ":" + port + "/" + service;
 120  7
         if ( log.isDebugEnabled() )
 121  
         {
 122  0
             log.debug( "looking up server " + registry );
 123  
         }
 124  
         try
 125  
         {
 126  7
             Object obj = Naming.lookup( registry );
 127  7
             if ( log.isDebugEnabled() )
 128  
             {
 129  0
                 log.debug( "server found" );
 130  
             }
 131  
             // Successful connection to the remote server.
 132  7
             remoteService = (IRemoteCacheService) obj;
 133  7
             if ( log.isDebugEnabled() )
 134  
             {
 135  0
                 log.debug( "remoteService = " + remoteService );
 136  
             }
 137  
 
 138  7
             remoteWatch = new RemoteCacheWatchRepairable();
 139  7
             remoteWatch.setCacheWatch( (IRemoteCacheObserver) obj );
 140  
         }
 141  0
         catch ( Exception ex )
 142  
         {
 143  
             // Failed to connect to the remote server.
 144  
             // Configure this RemoteCacheManager instance to use the "zombie"
 145  
             // services.
 146  0
             log.error( "Problem finding server at [" + registry + "]", ex );
 147  0
             remoteService = new ZombieRemoteCacheService();
 148  0
             remoteWatch = new RemoteCacheWatchRepairable();
 149  0
             remoteWatch.setCacheWatch( new ZombieRemoteCacheWatch() );
 150  
             // Notify the cache monitor about the error, and kick off the
 151  
             // recovery process.
 152  0
             RemoteCacheMonitor.getInstance().notifyError();
 153  7
         }
 154  7
     }
 155  
 
 156  
     /**
 157  
      * Gets the defaultCattr attribute of the RemoteCacheManager object.
 158  
      * <p>
 159  
      * @return The defaultCattr value
 160  
      */
 161  
     public IRemoteCacheAttributes getDefaultCattr()
 162  
     {
 163  0
         return this.irca;
 164  
     }
 165  
 
 166  
     /**
 167  
      * Adds the remote cache listener to the underlying cache-watch service.
 168  
      * <p>
 169  
      * @param cattr The feature to be added to the RemoteCacheListener attribute
 170  
      * @param listener The feature to be added to the RemoteCacheListener attribute
 171  
      * @throws IOException
 172  
      */
 173  
     public void addRemoteCacheListener( IRemoteCacheAttributes cattr, IRemoteCacheListener listener )
 174  
         throws IOException
 175  
     {
 176  21
         if ( cattr.isReceive() )
 177  
         {
 178  21
             if ( log.isInfoEnabled() )
 179  
             {
 180  21
                 log.info( "The remote cache is configured to receive events from the remote server.  "
 181  
                     + "We will register a listener." );
 182  
             }
 183  
 
 184  21
             synchronized ( caches )
 185  
             {
 186  21
                 remoteWatch.addCacheListener( cattr.getCacheName(), listener );
 187  21
             }
 188  21
         }
 189  
         else
 190  
         {
 191  0
             if ( log.isInfoEnabled() )
 192  
             {
 193  0
                 log.info( "The remote cache is configured to NOT receive events from the remote server.  "
 194  
                     + "We will NOT register a listener." );
 195  
             }
 196  
         }
 197  21
         return;
 198  
     }
 199  
 
 200  
     /**
 201  
      * Removes a listener. When the primary recovers the failover must deregister itself for a
 202  
      * region. The failover runner will call this method to de-register. We do not want to dergister
 203  
      * all listeners to a remote server, in case a failover is a primary of another region. Having
 204  
      * one regions failover act as another servers primary is not currently supported.
 205  
      * <p>
 206  
      * @param cattr
 207  
      * @param listener
 208  
      * @throws IOException
 209  
      */
 210  
     public void removeRemoteCacheListener( IRemoteCacheAttributes cattr, IRemoteCacheListener listener )
 211  
         throws IOException
 212  
     {
 213  0
         synchronized ( caches )
 214  
         {
 215  0
             remoteWatch.removeCacheListener( cattr.getCacheName(), listener );
 216  0
         }
 217  0
         return;
 218  
     }
 219  
 
 220  
     /**
 221  
      * Stops a listener. This is used to deregister a failover after primary reconnection.
 222  
      * <p>
 223  
      * @param cattr
 224  
      * @throws IOException
 225  
      */
 226  
     public void removeRemoteCacheListener( IRemoteCacheAttributes cattr )
 227  
         throws IOException
 228  
     {
 229  0
         synchronized ( caches )
 230  
         {
 231  0
             RemoteCacheNoWait cache = (RemoteCacheNoWait) caches.get( cattr.getCacheName() );
 232  0
             if ( cache != null )
 233  
             {
 234  0
                 IRemoteCacheClient rc = cache.getRemoteCache();
 235  0
                 if ( log.isDebugEnabled() )
 236  
                 {
 237  0
                     log.debug( "Found cache for[ " + cattr.getCacheName() + "], deregistering listener." );
 238  
                 }
 239  
                 // could also store the listener for a server in the manager.
 240  0
                 IRemoteCacheListener listener = rc.getListener();
 241  0
                 remoteWatch.removeCacheListener( cattr.getCacheName(), listener );
 242  0
             }
 243  
             else
 244  
             {
 245  0
                 if ( cattr.isReceive() )
 246  
                 {
 247  0
                     log.warn( "Trying to deregister Cache Listener that was never registered." );
 248  0
                 }
 249  
                 else
 250  
                 {
 251  0
                     if ( log.isDebugEnabled() )
 252  
                     {
 253  0
                         log.debug( "Since the remote cache is configured to not receive, "
 254  
                             + "there is no listener to deregister." );
 255  
                     }
 256  
                 }
 257  
             }
 258  0
         }
 259  0
         return;
 260  
     }
 261  
 
 262  
     /**
 263  
      * Stops a listener. This is used to deregister a failover after primary reconnection.
 264  
      * <p>
 265  
      * @param cacheName
 266  
      * @throws IOException
 267  
      */
 268  
     public void removeRemoteCacheListener( String cacheName )
 269  
         throws IOException
 270  
     {
 271  0
         synchronized ( caches )
 272  
         {
 273  0
             RemoteCacheNoWait cache = (RemoteCacheNoWait) caches.get( cacheName );
 274  0
             if ( cache != null )
 275  
             {
 276  0
                 IRemoteCacheClient rc = cache.getRemoteCache();
 277  0
                 if ( log.isDebugEnabled() )
 278  
                 {
 279  0
                     log.debug( "Found cache for [" + cacheName + "], deregistering listener." );
 280  
                 }
 281  
                 // could also store the listener for a server in the manager.
 282  0
                 IRemoteCacheListener listener = rc.getListener();
 283  0
                 remoteWatch.removeCacheListener( cacheName, listener );
 284  
             }
 285  0
         }
 286  0
         return;
 287  
     }
 288  
 
 289  
     /**
 290  
      * Returns an instance of RemoteCacheManager for the given connection parameters.
 291  
      * <p>
 292  
      * Host and Port uniquely identify a manager instance.
 293  
      * <p>
 294  
      * Also starts up the monitoring daemon, if not already started.
 295  
      * <p>
 296  
      * If the connection cannot be established, zombie objects will be used for future recovery
 297  
      * purposes.
 298  
      * <p>
 299  
      * @param cattr
 300  
      * @param cacheMgr
 301  
      * @return The instance value
 302  
      * @parma port port of the registry.
 303  
      */
 304  
     public static RemoteCacheManager getInstance( IRemoteCacheAttributes cattr, ICompositeCacheManager cacheMgr )
 305  
     {
 306  28
         String host = cattr.getRemoteHost();
 307  28
         int port = cattr.getRemotePort();
 308  28
         String service = cattr.getRemoteServiceName();
 309  28
         if ( host == null )
 310  
         {
 311  0
             host = "";
 312  
         }
 313  28
         if ( port < 1024 )
 314  
         {
 315  0
             port = Registry.REGISTRY_PORT;
 316  
         }
 317  28
         Location loc = new Location( host, port );
 318  
 
 319  28
         RemoteCacheManager ins = (RemoteCacheManager) instances.get( loc );
 320  28
         synchronized ( instances )
 321  
         {
 322  28
             if ( ins == null )
 323  
             {
 324  7
                 ins = (RemoteCacheManager) instances.get( loc );
 325  7
                 if ( ins == null )
 326  
                 {
 327  
                     // cahnge to use cattr and to set defaults
 328  7
                     ins = new RemoteCacheManager( host, port, service, cacheMgr );
 329  7
                     ins.irca = cattr;
 330  7
                     instances.put( loc, ins );
 331  
                 }
 332  
             }
 333  28
         }
 334  
 
 335  28
         ins.clients++;
 336  
         // Fires up the monitoring daemon.
 337  28
         if ( monitor == null )
 338  
         {
 339  7
             monitor = RemoteCacheMonitor.getInstance();
 340  
             // If the returned monitor is null, it means it's already started
 341  
             // elsewhere.
 342  7
             if ( monitor != null )
 343  
             {
 344  7
                 Thread t = new Thread( monitor );
 345  7
                 t.setDaemon( true );
 346  7
                 t.start();
 347  
             }
 348  
         }
 349  28
         return ins;
 350  
     }
 351  
 
 352  
     /**
 353  
      * Returns a remote cache for the given cache name.
 354  
      * <p>
 355  
      * @param cacheName
 356  
      * @return The cache value
 357  
      */
 358  
     public AuxiliaryCache getCache( String cacheName )
 359  
     {
 360  28
         IRemoteCacheAttributes ca = (IRemoteCacheAttributes) irca.copy();
 361  28
         ca.setCacheName( cacheName );
 362  28
         return getCache( ca );
 363  
     }
 364  
 
 365  
     /**
 366  
      * Gets a RemoteCacheNoWait from the RemoteCacheManager. The RemoteCacheNoWait objects are
 367  
      * identified by the cache name value of the RemoteCacheAttributes object.
 368  
      * <p>
 369  
      * If the client is configured to register a listener, this call results on a listener being
 370  
      * created if one isn't already registered with the remote cache for this region.
 371  
      * <p>
 372  
      * @param cattr
 373  
      * @return The cache value
 374  
      */
 375  
     public AuxiliaryCache getCache( IRemoteCacheAttributes cattr )
 376  
     {
 377  28
         RemoteCacheNoWait c = null;
 378  
 
 379  28
         synchronized ( caches )
 380  
         {
 381  28
             c = (RemoteCacheNoWait) caches.get( cattr.getCacheName() );
 382  28
             if ( c == null )
 383  
             {
 384  
                 // create a listener first and pass it to the remotecache
 385  
                 // sender.
 386  21
                 RemoteCacheListener listener = null;
 387  
                 try
 388  
                 {
 389  21
                     listener = new RemoteCacheListener( cattr, cacheMgr );
 390  21
                     addRemoteCacheListener( cattr, listener );
 391  
                 }
 392  0
                 catch ( IOException ioe )
 393  
                 {
 394  0
                     log.error( ioe.getMessage() );
 395  
                 }
 396  0
                 catch ( Exception e )
 397  
                 {
 398  0
                     log.error( e.getMessage() );
 399  21
                 }
 400  
 
 401  21
                 c = new RemoteCacheNoWait( class="keyword">new RemoteCache( cattr, remoteService, listener ) );
 402  21
                 caches.put( cattr.getCacheName(), c );
 403  
             }
 404  
 
 405  
             // might want to do some listener sanity checking here.
 406  28
         }
 407  
 
 408  28
         return c;
 409  
     }
 410  
 
 411  
     /**
 412  
      * Releases.
 413  
      * <p>
 414  
      * @param name
 415  
      * @throws IOException
 416  
      */
 417  
     public void freeCache( String name )
 418  
         throws IOException
 419  
     {
 420  0
         if ( log.isInfoEnabled() )
 421  
         {
 422  0
             log.info( "freeCache [" + name + "]" );
 423  
         }
 424  0
         ICache c = null;
 425  0
         synchronized ( caches )
 426  
         {
 427  0
             c = (ICache) caches.get( name );
 428  0
         }
 429  0
         if ( c != null )
 430  
         {
 431  0
             this.removeRemoteCacheListener( name );
 432  0
             c.dispose();
 433  
         }
 434  0
     }
 435  
 
 436  
     /**
 437  
      * Gets the stats attribute of the RemoteCacheManager object
 438  
      * <p>
 439  
      * @return The stats value
 440  
      */
 441  
     public String getStats()
 442  
     {
 443  0
         StringBuffer stats = new StringBuffer();
 444  0
         Iterator allCaches = caches.values().iterator();
 445  0
         while ( allCaches.hasNext() )
 446  
         {
 447  0
             ICache c = (ICache) allCaches.next();
 448  0
             if ( c != null )
 449  
             {
 450  0
                 stats.append( c.getCacheName() );
 451  
             }
 452  0
         }
 453  0
         return stats.toString();
 454  
     }
 455  
 
 456  
     /** Shutdown all. */
 457  
     public void release()
 458  
     {
 459  
         // Wait until called by the last client
 460  0
         if ( --clients != 0 )
 461  
         {
 462  0
             return;
 463  
         }
 464  0
         synchronized ( caches )
 465  
         {
 466  0
             Iterator allCaches = caches.values().iterator();
 467  0
             while ( allCaches.hasNext() )
 468  
             {
 469  0
                 ICache c = (ICache) allCaches.next();
 470  0
                 if ( c != null )
 471  
                 {
 472  
                     try
 473  
                     {
 474  
                         // c.dispose();
 475  0
                         freeCache( c.getCacheName() );
 476  
                     }
 477  0
                     catch ( IOException ex )
 478  
                     {
 479  0
                         log.error( "Problem in release.", ex );
 480  0
                     }
 481  
                 }
 482  0
             }
 483  0
         }
 484  0
     }
 485  
 
 486  
     /**
 487  
      * Fixes up all the caches managed by this cache manager.
 488  
      * <p>
 489  
      * @param remoteService
 490  
      * @param remoteWatch
 491  
      */
 492  
     public void fixCaches( IRemoteCacheService remoteService, IRemoteCacheObserver remoteWatch )
 493  
     {
 494  0
         synchronized ( caches )
 495  
         {
 496  0
             this.remoteService = remoteService;
 497  0
             this.remoteWatch.setCacheWatch( remoteWatch );
 498  0
             for ( Iterator en = caches.values().iterator(); en.hasNext(); )
 499  
             {
 500  0
                 RemoteCacheNoWait cache = (RemoteCacheNoWait) en.next();
 501  0
                 cache.fixCache( this.remoteService );
 502  0
             }
 503  0
         }
 504  0
     }
 505  
 
 506  
     /**
 507  
      * Gets the cacheType attribute of the RemoteCacheManager object
 508  
      * @return The cacheType value
 509  
      */
 510  
     public int getCacheType()
 511  
     {
 512  0
         return REMOTE_CACHE;
 513  
     }
 514  
 
 515  
     /**
 516  
      * Location of the RMI registry.
 517  
      */
 518  
     private final static class Location
 519  
     {
 520  
         /** Description of the Field */
 521  
         public final String host;
 522  
 
 523  
         /** Description of the Field */
 524  
         public final int port;
 525  
 
 526  
         /**
 527  
          * Constructor for the Location object
 528  
          * <p>
 529  
          * @param host
 530  
          * @param port
 531  
          */
 532  
         public Location( String host, int port )
 533  
         {
 534  
             this.host = host;
 535  
             this.port = port;
 536  
         }
 537  
 
 538  
         /*
 539  
          * (non-Javadoc)
 540  
          * @see java.lang.Object#equals(java.lang.Object)
 541  
          */
 542  
         public boolean equals( Object obj )
 543  
         {
 544  
             if ( obj == this )
 545  
             {
 546  
                 return true;
 547  
             }
 548  
             if ( obj == null || !( obj instanceof Location ) )
 549  
             {
 550  
                 return false;
 551  
             }
 552  
             Location l = (Location) obj;
 553  
             if ( this.host == null && l.host != class="keyword">null )
 554  
             {
 555  
                 return false;
 556  
             }
 557  
             return host.equals( l.host ) && port == l.port;
 558  
         }
 559  
 
 560  
         /**
 561  
          * @return int
 562  
          */
 563  
         public int hashCode()
 564  
         {
 565  
             return host == null ? port : host.hashCode() ^ port;
 566  
         }
 567  
     }
 568  
 
 569  
     /**
 570  
      * Shutdown callback from composite cache manager.
 571  
      * <p>
 572  
      * (non-Javadoc)
 573  
      * @see org.apache.jcs.engine.behavior.IShutdownObserver#shutdown()
 574  
      */
 575  
     public void shutdown()
 576  
     {
 577  0
         if ( log.isInfoEnabled() )
 578  
         {
 579  0
             log.info( "Observed shutdown request." );
 580  
         }
 581  0
         release();
 582  0
     }
 583  
 
 584  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.