View Javadoc

1   package org.apache.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.io.Serializable;
24  import java.util.Collections;
25  import java.util.Set;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheService;
30  import org.apache.jcs.engine.ZombieCacheService;
31  import org.apache.jcs.engine.behavior.ICacheElement;
32  import org.apache.jcs.utils.struct.BoundedQueue;
33  import org.apache.jcs.utils.timing.ElapsedTimer;
34  
35  /***
36   * Zombie adapter for the remote cache service. It just balks if there is no queue configured. If a
37   * queue is configured, then events will be added to the queue. The idea is that when proper
38   * operation is restored, the remote cache will walk the queue. The queue must be bounded so it does
39   * not eat memory.
40   * <p>
41   * Much of this is potentially reusable.
42   * <p>
43   * TODO figure out a way to get the propagate method into an interface for Zombies.
44   */
45  public class ZombieRemoteCacheService
46      extends ZombieCacheService
47      implements IRemoteCacheService
48  {
49      private final static Log log = LogFactory.getLog( ZombieRemoteCacheService.class );
50  
51      private int maxQueueSize = 0;
52  
53      private BoundedQueue queue;
54  
55      /***
56       * Default.
57       */
58      public ZombieRemoteCacheService()
59      {
60          queue = new BoundedQueue( 0 );
61      }
62  
63      /***
64       * Sets the maximum number of items that will be allowed on the queue.
65       * <p>
66       * @param maxQueueSize
67       */
68      public ZombieRemoteCacheService( int maxQueueSize )
69      {
70          this.maxQueueSize = maxQueueSize;
71          queue = new BoundedQueue( maxQueueSize );
72      }
73  
74      /***
75       * Gets the number of items on the queue.
76       * <p>
77       * @return size of the queue.
78       */
79      public int getQueueSize()
80      {
81          return queue.size();
82      }
83  
84      /***
85       * Adds an update event to the queue if the maxSize is greater than 0;
86       * <p>
87       * (non-Javadoc)
88       * @see org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheService#update(org.apache.jcs.engine.behavior.ICacheElement,
89       *      long)
90       */
91      public void update( ICacheElement item, long listenerId )
92      {
93          if ( maxQueueSize > 0 )
94          {
95              PutEvent event = new PutEvent( item, listenerId );
96              queue.add( event );
97          }
98          // Zombies have no inner life
99          return;
100     }
101 
102     /***
103      * Adds a removeAll event to the queue if the maxSize is greater than 0;
104      * <p>
105      * (non-Javadoc)
106      * @see org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheService#remove(java.lang.String,
107      *      java.io.Serializable, long)
108      */
109     public void remove( String cacheName, Serializable key, long listenerId )
110     {
111         if ( maxQueueSize > 0 )
112         {
113             RemoveEvent event = new RemoveEvent( cacheName, key, listenerId );
114             queue.add( event );
115         }
116         // Zombies have no inner life
117         return;
118     }
119 
120     /***
121      * Adds a removeAll event to the queue if the maxSize is greater than 0;
122      * <p>
123      * (non-Javadoc)
124      * @see org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheService#removeAll(java.lang.String,
125      *      long)
126      */
127     public void removeAll( String cacheName, long listenerId )
128     {
129         if ( maxQueueSize > 0 )
130         {
131             RemoveAllEvent event = new RemoveAllEvent( cacheName, listenerId );
132             queue.add( event );
133         }
134         // Zombies have no inner life
135         return;
136     }
137 
138     /***
139      * Does nothing. Gets are synchronous and cannot be added to a queue.
140      * <p>
141      * (non-Javadoc)
142      * @see org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheService#get(java.lang.String,
143      *      java.io.Serializable, long)
144      */
145     public ICacheElement get( String cacheName, Serializable key, long requesterId )
146         throws IOException
147     {
148         // Zombies have no inner life
149         return null;
150     }
151 
152     /***
153      * Does nothing.
154      * <p>
155      * (non-Javadoc)
156      * @see org.apache.jcs.auxiliary.remote.behavior.IRemoteCacheService#getGroupKeys(java.lang.String,
157      *      java.lang.String)
158      */
159     public Set getGroupKeys( String cacheName, String groupName )
160     {
161         return Collections.EMPTY_SET;
162     }
163 
164     /***
165      * Walk the queue, calling the service for each queue operation.
166      * <p>
167      * @param service
168      * @throws Exception
169      */
170     protected void propagateEvents( IRemoteCacheService service )
171         throws Exception
172     {
173         int cnt = 0;
174         if ( log.isInfoEnabled() )
175         {
176             log.info( "Propagating events to the new RemoteService." );
177         }
178         ElapsedTimer timer = new ElapsedTimer();
179         while ( !queue.isEmpty() )
180         {
181             cnt++;
182 
183             // for each item, call the appropriate service method
184             ZombieEvent event = (ZombieEvent) queue.take();
185 
186             if ( event instanceof PutEvent )
187             {
188                 PutEvent putEvent = (PutEvent) event;
189                 service.update( putEvent.element, event.requesterId );
190             }
191             else if ( event instanceof RemoveEvent )
192             {
193                 RemoveEvent removeEvent = (RemoveEvent) event;
194                 service.remove( event.cacheName, removeEvent.key, event.requesterId );
195             }
196             else if ( event instanceof RemoveAllEvent )
197             {
198                 service.removeAll( event.cacheName, event.requesterId );
199             }
200         }
201         if ( log.isInfoEnabled() )
202         {
203             log.info( "Propagated " + cnt + " events to the new RemoteService in " + timer.getElapsedTimeString() );
204         }
205     }
206 
207     /***
208      * Base of the other events.
209      */
210     private abstract class ZombieEvent
211     {
212         String cacheName;
213 
214         long requesterId;
215     }
216 
217     /***
218      * A basic put event.
219      */
220     private class PutEvent
221         extends ZombieEvent
222     {
223         ICacheElement element;
224 
225         /***
226          * Set the element
227          * @param element
228          * @param requesterId
229          */
230         public PutEvent( ICacheElement element, long requesterId )
231         {
232             this.requesterId = requesterId;
233             this.element = element;
234         }
235     }
236 
237     /***
238      * A basic Remove event.
239      */
240     private class RemoveEvent
241         extends ZombieEvent
242     {
243         Serializable key;
244 
245         /***
246          * Set the element
247          * @param cacheName
248          * @param key
249          * @param requesterId
250          */
251         public RemoveEvent( String cacheName, Serializable key, long requesterId )
252         {
253             this.cacheName = cacheName;
254             this.requesterId = requesterId;
255             this.key = key;
256         }
257     }
258 
259     /***
260      * A basic RemoveAll event.
261      */
262     private class RemoveAllEvent
263         extends ZombieEvent
264     {
265         /***
266          * @param cacheName
267          * @param requesterId
268          */
269         public RemoveAllEvent( String cacheName, long requesterId )
270         {
271             this.cacheName = cacheName;
272             this.requesterId = requesterId;
273         }
274     }
275 }