View Javadoc
1   package org.apache.commons.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.net.UnknownHostException;
24  
25  import org.apache.commons.jcs.access.exception.CacheException;
26  import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
27  import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
28  import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
29  import org.apache.commons.jcs.engine.behavior.ICacheElement;
30  import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized;
31  import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
32  import org.apache.commons.jcs.engine.behavior.IElementSerializer;
33  import org.apache.commons.jcs.engine.control.CompositeCacheManager;
34  import org.apache.commons.jcs.utils.net.HostNameUtil;
35  import org.apache.commons.jcs.utils.serialization.SerializationConversionUtil;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  /** Shared listener base. */
40  public abstract class AbstractRemoteCacheListener<K, V>
41      implements IRemoteCacheListener<K, V>
42  {
43      /** The logger */
44      private static final Log log = LogFactory.getLog( AbstractRemoteCacheListener.class );
45  
46      /** The cached name of the local host. The remote server gets this for logging purposes. */
47      private static String localHostName = null;
48  
49      /**
50       * The cache manager used to put items in different regions. This is set lazily and should not
51       * be sent to the remote server.
52       */
53      private ICompositeCacheManager cacheMgr;
54  
55      /** The remote cache configuration object. */
56      private final IRemoteCacheAttributes irca;
57  
58      /** This is set by the remote cache server. */
59      private long listenerId = 0;
60  
61      /** Custom serializer. */
62      private IElementSerializer elementSerializer;
63  
64      /**
65       * Only need one since it does work for all regions, just reference by multiple region names.
66       * <p>
67       * The constructor exports this object, making it available to receive incoming calls. The
68       * callback port is anonymous unless a local port value was specified in the configuration.
69       * <p>
70       * @param irca cache configuration
71       * @param cacheMgr the cache hub
72       * @param elementSerializer a custom serializer
73       */
74      public AbstractRemoteCacheListener( IRemoteCacheAttributes irca, ICompositeCacheManager cacheMgr, IElementSerializer elementSerializer )
75      {
76          this.irca = irca;
77          this.cacheMgr = cacheMgr;
78          this.elementSerializer = elementSerializer;
79      }
80  
81      /**
82       * Let the remote cache set a listener_id. Since there is only one listener for all the regions
83       * and every region gets registered? the id shouldn't be set if it isn't zero. If it is we
84       * assume that it is a reconnect.
85       * <p>
86       * @param id The new listenerId value
87       * @throws IOException
88       */
89      @Override
90      public void setListenerId( long id )
91          throws IOException
92      {
93          listenerId = id;
94          if ( log.isInfoEnabled() )
95          {
96              log.info( "set listenerId = [" + id + "]" );
97          }
98      }
99  
100     /**
101      * Gets the listenerId attribute of the RemoteCacheListener object. This is stored in the
102      * object. The RemoteCache object contains a reference to the listener and get the id this way.
103      * <p>
104      * @return The listenerId value
105      * @throws IOException
106      */
107     @Override
108     public long getListenerId()
109         throws IOException
110     {
111         if ( log.isDebugEnabled() )
112         {
113             log.debug( "get listenerId = [" + listenerId + "]" );
114         }
115         return listenerId;
116 
117     }
118 
119     /**
120      * Gets the remoteType attribute of the RemoteCacheListener object
121      * <p>
122      * @return The remoteType value
123      * @throws IOException
124      */
125     @Override
126     public RemoteType getRemoteType()
127         throws IOException
128     {
129         if ( log.isDebugEnabled() )
130         {
131             log.debug( "getRemoteType = [" + irca.getRemoteType() + "]" );
132         }
133         return irca.getRemoteType();
134     }
135 
136     /**
137      * If this is configured to remove on put, then remove the element since it has been updated
138      * elsewhere. cd should be incomplete for faster transmission. We don't want to pass data only
139      * invalidation. The next time it is used the local cache will get the new version from the
140      * remote store.
141      * <p>
142      * If remove on put is not configured, then update the item.
143      * @param cb
144      * @throws IOException
145      */
146     @Override
147     public void handlePut( ICacheElement<K, V> cb )
148         throws IOException
149     {
150         if ( irca.getRemoveUponRemotePut() )
151         {
152             if ( log.isDebugEnabled() )
153             {
154                 log.debug( "PUTTING ELEMENT FROM REMOTE, (  invalidating ) " );
155             }
156             handleRemove( cb.getCacheName(), cb.getKey() );
157         }
158         else
159         {
160             if ( log.isDebugEnabled() )
161             {
162                 log.debug( "PUTTING ELEMENT FROM REMOTE, ( updating ) " );
163                 log.debug( "cb = " + cb );
164             }
165 
166             // Eventually the instance of will not be necessary.
167             if ( cb instanceof ICacheElementSerialized )
168             {
169                 if ( log.isDebugEnabled() )
170                 {
171                     log.debug( "Object needs to be deserialized." );
172                 }
173                 try
174                 {
175                     cb = SerializationConversionUtil.getDeSerializedCacheElement(
176                             (ICacheElementSerialized<K, V>) cb, this.elementSerializer );
177                     if ( log.isDebugEnabled() )
178                     {
179                         log.debug( "Deserialized result = " + cb );
180                     }
181                 }
182                 catch ( IOException e )
183                 {
184                     throw e;
185                 }
186                 catch ( ClassNotFoundException e )
187                 {
188                     log.error( "Received a serialized version of a class that we don't know about.", e );
189                 }
190             }
191 
192             getCacheManager().<K, V>getCache( cb.getCacheName() ).localUpdate( cb );
193         }
194     }
195 
196     /**
197      * Calls localRemove on the CompositeCache.
198      * <p>
199      * @param cacheName
200      * @param key
201      * @throws IOException
202      */
203     @Override
204     public void handleRemove( String cacheName, K key )
205         throws IOException
206     {
207         if ( log.isDebugEnabled() )
208         {
209             log.debug( "handleRemove> cacheName=" + cacheName + ", key=" + key );
210         }
211 
212         getCacheManager().<K, V>getCache( cacheName ).localRemove( key );
213     }
214 
215     /**
216      * Calls localRemoveAll on the CompositeCache.
217      * <p>
218      * @param cacheName
219      * @throws IOException
220      */
221     @Override
222     public void handleRemoveAll( String cacheName )
223         throws IOException
224     {
225         if ( log.isDebugEnabled() )
226         {
227             log.debug( "handleRemoveAll> cacheName=" + cacheName );
228         }
229 
230         getCacheManager().<K, V>getCache( cacheName ).localRemoveAll();
231     }
232 
233     /**
234      * @param cacheName
235      * @throws IOException
236      */
237     @Override
238     public void handleDispose( String cacheName )
239         throws IOException
240     {
241         if ( log.isDebugEnabled() )
242         {
243             log.debug( "handleDispose> cacheName=" + cacheName );
244         }
245         // TODO consider what to do here, we really don't want to
246         // dispose, we just want to disconnect.
247         // just allow the cache to go into error recovery mode.
248         // getCacheManager().freeCache( cacheName, true );
249     }
250 
251     /**
252      * Gets the cacheManager attribute of the RemoteCacheListener object. This is one of the few
253      * places that force the cache to be a singleton.
254      */
255     protected ICompositeCacheManager getCacheManager()
256     {
257         if ( cacheMgr == null )
258         {
259             try
260             {
261                 cacheMgr = CompositeCacheManager.getInstance();
262 
263                 if ( log.isDebugEnabled() )
264                 {
265                     log.debug( "had to get cacheMgr" );
266                     log.debug( "cacheMgr = " + cacheMgr );
267                 }
268             }
269             catch (CacheException e)
270             {
271                 log.error( "Could not get cacheMgr", e );
272             }
273         }
274         else
275         {
276             if ( log.isDebugEnabled() )
277             {
278                 log.debug( "already got cacheMgr = " + cacheMgr );
279             }
280         }
281 
282         return cacheMgr;
283     }
284 
285     /**
286      * This is for debugging. It allows the remote server to log the address of clients.
287      * <p>
288      * @return String
289      * @throws IOException
290      */
291     @Override
292     public synchronized String getLocalHostAddress()
293         throws IOException
294     {
295         if ( localHostName == null )
296         {
297             try
298             {
299                 localHostName = HostNameUtil.getLocalHostAddress();
300             }
301             catch ( UnknownHostException uhe )
302             {
303                 localHostName = "unknown";
304             }
305         }
306         return localHostName;
307     }
308 
309     /**
310      * For easier debugging.
311      * <p>
312      * @return Basic info on this listener.
313      */
314     @Override
315     public String toString()
316     {
317         StringBuilder buf = new StringBuilder();
318         buf.append( "\n AbstractRemoteCacheListener: " );
319         buf.append( "\n RemoteHost = " + irca.getRemoteLocation().toString() );
320         buf.append( "\n ListenerId = " + listenerId );
321         return buf.toString();
322     }
323 }