/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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.mediator.cache.config.xml;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.Mediator;
import org.apache.synapse.config.xml.AbstractMediatorFactory;
import org.apache.synapse.config.xml.SequenceMediatorFactory;
import org.apache.synapse.config.xml.XMLConfigConstants;
import org.wso2.carbon.mediator.cache.CacheMediator;
import org.wso2.carbon.mediator.cache.digest.DigestGenerator;
import org.wso2.carbon.mediator.cache.CachingConstants;
import javax.xml.namespace.QName;
import java.util.Iterator;
import java.util.Properties;
/**
* Creates an instance of a Cache mediator using XML configuration specified
*
* <pre>
* <cache [id="string"] [hashGenerator="class"] [timeout="seconds"]
* [scope=(per-host | per-mediator)] collector=(true | false) [maxMessageSize="in-bytes"]>
* <onCacheHit [sequence="key"]>
* (mediator)+
* </onCacheHit>?
* <implementation type=(memory | disk) maxSize="int"/>
* </cache>
* </pre>
*/
public class CacheMediatorFactory extends AbstractMediatorFactory {
/**
* Log object to use when logging is required in this class.
*/
private static final Log log = LogFactory.getLog(CacheMediatorFactory.class);
/**
* QName of the ID of cache configuration
*/
private static final QName ATT_ID = new QName("id");
/**
* QName of the collector
*/
private static final QName ATT_COLLECTOR = new QName("collector");
/**
* QName of the digest generator
*/
private static final QName ATT_HASH_GENERATOR = new QName("hashGenerator");
/**
* QName of the maximum message size
*/
private static final QName ATT_MAX_MSG_SIZE = new QName("maxMessageSize");
/**
* QName of the timeout
*/
private static final QName ATT_TIMEOUT = new QName("timeout");
/**
* QName of the cache scope
*/
private static final QName ATT_SCOPE = new QName("scope");
/**
* QName of the mediator sequence
*/
private static final QName ATT_SEQUENCE = new QName("sequence");
/**
* QName of the implementation type
*/
private static final QName ATT_TYPE = new QName("type");
/**
* QName of the maximum message size
*/
private static final QName ATT_SIZE = new QName("maxSize");
/**
* QName of the onCacheHit mediator sequence reference
*/
private static final QName ON_CACHE_HIT_Q = new QName(XMLConfigConstants.SYNAPSE_NAMESPACE, "onCacheHit");
/**
* QName of the cache implementation
*/
private static final QName IMPLEMENTATION_Q = new QName(XMLConfigConstants.SYNAPSE_NAMESPACE, "implementation");
/**
* This holds the default timeout of the mediator cache
*/
private static final long DEFAULT_TIMEOUT = 5000L;
/**
* This holds the default disk cache size used in cache mediator
*/
private static final int DEFAULT_DISK_CACHE_SIZE = 200;
@Override
public QName getTagQName() {
return CachingConstants.CACHE_Q;
}
@Override
public Mediator createSpecificMediator(OMElement elem, Properties properties) {
if (!CachingConstants.CACHE_Q.equals(elem.getQName())) {
handleException(
"Unable to create the cache mediator. Unexpected element as the cache mediator configuration");
}
CacheMediator cache = new CacheMediator();
OMAttribute idAttr = elem.getAttribute(ATT_ID);
if (idAttr != null && idAttr.getAttributeValue() != null) {
cache.setId(idAttr.getAttributeValue());
}
OMAttribute scopeAttr = elem.getAttribute(ATT_SCOPE);
if (scopeAttr != null && scopeAttr.getAttributeValue() != null &&
isValidScope(scopeAttr.getAttributeValue(), cache.getId())) {
cache.setScope(scopeAttr.getAttributeValue());
} else {
cache.setScope(CachingConstants.SCOPE_PER_HOST);
}
OMAttribute collectorAttr = elem.getAttribute(ATT_COLLECTOR);
if (collectorAttr != null && collectorAttr.getAttributeValue() != null &&
"true".equals(collectorAttr.getAttributeValue())) {
cache.setCollector(true);
} else {
cache.setCollector(false);
OMAttribute hashGeneratorAttr = elem.getAttribute(ATT_HASH_GENERATOR);
if (hashGeneratorAttr != null && hashGeneratorAttr.getAttributeValue() != null) {
try {
Class generator = Class.forName(hashGeneratorAttr.getAttributeValue());
Object o = generator.newInstance();
if (o instanceof DigestGenerator) {
cache.setDigestGenerator((DigestGenerator) o);
} else {
handleException("Specified class for the hashGenerator is not a " +
"DigestGenerator. It *must* implement " +
"org.wso2.carbon.mediator.cache.digest.DigestGenerator interface");
}
} catch (ClassNotFoundException e) {
handleException("Unable to load the hash generator class", e);
} catch (IllegalAccessException e) {
handleException("Unable to access the hash generator class", e);
} catch (InstantiationException e) {
handleException("Unable to instantiate the hash generator class", e);
}
}
OMAttribute timeoutAttr = elem.getAttribute(ATT_TIMEOUT);
if (timeoutAttr != null && timeoutAttr.getAttributeValue() != null) {
cache.setTimeout(Long.parseLong(timeoutAttr.getAttributeValue()));
} else {
cache.setTimeout(DEFAULT_TIMEOUT);
}
OMAttribute maxMessageSizeAttr = elem.getAttribute(ATT_MAX_MSG_SIZE);
if (maxMessageSizeAttr != null && maxMessageSizeAttr.getAttributeValue() != null) {
cache.setMaxMessageSize(Integer.parseInt(maxMessageSizeAttr.getAttributeValue()));
}
OMElement onCacheHitElem = elem.getFirstChildWithName(ON_CACHE_HIT_Q);
if (onCacheHitElem != null) {
OMAttribute sequenceAttr = onCacheHitElem.getAttribute(ATT_SEQUENCE);
if (sequenceAttr != null && sequenceAttr.getAttributeValue() != null) {
cache.setOnCacheHitRef(sequenceAttr.getAttributeValue());
} else if (onCacheHitElem.getFirstElement() != null) {
cache.setOnCacheHitSequence(new SequenceMediatorFactory()
.createAnonymousSequence(onCacheHitElem, properties));
}
}
for (Iterator<OMElement> itr = elem.getChildrenWithName(IMPLEMENTATION_Q); itr.hasNext(); ) {
OMElement implElem = itr.next();
OMAttribute typeAttr = implElem.getAttribute(ATT_TYPE);
OMAttribute sizeAttr = implElem.getAttribute(ATT_SIZE);
if (typeAttr != null && typeAttr.getAttributeValue() != null) {
String type = typeAttr.getAttributeValue();
if (CachingConstants.TYPE_MEMORY.equals(type) && sizeAttr != null &&
sizeAttr.getAttributeValue() != null) {
cache.setInMemoryCacheSize(Integer.parseInt(sizeAttr.getAttributeValue()));
} else if (CachingConstants.TYPE_DISK.equals(type)) {
log.warn("Disk based and hierarchical caching is not implemented yet");
if (sizeAttr != null && sizeAttr.getAttributeValue() != null) {
cache.setDiskCacheSize(Integer.parseInt(sizeAttr.getAttributeValue()));
} else {
cache.setDiskCacheSize(DEFAULT_DISK_CACHE_SIZE);
}
} else {
handleException("unknown implementation type for the Cache mediator");
}
}
}
}
return cache;
}
/**
* Checks the validity of the provided cache scope in cache mediator configuration
*
* @param scope value of the scope attribute parsed in configuration
* @param id value of the id attribute parsed in configuration
* @return boolean value whether the scope is valid or not
*/
private boolean isValidScope(String scope, String id) {
if (CachingConstants.SCOPE_PER_HOST.equals(scope)) {
return true;
} else if (CachingConstants.SCOPE_PER_MEDIATOR.equals(scope)) {
if (id != null) {
return true;
} else {
handleException("Id is required for a cache with scope : " + scope);
return false;
}
} else if (CachingConstants.SCOPE_DISTRIBUTED.equals(scope)) {
return true;
} else {
handleException("Unknown scope " + scope + " for the Cache mediator");
return false;
}
}
}