/*
* Copyright 2009 VoidSearch.com
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.voidsearch.voidbase.apps.cache;
import com.voidsearch.voidbase.apps.cache.containers.CacheResponse;
import com.voidsearch.voidbase.module.*;
import com.voidsearch.voidbase.config.VoidBaseConfig;
import com.voidsearch.voidbase.config.ConfigException;
import com.voidsearch.voidbase.util.GenericUtil;
import com.voidsearch.voidbase.apps.cache.VoidBaseCache;
import com.voidsearch.voidbase.apps.cache.containers.CacheValue;
import com.voidsearch.voidbase.apps.cache.containers.CacheResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* CacheModule is a generic cache module which implements strategies for containing and routing to cache handlers and
* handling a high-level atomicity of operations on cache implementations based on individual cache handler configurations.
*/
public class CacheModule implements VoidBaseModule {
protected String name = null;
protected String defaultHandler = null;
protected VoidBaseConfig config = null;
protected ConcurrentHashMap<String, VoidBaseCache> handlers = new ConcurrentHashMap<String, VoidBaseCache>();
protected static final String CONFIG_PATH = "cache";
protected static final VoidBaseResponseType DEFAULT_TYPE = VoidBaseResponseType.TEXT;
protected static final Logger logger = LoggerFactory.getLogger(VoidBaseModule.class.getName());
/**
* Creates a new instance of CacheModule
*/
public CacheModule() {
try {
config = VoidBaseConfig.getInstance();
} catch (ConfigException e) {
logger.error("Failed to get configuration instance.");
GenericUtil.logException(e);
}
}
/**
* Initializes CacheModule and all configured cache handlers
* @param name
* @throws VoidBaseModuleException
*/
public void initialize(String name) throws VoidBaseModuleException {
Map<String, String> modules = config.getMap(CONFIG_PATH, "handlers");
if (modules == null || modules.size() == 0) {
throw new VoidBaseModuleException("Failed to initialize module " + name + " - no cache handlers");
}
// set module name
this.name = name;
// get default handler
defaultHandler = config.getString(CONFIG_PATH, "default_handler");
// initialize cache handler
initializeCacheModules(modules);
}
/**
* Handler for all cache requests which are further proxied down to specific cache handler
* @param request
* @return a response from VoidBaseModule handler
* @throws VoidBaseModuleException
*/
public VoidBaseModuleResponse handle(VoidBaseModuleRequest request) throws VoidBaseModuleException {
CacheValue value = new CacheValue();
Map<String, String> params = request.getParams();
List<String> route = getRoute(request.getRoute());
CacheModuleLockStrategy lockStrategy = new CacheModuleLockStrategy();
// check if the method and handler or list of handlers has been set
if (params == null) {
return new VoidBaseModuleResponse("Error - Params not set", VoidBaseResponseStatus.INTERNAL_ERROR, VoidBaseResponseType.TEXT);
}
if (!params.containsKey("method")) {
return new VoidBaseModuleResponse("Error - Method not set", VoidBaseResponseStatus.ERROR, VoidBaseResponseType.TEXT);
}
if (!params.containsKey("handler")) {
return new VoidBaseModuleResponse("Error - Handler not set", VoidBaseResponseStatus.ERROR, VoidBaseResponseType.TEXT);
}
// get methd and process handler list respectively - throw error if handler has not been initialized
String key = getKey(route, params);
String content = getContent(request.getContent(), params);
String method = params.containsKey("method") ? params.get("method").toLowerCase() : null;
String[] requestedHandlers = params.get("handler").split(",");
for (String handlerName: requestedHandlers) {
if (handlers.contains(handlerName)) {
return new VoidBaseModuleResponse("Error - Unknown handler " + handlerName, VoidBaseResponseStatus.ERROR, VoidBaseResponseType.TEXT);
}
// initialize handler
VoidBaseCache handler = (VoidBaseCache)handlers.get(handlerName).clone();
// check if valid method
if (!handler.isRegistered(method)) {
return new VoidBaseModuleResponse("Error - Unknown method '" + method + "' for handler " + handlerName,
VoidBaseResponseStatus.ERROR,
VoidBaseResponseType.TEXT);
}
// handle the method
try {
value.append(lockStrategy.execute(handler, method, route, params, key, content));
} catch (CacheException e) {
logger.error("Cache Exception - " + e.getMessage());
GenericUtil.logException(e);
return new VoidBaseModuleResponse("Error - " + e.getMessage());
}
}
return new VoidBaseModuleResponse(value.text, VoidBaseResponseStatus.OK, VoidBaseResponseType.TEXT);
}
/**
* Handler for all cache requests which are further proxied down to specific cache handler
* @param method
* @param key
* @param content
* @return
* @throws VoidBaseModuleException
*/
public CacheResponse handle(String method, String key, String content) throws VoidBaseModuleException {
return handle(defaultHandler, method, null, key, content);
}
/**
* Handler for all cache requests which are further proxied down to specific cache handler
* @param handler
* @param method
* @param key
* @param content
* @return
* @throws VoidBaseModuleException
*/
public CacheResponse handle(String handler, String method, String key, String content) throws VoidBaseModuleException {
return handle(handler, method, null, key, content);
}
/**
* Handler for all cache requests which are further proxied down to specific cache handler
* @param handler
* @param method
* @param name
* @param key
* @param content
* @return
* @throws VoidBaseModuleException
*/
public CacheResponse handle(String handler, String method, String name, String key, String content) throws VoidBaseModuleException {
CacheModuleLockStrategy lockStrategy = new CacheModuleLockStrategy();
if (handler == null) {
return new CacheResponse(new CacheValue("Unknown handler"), CacheResponseStatus.ERROR);
}
if (method == null) {
return new CacheResponse(new CacheValue("Unknown operation"), CacheResponseStatus.ERROR);
}
if (!handlers.containsKey(handler)) {
return new CacheResponse(new CacheValue("Unknown handler " + handler), CacheResponseStatus.ERROR);
}
// initialize handler
VoidBaseCache cache = (VoidBaseCache)handlers.get(handler).clone();
// check if valid method
if (!cache.isRegistered(method)) {
return new CacheResponse(new CacheValue("Unknown method " + method + " for handler " + handler),
CacheResponseStatus.ERROR);
}
try {
return new CacheResponse(lockStrategy.execute(cache, method, name, key, content),
CacheResponseStatus.OK);
} catch (CacheException e) {
logger.error("Cache Exception - " + e.getMessage());
GenericUtil.logException(e);
return new CacheResponse(new CacheValue("Error - " + e.getMessage()), CacheResponseStatus.INTERNAL_ERROR);
}
}
/**
* Currently just logs when CacheModule starts
*/
public void run() {
logger.info("Starting Cache Module...");
}
/**
* Initializes specific cache modules/handlers
* @param modules
* @throws VoidBaseModuleException
*/
protected void initializeCacheModules(Map<String, String> modules) throws VoidBaseModuleException {
Class obj;
for (Map.Entry<String, String> handler: modules.entrySet()) {
logger.info("Initializing cache interface: " + handler.getValue());
try {
obj = Class.forName(handler.getValue());
VoidBaseCache cacheHandler = (VoidBaseCache)obj.newInstance();
cacheHandler.initialize();
handlers.put(handler.getKey(), cacheHandler);
} catch (ClassNotFoundException e) {
logger.info("Class not found: " + handler.getValue());
GenericUtil.logException(e);
throw new VoidBaseModuleException("CacheModule - failed to initialize cache interface: " + handler.getValue());
} catch (IllegalAccessException e) {
logger.info("Access Exception in constructor for: " + handler.getValue());
GenericUtil.logException(e);
throw new VoidBaseModuleException("CacheModule - access exception for cache interface: " + handler.getValue());
} catch (InstantiationException e) {
logger.info("Instantiation Exception for: " + handler.getValue());
GenericUtil.logException(e);
throw new VoidBaseModuleException("CacheModule - failed to instantiate cache interface: " + handler.getValue());
} catch (CacheException e) {
logger.info("Cache exception for: " + handler.getValue());
GenericUtil.logException(e);
throw new VoidBaseModuleException("CacheModule - failed to initialize cache interface: " + handler.getValue());
}
}
}
/**
* Returns a Cache specific route from original request route
* @param route
* @return a vector of route
*/
protected List<String> getRoute(List<String> route) {
Boolean start = false;
String resource = getResource();
ArrayList<String> newRoute = new ArrayList<String>();
for (String str: route) {
if (str.equals(resource)) {
start = true;
} else if (start == true) {
newRoute.add(str);
}
}
return newRoute;
}
/**
* Returns a CacheModule's resource
* @return a module's resource
*/
protected String getResource() {
StringBuilder key = new StringBuilder("modules.");
key . append(name)
. append(".resource");
return config.getString(key.toString());
}
/**
* Returns rendered Cache response from CacheValue object
* @param value
* @param format
* @param status
* @return rendered response from VoidBaseModule handler
*/
protected VoidBaseModuleResponse renderResponse(CacheValue value, String format, CacheResponseStatus status) {
return renderResponse(value.text == null ? "" : value.text, format, status);
}
/**
* Returns rendered Cache response
* @param message
* @param format
* @param status
* @return rendered response from VoidBaseModule handler
*/
protected VoidBaseModuleResponse renderResponse(String message, String format, CacheResponseStatus status) {
String responseText;
VoidBaseResponseType type = VoidBaseResponseType.deserialize(format);
// default type
if (type == VoidBaseResponseType.UNKNOWN) {
type = DEFAULT_TYPE;
}
// render depending on type
if (type == VoidBaseResponseType.JSON) {
responseText = getJSON(message, status);
}
else if (type == VoidBaseResponseType.TEXT) {
responseText = message;
}
else {
return renderResponse("Unknown Format - " + format, DEFAULT_TYPE.serialize(), CacheResponseStatus.ERROR);
}
return new VoidBaseModuleResponse(responseText, renderStatus(status), type);
}
/**
* Returns a key from request params
* @param route
* @param params
* @return a key from a request
*/
protected String getKey(List<String> route, Map<String, String> params) {
if (params.containsKey("key"))
return params.get("key");
if (route.size() > 0)
return route.get(route.size() - 1);
return null;
}
/**
* Returns a content from request params
* @param content
* @param params
* @return a content from a request
*/
protected String getContent(String content, Map<String, String> params) {
if (params.containsKey("content"))
return params.get("content");
if (content == null || content.equals(""))
return null;
return content;
}
/**
* Builds JSON from a cache result response
* @param message
* @param status
* @return serialized JSON
*/
protected String getJSON(String message, CacheResponseStatus status) {
StringBuilder response = new StringBuilder();
response . append("{message=\"") . append(message) . append("\",")
. append(" status=\"") . append(status.serialize()) . append("\"}");
return response.toString();
}
/**
* Returnds response status from a cache response
* @param status
* @return response status of a VoidBaseModule handler
*/
protected VoidBaseResponseStatus renderStatus(CacheResponseStatus status) {
if (status == CacheResponseStatus.OK)
return VoidBaseResponseStatus.OK;
if (status == CacheResponseStatus.ERROR)
return VoidBaseResponseStatus.ERROR;
if (status == CacheResponseStatus.FATAL_ERROR || status == CacheResponseStatus.INTERNAL_ERROR)
return VoidBaseResponseStatus.INTERNAL_ERROR;
return VoidBaseResponseStatus.UNKNOWN;
}
}