/*
* Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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 org.wso2.carbon.caching.service;
import org.apache.axis2.AxisFault;
import org.apache.axis2.description.AxisDescription;
import org.apache.axis2.description.AxisModule;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.description.PolicyInclude;
import org.apache.axis2.description.PolicySubject;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.neethi.Policy;
import org.wso2.carbon.caching.CachingComponentConstants;
import org.wso2.carbon.caching.CachingComponentException;
import org.wso2.carbon.caching.CachingConfigData;
import org.wso2.carbon.caching.CachingPolicyUtils;
import org.wso2.carbon.core.AbstractAdmin;
import org.wso2.carbon.core.RegistryResources;
import org.wso2.carbon.core.persistence.PersistenceUtils;
import org.wso2.carbon.registry.core.Association;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import javax.xml.namespace.QName;
import java.util.Collection;
import java.util.Iterator;
/**
* The <code>CachingConfigAdminService</code> class provides service methods to configure the
* caching module for a given service.
*/
public class CachingConfigAdminService extends AbstractAdmin {
/**
* The logger object for this class.
*/
private static final Log log = LogFactory.getLog(CachingConfigAdminService.class);
/**
* Reference to configuration registry instance inside Carbon.
*/
private Registry configRegistry = null;
/**
* Reference to Axis configuration.
*/
private AxisConfiguration axisConfig = null;
/**
* The admin provides the implementations of the caching configuration options.
*/
private CachingPolicyUtils cachingPolicyUtils;
private static final String GLOBALLY_ENGAGED_PARAM_NAME = "globallyEngaged";
private static final String GLOBALLY_ENGAGED_CUSTOM = "globallyEngagedCustom";
private static final String ADMIN_SERVICE_PARAM_NAME = "adminService";
/**
* Creates a new instance of the <code>CachingConfigAdminService</code>.
*/
public CachingConfigAdminService() {
axisConfig = getAxisConfig();
configRegistry = getConfigSystemRegistry();
cachingPolicyUtils = new CachingPolicyUtils(configRegistry);
}
/**
* Enables caching for the given service using the given configuration.
*
* @param serviceName the name of the service to which caching should be enabled.
* @param confData the caching configuration to be used.
* @throws CachingComponentException if engaging of caching is unsuccessful.
*/
public void engageCachingForService(String serviceName, CachingConfigData confData)
throws CachingComponentException {
if (log.isDebugEnabled()) {
log.debug("Enabling caching for the service: " + serviceName);
}
//get AxisService from service name
AxisService service = this.retrieveAxisService(serviceName);
//create service path in configRegistry
String servicePath = RegistryResources.SERVICE_GROUPS
+ service.getAxisServiceGroup().getServiceGroupName()
+ RegistryResources.SERVICES + serviceName;
try {
this.enableCaching(service, confData, servicePath);
} catch (AxisFault af) {
throw new CachingComponentException("errorEngagingModuleToService",
new String[]{serviceName}, af, log);
}
if (log.isDebugEnabled()) {
log.debug("Engaged caching for the Axis service: " + serviceName);
}
}
/**
* Enables caching for the given service using the given configuration.
*
* @param serviceName the name of the service to which caching should be enabled.
* @param confData the caching configuration to be used.
* @param operationName name of the operation
* @return true if already engaged caching at the service level, else false
* @throws CachingComponentException if engaging of caching is unsuccessful.
*/
public boolean engageCachingForOperation(String serviceName, String
operationName, CachingConfigData confData) throws CachingComponentException {
if (log.isDebugEnabled()) {
log.debug("Enabling caching for the operation: " + operationName
+ " of service : " + serviceName);
}
//get AxisService from service name
AxisService service = this.retrieveAxisService(serviceName);
// Retrieves caching module.
AxisModule cachingModule = axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
if (service.isEngaged(cachingModule)) {
return true;
}
//get AxisOperation
AxisOperation operation = service.getOperation(new QName(operationName));
//create operation path in configRegistry
String operationPath = RegistryResources.SERVICE_GROUPS
+ service.getAxisServiceGroup().getServiceGroupName()
+ RegistryResources.SERVICES + serviceName +
RegistryResources.OPERATIONS + operationName;
try {
this.enableCaching(operation, confData, operationPath);
} catch (AxisFault af) {
throw new CachingComponentException("errorEngagingModuleToOperation",
new String[]{operationName}, af, log);
}
if (log.isDebugEnabled()) {
log.debug("Engaged caching for the Axis operation: " + serviceName
+ " of service : " + serviceName);
}
return false;
}
/**
* Engages caching for service or operation
*
* @param description - AxisService or AxisOperation
* @param confData - incomming config data
* @param engagementPath - service path or operation path
* @throws CachingComponentException - errors
* @throws AxisFault - errors on AxisDescription
*/
private void enableCaching(AxisDescription description, CachingConfigData confData,
String engagementPath)
throws CachingComponentException, AxisFault {
// Retrieves caching module.
AxisModule cachingModule = axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
try {
configRegistry.beginTransaction();
// Checks if an association exists between engagementPath and moduleResourcePath.
Association[] associations = configRegistry.getAssociations(engagementPath,
RegistryResources.Associations.ENGAGED_MODULES);
boolean associationExist = false;
for (Association association : associations) {
if (association.getDestinationPath().equals(getModuleResourcePath(cachingModule))) {
associationExist = true;
break;
}
}
// If no association exist between engagementPath and moduleResourcePath then
// add new association between them.
if (!associationExist) {
configRegistry.addAssociation(engagementPath, getModuleResourcePath(cachingModule),
RegistryResources.Associations.ENGAGED_MODULES);
}
// Gets a Policy object representing the configuration data.
Policy policy = confData.toPolicy();
//Add new policy to the description
this.handleNewPolicyAddition(policy, description.getPolicySubject(), confData);
// Save the policy in the configRegistry.
Resource policyResource = configRegistry.newResource();
String policyType = "" + PolicyInclude.AXIS_SERVICE_POLICY;
String policyPath = engagementPath;
if (description instanceof AxisOperation) {
policyPath = engagementPath.substring(0, engagementPath
.indexOf(RegistryResources.OPERATIONS));
policyType = "" + PolicyInclude.AXIS_OPERATION_POLICY;
}
policyResource.setProperty(RegistryResources.ServiceProperties.POLICY_TYPE, policyType);
policyResource.setProperty(RegistryResources.ServiceProperties.POLICY_UUID,
policy.getId());
cachingPolicyUtils.persistPoliciesToService(policy, policyPath, engagementPath,
policyResource);
if (log.isDebugEnabled()) {
log.debug("Caching policy is saved in the configRegistry");
}
description.engageModule(cachingModule);
configRegistry.commitTransaction();
} catch (Exception e) {
rollbackTransaction(e);
throw new CachingComponentException("errorSavingPolicy", e, log);
}
// TODO - At the moment there is no notification mechanism to notify that the policy used by the particular
// service has changed. So all we can do is to engage the module each time after doing everything with policy.
}
public void globallyEngageCaching(CachingConfigData confData) throws AxisFault,
CachingComponentException {
// Retrieves caching module.
AxisModule cachingModule = axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
// Engage caching only if it is not engaged already.
String resourcePath = getModuleResourcePath(cachingModule);
try {
configRegistry.beginTransaction();
String globalPath = PersistenceUtils.getResourcePath(cachingModule);
if (configRegistry.resourceExists(globalPath)) {
Resource resource = configRegistry.get(globalPath);
if (!Boolean.parseBoolean(resource
.getProperty(GLOBALLY_ENGAGED_CUSTOM))) {
resource.removeProperty(GLOBALLY_ENGAGED_CUSTOM);
resource.addProperty(GLOBALLY_ENGAGED_CUSTOM, "true");
configRegistry.put(globalPath, resource);
}
} else {
Resource globalResource = configRegistry.newResource();
globalResource.addProperty(GLOBALLY_ENGAGED_CUSTOM, "true");
configRegistry.put(globalPath, globalResource);
}
// Gets a Policy object representing the configuration data.
Policy policy = confData.toPolicy();
// Add new policy to module
this.handleNewPolicyAddition(policy, cachingModule.getPolicySubject(), confData);
// Save the policy in the configRegistry.
Resource policyResource = configRegistry.newResource();
policyResource.setProperty(RegistryResources.ModuleProperties.POLICY_TYPE,
"" + PolicyInclude.AXIS_MODULE_POLICY);
policyResource.setProperty(RegistryResources.ModuleProperties.POLICY_UUID,
policy.getId());
policyResource.setProperty(RegistryResources.ModuleProperties.VERSION,
cachingModule.getVersion().toString());
cachingPolicyUtils.persistPoliciesToService(policy, resourcePath, null,
policyResource);
if (log.isDebugEnabled()) {
log.debug("Caching policy is saved in the configRegistry");
}
cachingModule.addParameter(new Parameter(GLOBALLY_ENGAGED_PARAM_NAME, "true"));
//engage the module for every service which is not an admin service
for (Iterator serviceIter = this.axisConfig.getServices().values().iterator();
serviceIter.hasNext();) {
AxisService service = (AxisService) serviceIter.next();
String adminParamValue =
(String) service.getParent().getParameterValue(ADMIN_SERVICE_PARAM_NAME);
//avoid admin services
if (adminParamValue != null && adminParamValue.length() != 0 &&
Boolean.parseBoolean(adminParamValue.trim())) {
continue;
}
this.engageCachingForService(service.getName(), confData);
}
configRegistry.commitTransaction();
} catch (Exception e) {
rollbackTransaction(e);
log.error("Error occured in globally engaging caching", e);
throw new CachingComponentException("errorEngagingModuleAtRegistry", log);
}
}
/**
* Disables caching from the given service
*
* @param serviceName the name of the service from which caching should be disabled
* @throws CachingComponentException if disengaging caching is unsuccessful
*/
public void disengageCachingForService(String serviceName) throws CachingComponentException {
if (log.isDebugEnabled()) {
log.debug("Disabling caching for the service: " + serviceName);
}
// Retrieves the AxisService instance corresponding to the serviceName.
AxisService service = retrieveAxisService(serviceName);
String servicePath = RegistryResources.SERVICE_GROUPS
+ service.getAxisServiceGroup().getServiceGroupName()
+ RegistryResources.SERVICES + serviceName;
try {
this.disableCaching(service, servicePath);
} catch (AxisFault af) {
throw new CachingComponentException("errorDisablingCaching",
new String[]{serviceName}, af, log);
}
if (log.isDebugEnabled()) {
log.debug("Disengaged caching for the Axis service: " + serviceName);
}
}
/**
* Disables caching from the given operation
*
* @param serviceName the name of the service from which caching should be disabled
* @param operationName the name of the operation
* @return true if already engaged caching at the service level, else false
* @throws CachingComponentException if disengaging caching is unsuccessful
*/
public boolean disengageCachingForOperation(String serviceName, String
operationName) throws CachingComponentException {
if (log.isDebugEnabled()) {
log.debug("Disabling caching for the operation: " + operationName +
"service: " + serviceName);
}
// Retrieves the AxisService instance corresponding to the serviceName.
AxisService service = retrieveAxisService(serviceName);
// Retrieves caching module.
AxisModule cachingModule = axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
if (service.isEngaged(cachingModule)) {
return true;
}
//get AxisOperation
AxisOperation operation = service.getOperation(new QName(operationName));
String operationPath = RegistryResources.SERVICE_GROUPS
+ service.getAxisServiceGroup().getServiceGroupName()
+ RegistryResources.SERVICES + serviceName + RegistryResources.OPERATIONS
+ operationName;
try {
this.disableCaching(operation, operationPath);
} catch (AxisFault af) {
throw new CachingComponentException("errorDisablingCaching",
new String[]{serviceName + "operation : " + operationName}, af, log);
}
if (log.isDebugEnabled()) {
log.debug("Disengaged caching for the Axis operation: " + operationName +
"service: " + serviceName);
}
return false;
}
/**
* disengages caching from description
*
* @param description - AxisService or AxisOperation
* @param engagementPath - service path or operation path
* @throws CachingComponentException - error
* @throws AxisFault - error on AxisDescription
*/
private void disableCaching(AxisDescription description, String engagementPath)
throws CachingComponentException, AxisFault {
// Removes caching from both the configRegistry and the description
try {
AxisModule cachingModule = axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
configRegistry.beginTransaction();
/* TODO - replace these two lines with moduleResourcePath += cachingModule.getVersion()
This is done at the moment
*/
// Removes the association from the configRegistry.
configRegistry.removeAssociation(engagementPath, getModuleResourcePath(cachingModule),
RegistryResources.Associations.ENGAGED_MODULES);
// Disengage from description
description.disengageModule(cachingModule);
configRegistry.commitTransaction();
} catch (Exception e) {
rollbackTransaction(e);
throw new CachingComponentException("errorDisablingAtRegistry", e, log);
}
}
private void rollbackTransaction(Exception e) {
try {
configRegistry.rollbackTransaction();
} catch (RegistryException e1) {
log.error("Cannot rollback configRegistry transaction", e);
}
}
public void disengageGlobalCaching() throws CachingComponentException {
if (log.isDebugEnabled()) {
log.debug("Disengaging globally engaged caching");
}
//disengage the caching module
try {
//get the caching module from the current axis config
AxisModule module = this.axisConfig
.getModule(CachingComponentConstants.CACHING_MODULE);
String resourcePath = getModuleResourcePath(module);
configRegistry.beginTransaction();
String globalPath = PersistenceUtils.getResourcePath(module);
if (configRegistry.resourceExists(globalPath)) {
Resource resource = configRegistry.get(globalPath);
if (Boolean.parseBoolean(resource
.getProperty(GLOBALLY_ENGAGED_CUSTOM))) {
resource.removeProperty(GLOBALLY_ENGAGED_CUSTOM);
resource.addProperty(GLOBALLY_ENGAGED_CUSTOM, "false");
configRegistry.put(globalPath, resource);
}
}
Parameter param = module.getParameter(GLOBALLY_ENGAGED_PARAM_NAME);
if (param != null) {
module.removeParameter(module.getParameter(GLOBALLY_ENGAGED_PARAM_NAME));
}
//disengage throttling from all the services which are not admin services
for (Iterator serviceIter = this.axisConfig.getServices().values().iterator();
serviceIter.hasNext();) {
AxisService service = (AxisService) serviceIter.next();
String adminParamValue =
(String) service.getParent().getParameterValue(ADMIN_SERVICE_PARAM_NAME);
if (adminParamValue != null && adminParamValue.length() != 0 &&
Boolean.parseBoolean(adminParamValue.trim())) {
continue;
}
this.disengageCachingForService(service.getName());
}
configRegistry.commitTransaction();
} catch (RegistryException e) {
rollbackTransaction(e);
log.error("Error occured while removing global caching from configRegistry", e);
throw new CachingComponentException("errorDisablingAtRegistry", log);
} catch (AxisFault e) {
rollbackTransaction(e);
log.error("Error occured while disengaging module from AxisService", e);
throw new CachingComponentException("errorDisablingCaching", log);
}
}
/**
* Retrieves the caching configuration associated with the given service
*
* @param serviceName the name of the service from which caching configuration
* should be retrieved
* @return <code>CachingConfigData</code> instance containing the configuration details
* @throws CachingComponentException if retrieving configuration is unsuccessful
*/
public CachingConfigData getCachingPolicyForService(String serviceName)
throws CachingComponentException {
// Retrieves the AxisService instance corresponding to the serviceName.
AxisService service = retrieveAxisService(serviceName);
Collection policyComponents = service.getPolicySubject().getAttachedPolicyComponents();
return getCachingConfig(policyComponents);
}
/**
* Retrieves the caching configuration associated with the given service
*
* @param serviceName the name of the service from which caching configuration
* should be retrieved
* @param operationName name of the operation
* @return <code>CachingConfigData</code> instance containing the configuration details
* @throws CachingComponentException if retrieving configuration is unsuccessful
*/
public CachingConfigData getCachingPolicyForOperation(String serviceName, String operationName)
throws CachingComponentException {
// Retrieves the AxisService instance corresponding to the serviceName.
AxisService service = retrieveAxisService(serviceName);
AxisOperation operation = service.getOperation(new QName(operationName));
AxisModule module = this.axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
Policy[] arr = null;
Collection policyComponents = null;
if (service.isEngaged(module)) {
policyComponents = service.getPolicySubject().getAttachedPolicyComponents();
arr = cachingPolicyUtils.retrieveCachingAssertionAndPolicy(policyComponents);
}
if (arr == null) {
policyComponents = operation.getPolicySubject().getAttachedPolicyComponents();
}
return getCachingConfig(policyComponents);
}
/**
* Returns the current global configuration
*
* @return CachingConfigData
* @throws CachingComponentException - on error
*/
public CachingConfigData getGlobalCachingPolicy() throws CachingComponentException {
AxisModule module = this.axisConfig
.getModule(CachingComponentConstants.CACHING_MODULE);
Collection policyComponents = module.getPolicySubject().getAttachedPolicyComponents();
return getCachingConfig(policyComponents);
}
/**
* Handles the policy addition to given policy subject
*
* @param builtPolicy - policy built from new configs
* @param policySubject - service or operation level policy subject
* @param confData - new config data
*/
private void handleNewPolicyAddition(Policy builtPolicy, PolicySubject
policySubject, CachingConfigData confData) {
Collection policyComponents = policySubject.getAttachedPolicyComponents();
if (policyComponents == null) {
// No policy components found. So we add the new caching policy directly.
policySubject.attachPolicy(builtPolicy);
if (log.isDebugEnabled()) {
log.debug("Used the new policy configuration as no " +
"existing policy components were found");
}
} else {
Policy[] arr = cachingPolicyUtils.retrieveCachingAssertionAndPolicy(policyComponents);
if (arr == null) {
// No caching assertion found. So we add the new caching policy directly.
policySubject.attachPolicy(builtPolicy);
if (log.isDebugEnabled()) {
log.debug("Used the new policy configuration as no existing " +
"caching assertion was found");
}
} else {
// Caching assertion found in service policy. So we update it with the given
// configuration values.
cachingPolicyUtils.updateCachingAssertion(arr[0], confData);
policySubject.updatePolicy(arr[1]);
if (log.isDebugEnabled()) {
log.debug("The existing caching policy is updated with the new configuration data");
}
}
}
}
private CachingConfigData getCachingConfig(Collection policyComponents) {
Policy[] arr;
// Checks whether the service is already associated with any caching policy.
if (policyComponents != null &&
(arr = cachingPolicyUtils.retrieveCachingAssertionAndPolicy(policyComponents)) != null) {
if (log.isDebugEnabled()) {
log.debug("Returns the configuration data generated from the exisiting caching policy");
}
return cachingPolicyUtils.generateConfigurationFromPolicy(arr[0]);
}
return null;
}
/**
* Returns true if the given eservice is engaged with caching
*
* @param serviceName the name of the service which is to be checked for the availability of
* caching
* @return <cdoe>true</code> if the service for the given <code>serviceName</code> is engaged
* with caching and else <code>false</code>
* @throws CachingComponentException if retrieving of the availability of caching is unsuccessful
*/
public boolean isCachingEnabledForService(String serviceName) throws CachingComponentException {
AxisService service = retrieveAxisService(serviceName);
AxisModule module = axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
return service.isEngaged(module);
}
/**
* Returns true if the given eservice is engaged with caching
*
* @param serviceName the name of the service which is to be checked for the availability of
* caching
* @param operationName name of the operation
* @return <cdoe>true</code> if the service for the given <code>serviceName</code> is engaged
* with caching and else <code>false</code>
* @throws CachingComponentException if retrieving of the availability of caching is unsuccessful
*/
public boolean isCachingEnabledForOperation(String serviceName, String operationName)
throws CachingComponentException {
AxisService service = retrieveAxisService(serviceName);
AxisOperation operation = service.getOperation(new QName(operationName));
AxisModule module = axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
return operation.isEngaged(module) || service.isEngaged(module);
}
public boolean isCachingGloballyEnabled() throws CachingComponentException {
AxisModule module = axisConfig.getModule(CachingComponentConstants.CACHING_MODULE);
Parameter param = module.getParameter(GLOBALLY_ENGAGED_PARAM_NAME);
if (param != null) {
String globallyEngaged = (String) param.getValue();
if (globallyEngaged != null && globallyEngaged.length() != 0) {
return Boolean.parseBoolean(globallyEngaged.trim());
}
}
return false;
}
/**
* Retrieves the <code>AxisService</code> instance for the given <code>serviceName</code>.
*
* @param serviceName the name of the axis service to be retrieved
* @return the <code>AxisService</code> instance for the given <code>serviceName</code>.
* @throws CachingComponentException if the retrieval is unsuccessful
*/
private AxisService retrieveAxisService(String serviceName) throws CachingComponentException {
if (log.isDebugEnabled()) {
log.debug("Retrieving Axis service: " + serviceName);
}
AxisService axisService = axisConfig.getServiceForActivation(serviceName);
if (axisService == null) {
throw new CachingComponentException("noSuchService",
new String[]{serviceName}, log);
}
return axisService;
}
/**
* Resource path of the caching module including its version.
*
* @param axisModule AxisModule
* @return module loation
*/
private String getModuleResourcePath(AxisModule axisModule) {
String moduleName = axisModule.getName();
String moduleVersion = axisModule.getVersion().toString();
if (moduleVersion == null || moduleVersion.length() == 0) {
moduleVersion = "SNAPSHOT";
}
return RegistryResources.MODULES + moduleName + "/" + moduleVersion;
}
}