001package org.apache.commons.jcs3.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.OutputStream; 025import java.io.Serializable; 026import java.util.HashMap; 027import java.util.Map; 028import java.util.Properties; 029import java.util.Set; 030 031import javax.servlet.ServletConfig; 032import javax.servlet.ServletException; 033import javax.servlet.http.HttpServlet; 034import javax.servlet.http.HttpServletRequest; 035import javax.servlet.http.HttpServletResponse; 036 037import org.apache.commons.jcs3.access.exception.CacheException; 038import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheConfigurator; 039import org.apache.commons.jcs3.auxiliary.remote.http.behavior.IRemoteHttpCacheConstants; 040import org.apache.commons.jcs3.auxiliary.remote.value.RemoteCacheRequest; 041import org.apache.commons.jcs3.auxiliary.remote.value.RemoteCacheResponse; 042import org.apache.commons.jcs3.engine.behavior.ICacheElement; 043import org.apache.commons.jcs3.engine.behavior.ICacheServiceNonLocal; 044import org.apache.commons.jcs3.engine.behavior.ICompositeCacheManager; 045import org.apache.commons.jcs3.engine.control.CompositeCacheManager; 046import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger; 047import org.apache.commons.jcs3.log.Log; 048import org.apache.commons.jcs3.log.LogManager; 049import org.apache.commons.jcs3.utils.config.PropertySetter; 050import org.apache.commons.jcs3.utils.serialization.StandardSerializer; 051 052/** 053 * This servlet simply reads and writes objects. The requests are packaged in a general wrapper. The 054 * processor works on the wrapper object and returns a response wrapper. 055 */ 056public class RemoteHttpCacheServlet 057 extends HttpServlet 058{ 059 /** Don't change. */ 060 private static final long serialVersionUID = 8752849397531933346L; 061 062 /** The Logger. */ 063 private static final Log log = LogManager.getLog( RemoteHttpCacheServlet.class ); 064 065 /** The cache manager */ 066 private static CompositeCacheManager cacheMgr; 067 068 /** The service that does the work. */ 069 private static ICacheServiceNonLocal<Serializable, Serializable> remoteCacheService; 070 071 /** This needs to be standard, since the other side is standard */ 072 private static final StandardSerializer serializer = new StandardSerializer(); 073 074 /** Number of service calls. */ 075 private int serviceCalls; 076 077 /** The interval at which we will log the count. */ 078 private static final int logInterval = 100; 079 080 /** 081 * Initializes the cache. 082 * <p> 083 * This provides an easy extension point. Simply extend this servlet and override the init 084 * method to change the way the properties are loaded. 085 * @param config 086 * @throws ServletException 087 */ 088 @Override 089 public void init( final ServletConfig config ) 090 throws ServletException 091 { 092 try 093 { 094 cacheMgr = CompositeCacheManager.getInstance(); 095 } 096 catch (final CacheException e) 097 { 098 throw new ServletException(e); 099 } 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}