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