001package org.apache.commons.jcs.auxiliary.remote.http.server; 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.io.InputStream; 024import java.io.ObjectInputStream; 025import java.io.OutputStream; 026import java.io.Serializable; 027import java.util.HashMap; 028import java.util.Map; 029import java.util.Properties; 030import java.util.Set; 031 032import javax.servlet.ServletConfig; 033import javax.servlet.ServletException; 034import javax.servlet.http.HttpServlet; 035import javax.servlet.http.HttpServletRequest; 036import javax.servlet.http.HttpServletResponse; 037 038import org.apache.commons.jcs.access.exception.CacheException; 039import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator; 040import org.apache.commons.jcs.auxiliary.remote.http.behavior.IRemoteHttpCacheConstants; 041import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest; 042import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse; 043import org.apache.commons.jcs.engine.behavior.ICacheElement; 044import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal; 045import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager; 046import org.apache.commons.jcs.engine.control.CompositeCacheManager; 047import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger; 048import org.apache.commons.jcs.io.ObjectInputStreamClassLoaderAware; 049import org.apache.commons.jcs.utils.config.PropertySetter; 050import org.apache.commons.jcs.utils.serialization.StandardSerializer; 051import org.apache.commons.logging.Log; 052import org.apache.commons.logging.LogFactory; 053 054/** 055 * This servlet simply reads and writes objects. The requests are packaged in a general wrapper. The 056 * processor works on the wrapper object and returns a response wrapper. 057 */ 058public class RemoteHttpCacheServlet 059 extends HttpServlet 060{ 061 /** Don't change. */ 062 private static final long serialVersionUID = 8752849397531933346L; 063 064 /** The Logger. */ 065 private static final Log log = LogFactory.getLog( RemoteHttpCacheServlet.class ); 066 067 /** The cache manager */ 068 private static CompositeCacheManager cacheMgr; 069 070 /** The service that does the work. */ 071 private static ICacheServiceNonLocal<Serializable, Serializable> remoteCacheService; 072 073 /** This needs to be standard, since the other side is standard */ 074 private final StandardSerializer serializer = new StandardSerializer(); 075 076 /** Number of service calls. */ 077 private int serviceCalls = 0; 078 079 /** The interval at which we will log the count. */ 080 private final int logInterval = 100; 081 082 /** 083 * Initializes the cache. 084 * <p> 085 * This provides an easy extension point. Simply extend this servlet and override the init 086 * method to change the way the properties are loaded. 087 * @param config 088 * @throws ServletException 089 */ 090 @Override 091 public void init( ServletConfig config ) 092 throws ServletException 093 { 094 try 095 { 096 cacheMgr = CompositeCacheManager.getInstance(); 097 } 098 catch (CacheException e) 099 { 100 throw new ServletException(e); 101 } 102 103 remoteCacheService = createRemoteHttpCacheService( cacheMgr ); 104 105 super.init( config ); 106 } 107 108 /** 109 * Read the request, call the processor, write the response. 110 * <p> 111 * @param request 112 * @param response 113 * @throws ServletException 114 * @throws IOException 115 */ 116 @Override 117 public void service( HttpServletRequest request, HttpServletResponse response ) 118 throws ServletException, IOException 119 { 120 incrementServiceCallCount(); 121 if ( log.isDebugEnabled() ) 122 { 123 log.debug( "Servicing a request. " + request ); 124 } 125 126 RemoteCacheRequest<Serializable, Serializable> remoteRequest = readRequest( request ); 127 RemoteCacheResponse<Object> cacheResponse = processRequest( remoteRequest ); 128 129 writeResponse( response, cacheResponse ); 130 } 131 132 /** 133 * Read the request from the input stream. 134 * <p> 135 * @param request 136 * @return RemoteHttpCacheRequest 137 */ 138 protected RemoteCacheRequest<Serializable, Serializable> readRequest( HttpServletRequest request ) 139 { 140 RemoteCacheRequest<Serializable, Serializable> remoteRequest = null; 141 try 142 { 143 InputStream inputStream = request.getInputStream(); 144 if ( log.isDebugEnabled() ) 145 { 146 log.debug( "After getting input stream and before reading it" ); 147 } 148 149 remoteRequest = readRequestFromStream( inputStream ); 150 } 151 catch ( Exception e ) 152 { 153 log.error( "Could not get a RemoteHttpCacheRequest object from the input stream.", e ); 154 } 155 return remoteRequest; 156 } 157 158 /** 159 * Reads the response from the stream and then closes it. 160 * <p> 161 * @param inputStream 162 * @return RemoteHttpCacheRequest 163 * @throws IOException 164 * @throws ClassNotFoundException 165 */ 166 protected RemoteCacheRequest<Serializable, Serializable> readRequestFromStream( InputStream inputStream ) 167 throws IOException, ClassNotFoundException 168 { 169 ObjectInputStream ois = new ObjectInputStreamClassLoaderAware( inputStream, null ); 170 171 @SuppressWarnings("unchecked") // Need to cast from Object 172 RemoteCacheRequest<Serializable, Serializable> remoteRequest 173 = (RemoteCacheRequest<Serializable, Serializable>) ois.readObject(); 174 ois.close(); 175 return remoteRequest; 176 } 177 178 /** 179 * Write the response to the output stream. 180 * <p> 181 * @param response 182 * @param cacheResponse 183 */ 184 protected void writeResponse( HttpServletResponse response, RemoteCacheResponse<Object> cacheResponse ) 185 { 186 try 187 { 188 response.setContentType( "application/octet-stream" ); 189 190 byte[] responseAsByteAray = serializer.serialize( cacheResponse ); 191 response.setContentLength( responseAsByteAray.length ); 192 193 OutputStream outputStream = response.getOutputStream(); 194 if ( log.isDebugEnabled() ) 195 { 196 log.debug( "Opened output stream. Response size: " + responseAsByteAray.length ); 197 } 198 // WRITE 199 outputStream.write( responseAsByteAray ); 200 outputStream.flush(); 201 outputStream.close(); 202 } 203 catch ( IOException e ) 204 { 205 log.error( "Problem writing response. " + cacheResponse, e ); 206 } 207 } 208 209 /** 210 * Processes the request. It will call the appropriate method on the service 211 * <p> 212 * @param request 213 * @return RemoteHttpCacheResponse, never null 214 */ 215 protected RemoteCacheResponse<Object> processRequest( RemoteCacheRequest<Serializable, Serializable> request ) 216 { 217 RemoteCacheResponse<Object> response = new RemoteCacheResponse<Object>(); 218 219 if ( request == null ) 220 { 221 String message = "The request is null. Cannot process"; 222 log.warn( message ); 223 response.setSuccess( false ); 224 response.setErrorMessage( message ); 225 } 226 else 227 { 228 try 229 { 230 switch ( request.getRequestType() ) 231 { 232 case GET: 233 ICacheElement<Serializable, Serializable> element = 234 remoteCacheService.get( request.getCacheName(), request.getKey(), request.getRequesterId() ); 235 response.setPayload(element); 236 break; 237 case GET_MULTIPLE: 238 Map<Serializable, ICacheElement<Serializable, Serializable>> elementMap = 239 remoteCacheService.getMultiple( request.getCacheName(), request.getKeySet(), request.getRequesterId() ); 240 if ( elementMap != null ) 241 { 242 Map<Serializable, ICacheElement<Serializable, Serializable>> map = new HashMap<Serializable, ICacheElement<Serializable, Serializable>>(); 243 map.putAll(elementMap); 244 response.setPayload(map); 245 } 246 break; 247 case GET_MATCHING: 248 Map<Serializable, ICacheElement<Serializable, Serializable>> elementMapMatching = 249 remoteCacheService.getMatching( request.getCacheName(), request.getPattern(), request.getRequesterId() ); 250 if ( elementMapMatching != null ) 251 { 252 Map<Serializable, ICacheElement<Serializable, Serializable>> map = new HashMap<Serializable, ICacheElement<Serializable, Serializable>>(); 253 map.putAll(elementMapMatching); 254 response.setPayload(map); 255 } 256 break; 257 case REMOVE: 258 remoteCacheService.remove( request.getCacheName(), request.getKey(), request.getRequesterId() ); 259 break; 260 case REMOVE_ALL: 261 remoteCacheService.removeAll( request.getCacheName(), request.getRequesterId() ); 262 break; 263 case UPDATE: 264 remoteCacheService.update( request.getCacheElement(), request.getRequesterId() ); 265 break; 266 case ALIVE_CHECK: 267 case DISPOSE: 268 response.setSuccess( true ); 269 // DO NOTHING 270 break; 271 case GET_KEYSET: 272 Set<Serializable> keys = remoteCacheService.getKeySet( request.getCacheName() ); 273 response.setPayload( keys ); 274 break; 275 default: 276 String message = "Unknown event type. Cannot process " + request; 277 log.warn( message ); 278 response.setSuccess( false ); 279 response.setErrorMessage( message ); 280 break; 281 } 282 } 283 catch ( IOException e ) 284 { 285 String message = "Problem processing request. " + request + " Error: " + e.getMessage(); 286 log.error( message, e ); 287 response.setSuccess( false ); 288 response.setErrorMessage( message ); 289 } 290 } 291 292 return response; 293 } 294 295 /** 296 * Configures the attributes and the event logger and constructs a service. 297 * <p> 298 * @param cacheManager 299 * @return RemoteHttpCacheService 300 */ 301 protected <K, V> RemoteHttpCacheService<K, V> createRemoteHttpCacheService( ICompositeCacheManager cacheManager ) 302 { 303 Properties props = cacheManager.getConfigurationProperties(); 304 ICacheEventLogger cacheEventLogger = configureCacheEventLogger( props ); 305 RemoteHttpCacheServerAttributes attributes = configureRemoteHttpCacheServerAttributes( props ); 306 307 RemoteHttpCacheService<K, V> service = new RemoteHttpCacheService<K, V>( cacheManager, attributes, cacheEventLogger ); 308 if ( log.isInfoEnabled() ) 309 { 310 log.info( "Created new RemoteHttpCacheService " + service ); 311 } 312 return service; 313 } 314 315 /** 316 * Tries to get the event logger. 317 * <p> 318 * @param props 319 * @return ICacheEventLogger 320 */ 321 protected ICacheEventLogger configureCacheEventLogger( Properties props ) 322 { 323 ICacheEventLogger cacheEventLogger = AuxiliaryCacheConfigurator 324 .parseCacheEventLogger( props, IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_PREFIX ); 325 326 return cacheEventLogger; 327 } 328 329 /** 330 * Configure. 331 * <p> 332 * jcs.remotehttpcache.serverattributes.ATTRIBUTENAME=ATTRIBUTEVALUE 333 * <p> 334 * @param prop 335 * @return RemoteCacheServerAttributesconfigureRemoteCacheServerAttributes 336 */ 337 protected RemoteHttpCacheServerAttributes configureRemoteHttpCacheServerAttributes( Properties prop ) 338 { 339 RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes(); 340 341 // configure automatically 342 PropertySetter.setProperties( rcsa, prop, 343 IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + "." ); 344 345 return rcsa; 346 } 347 348 /** 349 * @param rcs the remoteCacheService to set 350 */ 351 protected void setRemoteCacheService(ICacheServiceNonLocal<Serializable, Serializable> rcs) 352 { 353 remoteCacheService = rcs; 354 } 355 356 /** 357 * Log some details. 358 */ 359 private void incrementServiceCallCount() 360 { 361 // not thread safe, but it doesn't have to be accurate 362 serviceCalls++; 363 if ( log.isInfoEnabled() ) 364 { 365 if ( serviceCalls % logInterval == 0 ) 366 { 367 log.info( "serviceCalls = " + serviceCalls ); 368 } 369 } 370 } 371 372 /** Release the cache manager. */ 373 @Override 374 public void destroy() 375 { 376 if ( log.isInfoEnabled() ) 377 { 378 log.info( "Servlet Destroyed, shutting down JCS." ); 379 } 380 381 cacheMgr.shutDown(); 382 } 383 384 /** 385 * Get servlet information 386 * <p> 387 * @return basic info 388 */ 389 @Override 390 public String getServletInfo() 391 { 392 return "RemoteHttpCacheServlet"; 393 } 394}