001package org.apache.commons.jcs3.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.jcs3.auxiliary.remote.behavior.IRemoteCacheAttributes; 026import org.apache.commons.jcs3.auxiliary.remote.behavior.IRemoteCacheListener; 027import org.apache.commons.jcs3.auxiliary.remote.server.behavior.RemoteType; 028import org.apache.commons.jcs3.engine.behavior.ICacheElement; 029import org.apache.commons.jcs3.engine.behavior.ICacheElementSerialized; 030import org.apache.commons.jcs3.engine.behavior.ICompositeCacheManager; 031import org.apache.commons.jcs3.engine.behavior.IElementSerializer; 032import org.apache.commons.jcs3.log.Log; 033import org.apache.commons.jcs3.log.LogManager; 034import org.apache.commons.jcs3.utils.net.HostNameUtil; 035import org.apache.commons.jcs3.utils.serialization.SerializationConversionUtil; 036 037/** Shared listener base. */ 038public abstract class AbstractRemoteCacheListener<K, V> 039 implements IRemoteCacheListener<K, V> 040{ 041 /** The logger */ 042 private static final Log log = LogManager.getLog( AbstractRemoteCacheListener.class ); 043 044 /** The cached name of the local host. The remote server gets this for logging purposes. */ 045 private static String localHostName; 046 047 /** 048 * The cache manager used to put items in different regions. This is set lazily and should not 049 * be sent to the remote server. 050 */ 051 private final ICompositeCacheManager cacheMgr; 052 053 /** The remote cache configuration object. */ 054 private final IRemoteCacheAttributes irca; 055 056 /** This is set by the remote cache server. */ 057 private long listenerId; 058 059 /** Custom serializer. */ 060 private final IElementSerializer elementSerializer; 061 062 /** 063 * Only need one since it does work for all regions, just reference by multiple region names. 064 * <p> 065 * The constructor exports this object, making it available to receive incoming calls. The 066 * callback port is anonymous unless a local port value was specified in the configuration. 067 * <p> 068 * @param irca cache configuration 069 * @param cacheMgr the cache hub 070 * @param elementSerializer a custom serializer 071 */ 072 public AbstractRemoteCacheListener( final IRemoteCacheAttributes irca, final ICompositeCacheManager cacheMgr, final IElementSerializer elementSerializer ) 073 { 074 this.irca = irca; 075 this.cacheMgr = cacheMgr; 076 this.elementSerializer = elementSerializer; 077 } 078 079 /** 080 * Let the remote cache set a listener_id. Since there is only one listener for all the regions 081 * and every region gets registered? the id shouldn't be set if it isn't zero. If it is we 082 * assume that it is a reconnect. 083 * <p> 084 * @param id The new listenerId value 085 * @throws IOException 086 */ 087 @Override 088 public void setListenerId( final long id ) 089 throws IOException 090 { 091 listenerId = id; 092 log.info( "set listenerId = [{0}]", id ); 093 } 094 095 /** 096 * Gets the listenerId attribute of the RemoteCacheListener object. This is stored in the 097 * object. The RemoteCache object contains a reference to the listener and get the id this way. 098 * <p> 099 * @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}