View Javadoc
1   package org.apache.commons.jcs3.auxiliary.remote.http.server;
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.InputStream;
24  import java.io.OutputStream;
25  import java.io.Serializable;
26  import java.util.HashMap;
27  import java.util.Map;
28  import java.util.Properties;
29  import java.util.Set;
30  
31  import javax.servlet.ServletConfig;
32  import javax.servlet.ServletException;
33  import javax.servlet.http.HttpServlet;
34  import javax.servlet.http.HttpServletRequest;
35  import javax.servlet.http.HttpServletResponse;
36  
37  import org.apache.commons.jcs3.access.exception.CacheException;
38  import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheConfigurator;
39  import org.apache.commons.jcs3.auxiliary.remote.http.behavior.IRemoteHttpCacheConstants;
40  import org.apache.commons.jcs3.auxiliary.remote.value.RemoteCacheRequest;
41  import org.apache.commons.jcs3.auxiliary.remote.value.RemoteCacheResponse;
42  import org.apache.commons.jcs3.engine.behavior.ICacheElement;
43  import org.apache.commons.jcs3.engine.behavior.ICacheServiceNonLocal;
44  import org.apache.commons.jcs3.engine.behavior.ICompositeCacheManager;
45  import org.apache.commons.jcs3.engine.control.CompositeCacheManager;
46  import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger;
47  import org.apache.commons.jcs3.log.Log;
48  import org.apache.commons.jcs3.log.LogManager;
49  import org.apache.commons.jcs3.utils.config.PropertySetter;
50  import org.apache.commons.jcs3.utils.serialization.StandardSerializer;
51  
52  /**
53   * This servlet simply reads and writes objects. The requests are packaged in a general wrapper. The
54   * processor works on the wrapper object and returns a response wrapper.
55   */
56  public class RemoteHttpCacheServlet
57      extends HttpServlet
58  {
59      /** Don't change. */
60      private static final long serialVersionUID = 8752849397531933346L;
61  
62      /** The Logger. */
63      private static final Log log = LogManager.getLog( RemoteHttpCacheServlet.class );
64  
65      /** The cache manager */
66      private static CompositeCacheManager cacheMgr;
67  
68      /** The service that does the work. */
69      private static ICacheServiceNonLocal<Serializable, Serializable> remoteCacheService;
70  
71      /** This needs to be standard, since the other side is standard */
72      private static final StandardSerializer serializer = new StandardSerializer();
73  
74      /** Number of service calls. */
75      private int serviceCalls;
76  
77      /** The interval at which we will log the count. */
78      private static final int logInterval = 100;
79  
80      /**
81       * Initializes the cache.
82       * <p>
83       * This provides an easy extension point. Simply extend this servlet and override the init
84       * method to change the way the properties are loaded.
85       * @param config
86       * @throws ServletException
87       */
88      @Override
89      public void init( final ServletConfig config )
90          throws ServletException
91      {
92          try
93          {
94              cacheMgr = CompositeCacheManager.getInstance();
95          }
96          catch (final CacheException e)
97          {
98              throw new ServletException(e);
99          }
100 
101         remoteCacheService = createRemoteHttpCacheService( cacheMgr );
102 
103         super.init( config );
104     }
105 
106     /**
107      * Read the request, call the processor, write the response.
108      * <p>
109      * @param request
110      * @param response
111      * @throws ServletException
112      * @throws IOException
113      */
114     @Override
115     public void service( final HttpServletRequest request, final HttpServletResponse response )
116         throws ServletException, IOException
117     {
118         incrementServiceCallCount();
119         log.debug( "Servicing a request. {0}", request );
120 
121         final RemoteCacheRequest<Serializable, Serializable> remoteRequest = readRequest( request );
122         final RemoteCacheResponse<Object> cacheResponse = processRequest( remoteRequest );
123 
124         writeResponse( response, cacheResponse );
125     }
126 
127     /**
128      * Read the request from the input stream.
129      * <p>
130      * @param request
131      * @return RemoteHttpCacheRequest
132      */
133     protected RemoteCacheRequest<Serializable, Serializable> readRequest( final HttpServletRequest request )
134     {
135         RemoteCacheRequest<Serializable, Serializable> remoteRequest = null;
136 
137         try (InputStream inputStream = request.getInputStream())
138         {
139             log.debug( "After getting input stream and before reading it" );
140             remoteRequest = readRequestFromStream( inputStream );
141         }
142         catch ( final IOException | ClassNotFoundException e )
143         {
144             log.error( "Could not get a RemoteHttpCacheRequest object from the input stream.", e );
145         }
146 
147         return remoteRequest;
148     }
149 
150     /**
151      * Reads the response from the stream and then closes it.
152      * <p>
153      * @param inputStream
154      * @return RemoteHttpCacheRequest
155      * @throws IOException
156      * @throws ClassNotFoundException
157      */
158     protected RemoteCacheRequest<Serializable, Serializable> readRequestFromStream( final InputStream inputStream )
159         throws IOException, ClassNotFoundException
160     {
161         return serializer.deSerializeFrom(inputStream, null);
162     }
163 
164     /**
165      * Write the response to the output stream.
166      * <p>
167      * @param response
168      * @param cacheResponse
169      */
170     protected void writeResponse( final HttpServletResponse response, final RemoteCacheResponse<Object> cacheResponse )
171     {
172         try (OutputStream outputStream = response.getOutputStream())
173         {
174             response.setContentType( "application/octet-stream" );
175             serializer.serializeTo(cacheResponse, outputStream);
176         }
177         catch ( final IOException e )
178         {
179             log.error( "Problem writing response. {0}", cacheResponse, e );
180         }
181     }
182 
183     /**
184      * Processes the request. It will call the appropriate method on the service
185      * <p>
186      * @param request
187      * @return RemoteHttpCacheResponse, never null
188      */
189     protected RemoteCacheResponse<Object> processRequest( final RemoteCacheRequest<Serializable, Serializable> request )
190     {
191         final RemoteCacheResponse<Object> response = new RemoteCacheResponse<>();
192 
193         if ( request == null )
194         {
195             final String message = "The request is null. Cannot process";
196             log.warn( message );
197             response.setSuccess( false );
198             response.setErrorMessage( message );
199         }
200         else
201         {
202             try
203             {
204                 switch ( request.getRequestType() )
205                 {
206                     case GET:
207                         final ICacheElement<Serializable, Serializable> element =
208                             remoteCacheService.get( request.getCacheName(), request.getKey(), request.getRequesterId() );
209                         response.setPayload(element);
210                         break;
211                     case GET_MULTIPLE:
212                         final Map<Serializable, ICacheElement<Serializable, Serializable>> elementMap =
213                             remoteCacheService.getMultiple( request.getCacheName(), request.getKeySet(), request.getRequesterId() );
214                         if ( elementMap != null )
215                         {
216                             response.setPayload(new HashMap<>(elementMap));
217                         }
218                         break;
219                     case GET_MATCHING:
220                         final Map<Serializable, ICacheElement<Serializable, Serializable>> elementMapMatching =
221                             remoteCacheService.getMatching( request.getCacheName(), request.getPattern(), request.getRequesterId() );
222                         if ( elementMapMatching != null )
223                         {
224                             response.setPayload(new HashMap<>(elementMapMatching));
225                         }
226                         break;
227                     case REMOVE:
228                         remoteCacheService.remove( request.getCacheName(), request.getKey(), request.getRequesterId() );
229                         break;
230                     case REMOVE_ALL:
231                         remoteCacheService.removeAll( request.getCacheName(), request.getRequesterId() );
232                         break;
233                     case UPDATE:
234                         remoteCacheService.update( request.getCacheElement(), request.getRequesterId() );
235                         break;
236                     case ALIVE_CHECK:
237                     case DISPOSE:
238                         response.setSuccess( true );
239                         // DO NOTHING
240                         break;
241                     case GET_KEYSET:
242                         final Set<Serializable> keys = remoteCacheService.getKeySet( request.getCacheName() );
243                         response.setPayload( keys );
244                         break;
245                     default:
246                         final String message = "Unknown event type.  Cannot process " + request;
247                         log.warn( message );
248                         response.setSuccess( false );
249                         response.setErrorMessage( message );
250                         break;
251                 }
252             }
253             catch ( final IOException e )
254             {
255                 final String message = "Problem processing request. " + request + " Error: " + e.getMessage();
256                 log.error( message, e );
257                 response.setSuccess( false );
258                 response.setErrorMessage( message );
259             }
260         }
261 
262         return response;
263     }
264 
265     /**
266      * Configures the attributes and the event logger and constructs a service.
267      * <p>
268      * @param cacheManager
269      * @return RemoteHttpCacheService
270      */
271     protected <K, V> RemoteHttpCacheService<K, V> createRemoteHttpCacheService( final ICompositeCacheManager cacheManager )
272     {
273         final Properties props = cacheManager.getConfigurationProperties();
274         final ICacheEventLogger cacheEventLogger = configureCacheEventLogger( props );
275         final RemoteHttpCacheServerAttributes attributes = configureRemoteHttpCacheServerAttributes( props );
276 
277         final RemoteHttpCacheService<K, V> service = new RemoteHttpCacheService<>( cacheManager, attributes, cacheEventLogger );
278         log.info( "Created new RemoteHttpCacheService {0}", service );
279         return service;
280     }
281 
282     /**
283      * Tries to get the event logger.
284      * <p>
285      * @param props
286      * @return ICacheEventLogger
287      */
288     protected ICacheEventLogger configureCacheEventLogger( final Properties props )
289     {
290 
291         return AuxiliaryCacheConfigurator
292             .parseCacheEventLogger( props, IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_PREFIX );
293     }
294 
295     /**
296      * Configure.
297      * <p>
298      * jcs.remotehttpcache.serverattributes.ATTRIBUTENAME=ATTRIBUTEVALUE
299      * <p>
300      * @param prop
301      * @return RemoteCacheServerAttributesconfigureRemoteCacheServerAttributes
302      */
303     protected RemoteHttpCacheServerAttributes configureRemoteHttpCacheServerAttributes( final Properties prop )
304     {
305         final RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes();
306 
307         // configure automatically
308         PropertySetter.setProperties( rcsa, prop,
309                                       IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + "." );
310 
311         return rcsa;
312     }
313 
314     /**
315      * @param rcs the remoteCacheService to set
316      */
317     protected void setRemoteCacheService(final ICacheServiceNonLocal<Serializable, Serializable> rcs)
318     {
319         remoteCacheService = rcs;
320     }
321 
322     /**
323      * Log some details.
324      */
325     private void incrementServiceCallCount()
326     {
327         // not thread safe, but it doesn't have to be accurate
328         serviceCalls++;
329         if ( log.isInfoEnabled() && (serviceCalls % logInterval == 0) )
330         {
331             log.info( "serviceCalls = {0}", serviceCalls );
332         }
333     }
334 
335     /** Release the cache manager. */
336     @Override
337     public void destroy()
338     {
339         log.info( "Servlet Destroyed, shutting down JCS." );
340 
341         cacheMgr.shutDown();
342     }
343 
344     /**
345      * Get servlet information
346      * <p>
347      * @return basic info
348      */
349     @Override
350     public String getServletInfo()
351     {
352         return "RemoteHttpCacheServlet";
353     }
354 }