View Javadoc

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.io.Serializable;
24  import java.rmi.RemoteException;
25  import java.rmi.server.UnicastRemoteObject;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
30  import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
31  import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
32  import org.apache.jcs.engine.behavior.ICacheElement;
33  import org.apache.jcs.engine.behavior.ICacheElementSerialized;
34  import org.apache.jcs.engine.behavior.ICompositeCacheManager;
35  import org.apache.jcs.engine.behavior.IElementSerializer;
36  import org.apache.jcs.engine.control.CompositeCache;
37  import org.apache.jcs.engine.control.CompositeCacheManager;
38  import org.apache.jcs.utils.net.HostNameUtil;
39  import org.apache.jcs.utils.serialization.SerializationConversionUtil;
40  import org.apache.jcs.utils.serialization.StandardSerializer;
41  
42  /***
43   * Registered with RemoteCache server. The server updates the local caches via this listener. Each
44   * server asings a unique listener id for a listener.
45   * <p>
46   * One listener is used per remote cache server. The same listener is used for all the regions that
47   * talk to a particular server.
48   */
49  public class RemoteCacheListener
50      implements IRemoteCacheListener, IRemoteCacheConstants, Serializable
51  {
52      private static final long serialVersionUID = 1L;
53  
54      private final static Log log = LogFactory.getLog( RemoteCacheListener.class );
55  
56      private static String localHostName = HostNameUtil.getLocalHostAddress();
57  
58      boolean disposed = false;
59  
60      /***
61       * The cache manager used to put items in differnt regions. This is set lazily and should not be
62       * sent to the remote server.
63       */
64      protected transient ICompositeCacheManager cacheMgr;
65  
66      /*** The remote cache configuration object. */
67      protected IRemoteCacheAttributes irca;
68  
69      /*** Number of put requests received. For debugging only. */
70      protected int puts = 0;
71  
72      /*** Number of remove requests received. For debugging only. */
73      protected int removes = 0;
74  
75      /*** This is set by the remote cache server. */
76      protected long listenerId = 0;
77  
78      private transient IElementSerializer elementSerializer = new StandardSerializer();
79  
80      /***
81       * Only need one since it does work for all regions, just reference by multiple region names.
82       * <p>
83       * The constructor exports this object, making it available to receive incoming calls. The
84       * calback port is anonymous unless a local port vlaue was specified in the configurtion.
85       * @param irca
86       * @param cacheMgr
87       */
88      public RemoteCacheListener( IRemoteCacheAttributes irca, ICompositeCacheManager cacheMgr )
89      {
90          this.irca = irca;
91  
92          this.cacheMgr = cacheMgr;
93  
94          // Export this remote object to make it available to receive incoming
95          // calls, using an anonymous port unless the local port is specified.
96          try
97          {
98              if ( irca.getLocalPort() != 0 )
99              {
100                 UnicastRemoteObject.exportObject( this, irca.getLocalPort() );
101             }
102             else
103             {
104                 UnicastRemoteObject.exportObject( this );
105             }
106         }
107         catch ( RemoteException ex )
108         {
109             log.error( "Problem exporting object.", ex );
110             throw new IllegalStateException( ex.getMessage() );
111         }
112     }
113 
114     /***
115      * Deregisters itself.
116      * <p>
117      * @throws IOException
118      */
119     public synchronized void dispose()
120         throws IOException
121     {
122         if ( !disposed )
123         {
124             if ( log.isInfoEnabled() )
125             {
126                 log.info( "Unexporting listener." );
127             }
128             try
129             {
130                 UnicastRemoteObject.unexportObject( this, true );
131             }
132             catch ( RemoteException ex )
133             {
134                 log.error( "Problem unexporting the listener.", ex );
135                 throw new IllegalStateException( ex.getMessage() );
136             }
137             disposed = true;
138         }
139     }
140 
141     /***
142      * Let the remote cache set a listener_id. Since there is only one listerenr for all the regions
143      * and every region gets registered? the id shouldn't be set if it isn't zero. If it is we
144      * assume that it is a reconnect.
145      * <p>
146      * @param id The new listenerId value
147      * @throws IOException
148      */
149     public void setListenerId( long id )
150         throws IOException
151     {
152         listenerId = id;
153         if ( log.isDebugEnabled() )
154         {
155             log.debug( "set listenerId = [" + id + "]" );
156         }
157     }
158 
159     /***
160      * Gets the listenerId attribute of the RemoteCacheListener object. This is stored int he
161      * object. The RemoteCache object contains a reference to the listener and get the id this way.
162      * @return The listenerId value
163      * @throws IOException
164      */
165     public long getListenerId()
166         throws IOException
167     {
168         if ( log.isDebugEnabled() )
169         {
170             log.debug( "get listenerId = [" + listenerId + "]" );
171         }
172         return listenerId;
173 
174     }
175 
176     /***
177      * Gets the remoteType attribute of the RemoteCacheListener object
178      * @return The remoteType value
179      * @throws IOException
180      */
181     public int getRemoteType()
182         throws IOException
183     {
184         if ( log.isDebugEnabled() )
185         {
186             log.debug( "getRemoteType = [" + irca.getRemoteType() + "]" );
187         }
188         return irca.getRemoteType();
189     }
190 
191     /***
192      * If this is configured to remove on put, then remove the element since it has been updated
193      * elsewhere. cd should be incomplete for faster transmission. We don't want to pass data only
194      * invalidation. The next time it is used the local cache will get the new version from the
195      * remote store.
196      * <p>
197      * If remove on put is not ocnfigured, then update the item.
198      * @param cb
199      * @throws IOException
200      */
201     public void handlePut( ICacheElement cb )
202         throws IOException
203     {
204         if ( irca.getRemoveUponRemotePut() )
205         {
206             if ( log.isDebugEnabled() )
207             {
208                 log.debug( "PUTTING ELEMENT FROM REMOTE, (  invalidating ) " );
209             }
210             handleRemove( cb.getCacheName(), cb.getKey() );
211         }
212         else
213         {
214             puts++;
215             if ( log.isDebugEnabled() )
216             {
217                 log.debug( "PUTTING ELEMENT FROM REMOTE, ( updating ) " );
218                 log.debug( "cb = " + cb );
219 
220                 if ( puts % 100 == 0 )
221                 {
222                     log.debug( "puts = " + puts );
223                 }
224             }
225 
226             ensureCacheManager();
227             CompositeCache cache = cacheMgr.getCache( cb.getCacheName() );
228 
229             // Eventually the instance of will not be necessary.
230             if ( cb != null && cb instanceof ICacheElementSerialized )
231             {
232                 if ( log.isDebugEnabled() )
233                 {
234                     log.debug( "Object needs to be deserialized." );
235                 }
236                 try
237                 {
238                     cb = SerializationConversionUtil.getDeSerializedCacheElement( (ICacheElementSerialized) cb,
239                                                                                   this.elementSerializer );
240                     if ( log.isDebugEnabled() )
241                     {
242                         log.debug( "Deserialized result = " + cb );
243                     }
244                 }
245                 catch ( IOException e )
246                 {
247                     throw e;
248                 }
249                 catch ( ClassNotFoundException e )
250                 {
251                     log.error( "Received a serialized version of a class that we don't know about.", e );
252                 }
253             }
254 
255             cache.localUpdate( cb );
256         }
257 
258         return;
259     }
260 
261     /***
262      * Calls localRemove on the CompositeCache.
263      * <p>
264      * (non-Javadoc)
265      * @see org.apache.jcs.engine.behavior.ICacheListener#handleRemove(java.lang.String,
266      *      java.io.Serializable)
267      */
268     public void handleRemove( String cacheName, Serializable key )
269         throws IOException
270     {
271         removes++;
272         if ( log.isDebugEnabled() )
273         {
274             if ( removes % 100 == 0 )
275             {
276                 log.debug( "removes = " + removes );
277             }
278 
279             log.debug( "handleRemove> cacheName=" + cacheName + ", key=" + key );
280         }
281 
282         ensureCacheManager();
283         CompositeCache cache = cacheMgr.getCache( cacheName );
284 
285         cache.localRemove( key );
286     }
287 
288     /***
289      * Calls localRemoveAll on the CompositeCache.
290      * <p>
291      * (non-Javadoc)
292      * @see org.apache.jcs.engine.behavior.ICacheListener#handleRemoveAll(java.lang.String)
293      */
294     public void handleRemoveAll( String cacheName )
295         throws IOException
296     {
297         if ( log.isDebugEnabled() )
298         {
299             log.debug( "handleRemoveAll> cacheName=" + cacheName );
300         }
301         ensureCacheManager();
302         CompositeCache cache = cacheMgr.getCache( cacheName );
303         cache.localRemoveAll();
304     }
305 
306     /*
307      * (non-Javadoc)
308      * @see org.apache.jcs.engine.behavior.ICacheListener#handleDispose(java.lang.String)
309      */
310     public void handleDispose( String cacheName )
311         throws IOException
312     {
313         if ( log.isDebugEnabled() )
314         {
315             log.debug( "handleDispose> cacheName=" + cacheName );
316         }
317         // TODO consider what to do here, we really don't want to
318         // dispose, we just want to disconnect.
319         // just allow the cache to go into error recovery mode.
320         // getCacheManager().freeCache( cacheName, true );
321     }
322 
323     /***
324      * Gets the cacheManager attribute of the RemoteCacheListener object. This is one of the few
325      * places that force the cache to be a singleton.
326      */
327     protected void ensureCacheManager()
328     {
329         if ( cacheMgr == null )
330         {
331             cacheMgr = CompositeCacheManager.getInstance();
332             log.debug( "had to get cacheMgr" );
333             if ( log.isDebugEnabled() )
334             {
335                 log.debug( "cacheMgr = " + cacheMgr );
336             }
337         }
338         else
339         {
340             if ( log.isDebugEnabled() )
341             {
342                 log.debug( "already got cacheMgr = " + cacheMgr );
343             }
344         }
345     }
346 
347     /***
348      * This is for debugging. It allows the remote server to log the address of clients.
349      */
350     public String getLocalHostAddress()
351         throws IOException
352     {
353         return localHostName;
354     }
355 
356     /***
357      * For easier debugging.
358      * <p>
359      * @return Basic info on this listener.
360      */
361     public String toString()
362     {
363         StringBuffer buf = new StringBuffer();
364         buf.append( "\n RemoteCacheListener: " );
365         buf.append( "\n RemoteHost = " + irca.getRemoteHost() );
366         buf.append( "\n RemotePort = " + irca.getRemotePort() );
367         buf.append( "\n ListenerId = " + listenerId );
368         return buf.toString();
369     }
370 }