001package org.apache.commons.jcs.auxiliary.remote; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.IOException; 023import java.net.UnknownHostException; 024 025import org.apache.commons.jcs.access.exception.CacheException; 026import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes; 027import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener; 028import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType; 029import org.apache.commons.jcs.engine.behavior.ICacheElement; 030import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized; 031import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager; 032import org.apache.commons.jcs.engine.behavior.IElementSerializer; 033import org.apache.commons.jcs.engine.control.CompositeCacheManager; 034import org.apache.commons.jcs.utils.net.HostNameUtil; 035import org.apache.commons.jcs.utils.serialization.SerializationConversionUtil; 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038 039/** Shared listener base. */ 040public abstract class AbstractRemoteCacheListener<K, V> 041 implements IRemoteCacheListener<K, V> 042{ 043 /** The logger */ 044 private static final Log log = LogFactory.getLog( AbstractRemoteCacheListener.class ); 045 046 /** The cached name of the local host. The remote server gets this for logging purposes. */ 047 private static String localHostName = null; 048 049 /** 050 * The cache manager used to put items in different regions. This is set lazily and should not 051 * be sent to the remote server. 052 */ 053 private ICompositeCacheManager cacheMgr; 054 055 /** The remote cache configuration object. */ 056 private final IRemoteCacheAttributes irca; 057 058 /** This is set by the remote cache server. */ 059 private long listenerId = 0; 060 061 /** Custom serializer. */ 062 private IElementSerializer elementSerializer; 063 064 /** 065 * Only need one since it does work for all regions, just reference by multiple region names. 066 * <p> 067 * The constructor exports this object, making it available to receive incoming calls. The 068 * callback port is anonymous unless a local port value was specified in the configuration. 069 * <p> 070 * @param irca cache configuration 071 * @param cacheMgr the cache hub 072 * @param elementSerializer a custom serializer 073 */ 074 public AbstractRemoteCacheListener( IRemoteCacheAttributes irca, ICompositeCacheManager cacheMgr, IElementSerializer elementSerializer ) 075 { 076 this.irca = irca; 077 this.cacheMgr = cacheMgr; 078 this.elementSerializer = elementSerializer; 079 } 080 081 /** 082 * Let the remote cache set a listener_id. Since there is only one listener for all the regions 083 * and every region gets registered? the id shouldn't be set if it isn't zero. If it is we 084 * assume that it is a reconnect. 085 * <p> 086 * @param id The new listenerId value 087 * @throws IOException 088 */ 089 @Override 090 public void setListenerId( long id ) 091 throws IOException 092 { 093 listenerId = id; 094 if ( log.isInfoEnabled() ) 095 { 096 log.info( "set listenerId = [" + id + "]" ); 097 } 098 } 099 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}