001package org.apache.commons.jcs.engine; 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.util.Collections; 024import java.util.HashMap; 025import java.util.Map; 026import java.util.Set; 027import java.util.concurrent.ConcurrentLinkedQueue; 028 029import org.apache.commons.jcs.engine.behavior.ICacheElement; 030import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal; 031import org.apache.commons.jcs.utils.timing.ElapsedTimer; 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034 035/** 036 * Zombie adapter for the non local cache services. It just balks if there is no queue configured. 037 * <p> 038 * If a queue is configured, then events will be added to the queue. The idea is that when proper 039 * operation is restored, the non local cache will walk the queue. The queue must be bounded so it 040 * does not eat memory. 041 * <p> 042 * This originated in the remote cache. 043 */ 044public class ZombieCacheServiceNonLocal<K, V> 045 extends ZombieCacheService<K, V> 046 implements ICacheServiceNonLocal<K, V> 047{ 048 /** The logger */ 049 private static final Log log = LogFactory.getLog( ZombieCacheServiceNonLocal.class ); 050 051 /** How big can the queue grow. */ 052 private int maxQueueSize = 0; 053 054 /** The queue */ 055 private final ConcurrentLinkedQueue<ZombieEvent> queue; 056 057 /** 058 * Default. 059 */ 060 public ZombieCacheServiceNonLocal() 061 { 062 queue = new ConcurrentLinkedQueue<ZombieEvent>(); 063 } 064 065 /** 066 * Sets the maximum number of items that will be allowed on the queue. 067 * <p> 068 * @param maxQueueSize 069 */ 070 public ZombieCacheServiceNonLocal( int maxQueueSize ) 071 { 072 this.maxQueueSize = maxQueueSize; 073 queue = new ConcurrentLinkedQueue<ZombieEvent>(); 074 } 075 076 /** 077 * Gets the number of items on the queue. 078 * <p> 079 * @return size of the queue. 080 */ 081 public int getQueueSize() 082 { 083 return queue.size(); 084 } 085 086 private void addQueue(ZombieEvent event) 087 { 088 queue.add(event); 089 if (queue.size() > maxQueueSize) 090 { 091 queue.poll(); // drop oldest entry 092 } 093 } 094 095 /** 096 * Adds an update event to the queue if the maxSize is greater than 0; 097 * <p> 098 * @param item ICacheElement 099 * @param listenerId - identifies the caller. 100 */ 101 @Override 102 public void update( ICacheElement<K, V> item, long listenerId ) 103 { 104 if ( maxQueueSize > 0 ) 105 { 106 PutEvent<K, V> event = new PutEvent<K, V>( item, listenerId ); 107 addQueue( event ); 108 } 109 // Zombies have no inner life 110 } 111 112 /** 113 * Adds a removeAll event to the queue if the maxSize is greater than 0; 114 * <p> 115 * @param cacheName - region name 116 * @param key - item key 117 * @param listenerId - identifies the caller. 118 */ 119 @Override 120 public void remove( String cacheName, K key, long listenerId ) 121 { 122 if ( maxQueueSize > 0 ) 123 { 124 RemoveEvent<K> event = new RemoveEvent<K>( cacheName, key, listenerId ); 125 addQueue( event ); 126 } 127 // Zombies have no inner life 128 } 129 130 /** 131 * Adds a removeAll event to the queue if the maxSize is greater than 0; 132 * <p> 133 * @param cacheName - name of the region 134 * @param listenerId - identifies the caller. 135 */ 136 @Override 137 public void removeAll( String cacheName, long listenerId ) 138 { 139 if ( maxQueueSize > 0 ) 140 { 141 RemoveAllEvent event = new RemoveAllEvent( cacheName, listenerId ); 142 addQueue( event ); 143 } 144 // Zombies have no inner life 145 } 146 147 /** 148 * Does nothing. Gets are synchronous and cannot be added to a queue. 149 * <p> 150 * @param cacheName - region name 151 * @param key - item key 152 * @param requesterId - identifies the caller. 153 * @return null 154 * @throws IOException 155 */ 156 @Override 157 public ICacheElement<K, V> get( String cacheName, K key, long requesterId ) 158 throws IOException 159 { 160 // Zombies have no inner life 161 return null; 162 } 163 164 /** 165 * Does nothing. 166 * <p> 167 * @param cacheName 168 * @param pattern 169 * @param requesterId 170 * @return empty map 171 * @throws IOException 172 */ 173 @Override 174 public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId ) 175 throws IOException 176 { 177 return Collections.emptyMap(); 178 } 179 180 /** 181 * @param cacheName - region name 182 * @param keys - item key 183 * @param requesterId - identity of the caller 184 * @return an empty map. zombies have no internal data 185 */ 186 @Override 187 public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId ) 188 { 189 return new HashMap<K, ICacheElement<K, V>>(); 190 } 191 192 /** 193 * Does nothing. 194 * <p> 195 * @param cacheName - region name 196 * @return empty set 197 */ 198 @Override 199 public Set<K> getKeySet( String cacheName ) 200 { 201 return Collections.emptySet(); 202 } 203 204 /** 205 * Walk the queue, calling the service for each queue operation. 206 * <p> 207 * @param service 208 * @throws Exception 209 */ 210 public synchronized void propagateEvents( ICacheServiceNonLocal<K, V> service ) 211 throws Exception 212 { 213 int cnt = 0; 214 if ( log.isInfoEnabled() ) 215 { 216 log.info( "Propagating events to the new ICacheServiceNonLocal." ); 217 } 218 ElapsedTimer timer = new ElapsedTimer(); 219 while ( !queue.isEmpty() ) 220 { 221 cnt++; 222 223 // for each item, call the appropriate service method 224 ZombieEvent event = queue.poll(); 225 226 if ( event instanceof PutEvent ) 227 { 228 @SuppressWarnings("unchecked") // Type checked by instanceof 229 PutEvent<K, V> putEvent = (PutEvent<K, V>) event; 230 service.update( putEvent.element, event.requesterId ); 231 } 232 else if ( event instanceof RemoveEvent ) 233 { 234 @SuppressWarnings("unchecked") // Type checked by instanceof 235 RemoveEvent<K> removeEvent = (RemoveEvent<K>) event; 236 service.remove( event.cacheName, removeEvent.key, event.requesterId ); 237 } 238 else if ( event instanceof RemoveAllEvent ) 239 { 240 service.removeAll( event.cacheName, event.requesterId ); 241 } 242 } 243 if ( log.isInfoEnabled() ) 244 { 245 log.info( "Propagated " + cnt + " events to the new ICacheServiceNonLocal in " 246 + timer.getElapsedTimeString() ); 247 } 248 } 249 250 /** 251 * Base of the other events. 252 */ 253 protected static abstract class ZombieEvent 254 { 255 /** The name of the region. */ 256 String cacheName; 257 258 /** The id of the requester */ 259 long requesterId; 260 } 261 262 /** 263 * A basic put event. 264 */ 265 private static class PutEvent<K, V> 266 extends ZombieEvent 267 { 268 /** The element to put */ 269 ICacheElement<K, V> element; 270 271 /** 272 * Set the element 273 * @param element 274 * @param requesterId 275 */ 276 public PutEvent( ICacheElement<K, V> element, long requesterId ) 277 { 278 this.requesterId = requesterId; 279 this.element = element; 280 } 281 } 282 283 /** 284 * A basic Remove event. 285 */ 286 private static class RemoveEvent<K> 287 extends ZombieEvent 288 { 289 /** The key to remove */ 290 K key; 291 292 /** 293 * Set the element 294 * @param cacheName 295 * @param key 296 * @param requesterId 297 */ 298 public RemoveEvent( String cacheName, K key, long requesterId ) 299 { 300 this.cacheName = cacheName; 301 this.requesterId = requesterId; 302 this.key = key; 303 } 304 } 305 306 /** 307 * A basic RemoveAll event. 308 */ 309 private static class RemoveAllEvent 310 extends ZombieEvent 311 { 312 /** 313 * @param cacheName 314 * @param requesterId 315 */ 316 public RemoveAllEvent( String cacheName, long requesterId ) 317 { 318 this.cacheName = cacheName; 319 this.requesterId = requesterId; 320 } 321 } 322}