/*
* Copyright (c) 2006, 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.mediator.transform;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.axis2.AxisFault;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseLog;
import org.apache.synapse.config.Entry;
import org.apache.synapse.config.SynapseConfigUtils;
import org.apache.synapse.config.SynapseConfiguration;
import org.apache.synapse.mediators.AbstractMediator;
import org.milyn.Smooks;
import org.milyn.SmooksException;
import org.milyn.container.ExecutionContext;
import org.milyn.payload.JavaResult;
import org.milyn.persistence.util.PersistenceUtil;
import org.milyn.scribe.adapter.jpa.EntityManagerRegister;
import org.xml.sax.SAXException;
import org.wso2.carbon.mediator.service.MediatorException;
/**
* Transforms the current message payload using the given Smooks configuration.
* The current message context is replaced with the result as XML.
*/
public class SmooksMediator extends AbstractMediator {
public enum TYPES {
TEXT, XML, JAVA
}
/** Smooks engine */
private Smooks smooks = null;
/** Smooks configuration file */
private String configKey = null;
/** This lock is used to create the smooks configuration synchronously */
private volatile Lock lock = new ReentrantLock();
private Input input = null;
private Output output = null;
/** JPA Persistence Unit Name */
private String persistenceUnitName = null;
private EntityTransaction transaction = null;
private boolean transactionStarted = false;
private EntityManagerFactory emf;
/* To disable huge messages load into the payload , ex: scenarios like JMS routing*/
public static String DISABLE_SMOOKS_RESULT_PAYLOAD = "DISABLE_SMOOKS_RESULT_PAYLOAD";
public boolean mediate(MessageContext synCtx) {
if (synCtx.getEnvironment().isDebuggerEnabled()) {
if (super.divertMediationRoute(synCtx)) {
return true;
}
}
SynapseLog synLog = getLog(synCtx);
String disableResultPayload = (String)synCtx.getProperty(SmooksMediator.DISABLE_SMOOKS_RESULT_PAYLOAD);
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Start : Smooks mediator");
if (synLog.isTraceTraceEnabled()) {
synLog.traceTrace("Message : " + synCtx.getEnvelope());
}
}
// check weather we need to create the smooks configuration
lock.lock();
try {
if (isCreationOrRecreationRequired(synCtx.getConfiguration())) {
smooks = createSmooksConfig(synCtx);
}
} finally {
lock.unlock();
}
// get the input as an stream
StreamSource streamSource = input.process(synCtx, synLog);
// create the execution context for smooks. This is required for every
// message
ExecutionContext executionContext = smooks.createExecutionContext();
try {
// Start transaction if persistenceUnit name is provided
if (persistenceUnitName != null) {
startTransaction(synCtx, executionContext);
}
if (output.getType().equals(SmooksMediator.TYPES.JAVA)) {
// create a JavaResult object to store java result
JavaResult result = new JavaResult();
// filter the message through smooks
smooks.filterSource(executionContext, streamSource, result);
// add result
if(disableResultPayload ==null || (disableResultPayload !=null && !disableResultPayload.equalsIgnoreCase("true")) ){
output.process(null, synCtx, synLog, result);
}
} else {
if (disableResultPayload == null ||
(disableResultPayload != null && !disableResultPayload.equalsIgnoreCase("true"))) {
// create an output stream for store the result
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
StreamResult streamResult = new StreamResult(outputStream);
// filter the message through smooks with stream result
smooks.filterSource(executionContext, streamSource, streamResult);
// add result
output.process(outputStream, synCtx, synLog, null);
} else {
// filter the message through smooks without stream result
smooks.filterSource(executionContext, streamSource);
}
}
if (transactionStarted) {
commitTransaction();
transactionStarted = false;
}
} catch (AxisFault e) {
handleException("Error occured while processing smooks output", e);
} catch (SmooksException e) {
if (transactionStarted) {
this.transaction.rollback();
}
handleException(e.getMessage(), e);
}
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("End : Smooks mediator");
if (synLog.isTraceTraceEnabled()) {
synLog.traceTrace("Message : " + synCtx.getEnvelope());
}
}
return true;
}
/**
* create the entity manager if not created before and begin transaction
*
* @param synCtx
* @param executionContext
*/
private void startTransaction(MessageContext synCtx, ExecutionContext executionContext) {
if (emf == null) {
emf = Persistence.createEntityManagerFactory(persistenceUnitName);
}
EntityManager em = emf.createEntityManager();
PersistenceUtil.setDAORegister(executionContext, new EntityManagerRegister(em));
this.transaction = em.getTransaction();
this.transaction.begin();
this.transactionStarted = true;
}
/**
* commit transaction
*/
private void commitTransaction() {
this.transaction.commit();
}
/**
* Create the smoooks configuration from the configuration key. Smooks
* configuration can be stored as a local entry or can be stored in the
* registry.
*
* @param synCtx
* synapse context
* @return Smooks configuration
*/
private Smooks createSmooksConfig(MessageContext synCtx) {
SynapseLog log = getLog(synCtx);
Object o = synCtx.getEntry(configKey);
if (o == null) {
handleException("Cannot find the object for smooks config key: " + configKey);
}
InputStream in = SynapseConfigUtils.getInputStream(o);
if (in == null) {
handleException("Cannot get the input stream from the config key: " + configKey);
}
try {
Smooks smooks = new Smooks(in);
if (log.isTraceOrDebugEnabled()) {
log.traceOrDebug("Smooks configuration is created from the config key: "
+ configKey);
}
return smooks;
} catch (IOException e) {
handleException("I/O error occurred while creating the Smooks "
+ "configuration from the config key: " + configKey, e);
} catch (SAXException e) {
handleException("XML error occurred while creating the Smooks "
+ "configuration from the config key: " + configKey, e);
}
return null;
}
private boolean isCreationOrRecreationRequired(SynapseConfiguration synCfg) {
// if there are no cachedTemplates we need to create a one
if (smooks == null) {
// this is a creation case
return true;
} else {
// build transformer - if necessary
Entry dp = synCfg.getEntryDefinition(configKey);
// if the smooks config key refers to a dynamic resource, and if it
// has been expired
// it is a recreation case
boolean shouldRecreate = dp != null && dp.isDynamic()
&& (!dp.isCached() || dp.isExpired());
if (shouldRecreate) {
// we should clear all the existing resources
smooks.close();
}
return shouldRecreate;
}
}
private void handleException(String msg) {
log.error(msg);
throw new MediatorException(msg);
}
private void handleException(String msg, Exception ex) {
log.error(msg, ex);
throw new MediatorException(msg + " Caused by " + ex.getMessage());
}
public String getConfigKey() {
return configKey;
}
public void setConfigKey(String configKey) {
this.configKey = configKey;
}
public Input getInput() {
return input;
}
public Output getOutput() {
return output;
}
public void setInput(Input input) {
this.input = input;
}
public void setOutput(Output output) {
this.output = output;
}
public void setPersistenceUnitAttr(String persistenceUnitName) {
this.persistenceUnitName = persistenceUnitName;
}
public String getPersistenceUnitName() {
return persistenceUnitName;
}
}