Package org.jboss.soa.esb.actions.converters

Source Code of org.jboss.soa.esb.actions.converters.SmooksTransformer

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*
* (C) 2005-2006, JBoss Inc.
*/
package org.jboss.soa.esb.actions.converters;

import org.apache.log4j.Logger;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.actions.ActionLifecycleException;
import org.jboss.soa.esb.actions.ActionPipelineProcessor;
import org.jboss.soa.esb.actions.ActionProcessingException;
import org.jboss.soa.esb.actions.ActionUtils;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.helpers.KeyValuePair;
import org.jboss.soa.esb.lifecycle.LifecycleResourceException;
import org.jboss.soa.esb.listeners.message.MessageDeliverException;
import org.jboss.soa.esb.message.Body;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.MessagePayloadProxy;
import org.jboss.soa.esb.message.body.content.BytesBody;
import org.jboss.soa.esb.services.transform.TransformationException;
import org.jboss.soa.esb.services.transform.TransformationService;
import org.jboss.soa.esb.smooks.resource.SmooksResource;
import org.milyn.Smooks;
import org.milyn.SmooksUtil;
import org.milyn.container.ExecutionContext;
import org.milyn.payload.StringResult;
import org.milyn.payload.StringSource;
import org.milyn.profile.DefaultProfileSet;
import org.milyn.profile.ProfileStore;
import org.milyn.profile.UnknownProfileMemberException;
import org.xml.sax.SAXException;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

/**
* Smooks Transformer.
* <p/>
* This processor hooks the <a href="http://milyn.codehaus.org/Smooks">Milyn Smooks</a>
* Data Transformation/Processing Engine into a message processing pipeline.
*
* <p/>
* A wide range of source (XML, CSV, EDI etc) and target (XML, Java, CSV, EDI etc) formats
* are supported.
*
* <h3>Transformation Configuration</h3>
* This action works in one of 2 ways:
* <ol>
*      <li>Out of a Smooks resource configuration whose URL is specified directly on the action via the
*          "resource-config" property.  If no URI scheme ("http", "file" etc) is specified on the
*          resource config, the resource is assumed to reside on the local classpath.
*          </p>
*          Example :</p>
*          &lt;property name="<b>resource-config</b>" value="/smooks-res.xml" /&gt;
*      </li>
*      <li>Out of a centralised Smooks resource configuration datasource managed by the Transformation Admin Console.
*          This datasource is configured in the smooks.esb deployment.  See the "console.url" property in the
*          "smooks.esb.properties" file.
*      </li>
* </ol>
*
* If both of the above are specified, the action will use the locally specified config defined on the "resource-config"
* property.  If neither are specified, an error will result.
*
* <p/>
* This transformer also supports Smooks profiles as follows:
* <pre>
* &lt;action name="transformAB" class="<b>org.jboss.soa.esb.actions.converters.SmooksTransformer</b>"&gt;
*   &lt;property name="<b>from</b>" value="A" /&gt;
*   &lt;property name="<b>from-type</b>" value="text/xml:messageAtA" /&gt;
*   &lt;property name="<b>to</b>" value="B" /&gt;
*   &lt;property name="<b>to-type</b>" value="text/xml:messageAtB" /&gt;
* &lt;/action&gt;
* </pre>
*
* <h3>Transformation Input/Output Configuration</h3>
* This action gets the transformation input, and sets the transformation output
* based on the "input-location" and "output-location" configuration properties.
* These properties name the {@link Body Message.Body} location where the transformation input
* and output are attached.
* <p/>
* If either these properties are not set, the action class defaults that value
* to being "{@link Body#DEFAULT_LOCATION defaultEntry}".  In other words, if "input-location" is not configured
* on the action, the action will attempt to load the transformation input from the
* {@link Body Message.Body} location named "{@link Body#DEFAULT_LOCATION defaultEntry}".  If the "output-location"
* is not configured on the action, the action will set the transformation result/output
* in the {@link Body Message.Body} location named "{@link Body#DEFAULT_LOCATION defaultEntry}".
*
* <h3>Java Transformation Input/Output Configuration</h3>
* This action supports source (XML, CSV, EDI etc) to Java object transformation/binding.  See the
* "Transform_*" quickstarts for examples of this and also check out the
* <a href="http://milyn.codehaus.org/Tutorials">Smooks Tutorials</a>.
* <p/>
* The constructed Java object model can be used as part of a
* <a href="http://milyn.codehaus.org/Model+Driven+Transformation">model driven transform</a>, or can
* simply be used by other ESB action instances that follow the SmooksTransformer in an action
* pipeline.
* <p/>
* Such Java object graphs are available to subsequent pipeline action instances because they are
* attached to the ESB Message output by this action and input to the following action(s).  They are bound
* to the Message instance Body
* ({@link Body#add(String, Object) Message.getBody().add(String key, Object object)}) under a key based
* directly on the objects "beanId"
* <a href="http://milyn.codehaus.org/javadoc/smooks-cartridges/javabean/org/milyn/javabean/BeanPopulator.html">as defined in the Smooks Javabean config</a>.
*
* @author <a href="mailto:tom.fennelly@jboss.com">tom.fennelly@jboss.com</a>
* @since Version 4.0
* @deprecated Use {@link org.jboss.soa.esb.smooks.SmooksAction}.
*/

public class SmooksTransformer implements TransformationService, ActionPipelineProcessor {

    /**
     * Action config.
     */
    private ConfigTree actionConfig;
  /**
   * Key for storing/accessing any potential message Body bean HashMaps as populated
   * by the Smooks Javabean Cartridge.
     * @deprecated The Smooks {@link org.milyn.container.ExecutionContext} is
     * attached to the message and can be accessed through the {@link }
   */
  public static final String EXTRACTED_BEANS_HASH = "EXTRACTED_BEANS_HASH";
    /**
     * Action config Smooks configuration key.
     */
    public static final String RESOURCE_CONFIG = "resource-config";
    /**
     * Config key for the message body location on which the input message is attached.
     */
    public static final String INPUT_LOCATION = "input-location";
    /**
     * Config key for the message body location on which the output message is attached.
     */
    public static final String OUTPUT_LOCATION = "output-location";
    /**
     * Config key for the name of the Smooks Execution context object to be
     * output to the "output-location".
     */
    public static final String JAVA_OUTPUT = "java-output-location";

  public static final String FROM = "from";
  public static final String FROM_TYPE = "from-type";
  public static final String TO = "to";
  public static final String TO_TYPE = "to-type";
    public static final String UPDATE_TOPIC="update-topic";

    private static Logger logger = Logger.getLogger(SmooksTransformer.class);
    private Smooks smooks;
    private MessagePayloadProxy payloadProxy;
    private String inputLocation;
    private String outputLocation;
    private String javaOutputLocation;
    private String defaultMessageFromType;
    private String defaultMessageFrom;
    private String defaultMessageToType;
    private String defaultMessageTo;

    /**
     * Public constructor.
     * @param propertiesTree Action Properties.
     * @throws ConfigurationException Action not properly configured.
     */
  public SmooksTransformer(ConfigTree propertiesTree) throws ConfigurationException {
        List<KeyValuePair> properties = propertiesTree.attributesAsList();

        createPayloadProxy(properties, propertiesTree);

        if(javaOutputLocation != null) {
            javaOutputLocation = javaOutputLocation.trim();
        }
       
        // Get the default message flow properties (can be overriden by the message properties)...
    defaultMessageFromType = KeyValuePair.getValue(FROM_TYPE, properties);
    if(defaultMessageFromType != null && defaultMessageFromType.trim().equals("")) {
      throw new ConfigurationException("Empty '" + FROM_TYPE + "' config attribute supplied.");
    }
    defaultMessageToType = KeyValuePair.getValue(TO_TYPE, properties);
    if(defaultMessageToType != null && defaultMessageToType.trim().equals("")) {
      throw new ConfigurationException("Empty '" + TO_TYPE + "' config attribute supplied.");
    }
    defaultMessageFrom = KeyValuePair.getValue(FROM, properties);
    if(defaultMessageFrom != null && defaultMessageFrom.trim().equals("")) {
      throw new ConfigurationException("Empty '" + FROM + "' config attribute supplied.");
    }
    defaultMessageTo = KeyValuePair.getValue(TO, properties);
    if(defaultMessageTo != null && defaultMessageTo.trim().equals("")) {
      throw new ConfigurationException("Empty '" + TO + "' config attribute supplied.");
    }

        actionConfig = propertiesTree;
    }

    private void createPayloadProxy(List<KeyValuePair> properties, ConfigTree propertiesTree) {
        // if no input location given, then assume default location in message body.
        inputLocation = KeyValuePair.getValue(INPUT_LOCATION, properties);
        // if no output location given, then assume default location in message body.
        outputLocation = KeyValuePair.getValue(OUTPUT_LOCATION, properties);
        javaOutputLocation = KeyValuePair.getValue(JAVA_OUTPUT, properties);

        String[] legacyGetLocations;
        String[] legacySetLocations;
        if(inputLocation != null) {
            legacyGetLocations = new String[] {inputLocation, BytesBody.BYTES_LOCATION, ActionUtils.POST_ACTION_DATA};
        } else {
            legacyGetLocations = new String[] {BytesBody.BYTES_LOCATION, ActionUtils.POST_ACTION_DATA};
        }
        if(outputLocation != null) {
            legacySetLocations = new String[] {outputLocation, ActionUtils.POST_ACTION_DATA};
        } else {
            legacySetLocations = new String[] {ActionUtils.POST_ACTION_DATA};
        }
       
        payloadProxy = new MessagePayloadProxy(propertiesTree, legacyGetLocations, legacySetLocations);
    }

    /**
     * Initialise the Smooks instance.
     * @throws ActionLifecycleException Failed to load Smooks configurations.
     */
    public void initialise() throws ActionLifecycleException {
        String resourceConfig = actionConfig.getAttribute(RESOURCE_CONFIG);

        // If there's a Smooks resource config specified on the action config, this instance
        // of the SmooksTransformer will use that configuration.  Otherwise there needs to be a
        // centralised (console based) config specified in the smooks.esb.properties. If not,
        // we have an error!
        if(resourceConfig != null) {
            try {
                smooks = SmooksResource.createSmooksResource(resourceConfig);
            } catch (final LifecycleResourceException lre) {
                throw new ActionLifecycleException("Unexpected exception creating smooks lifecycle resource", lre);
            } catch (final IOException ie) {
                throw new ActionLifecycleException("Unexpected IO exception accessing smooks resource: " + resourceConfig, ie);
            } catch (final SAXException saxe) {
                throw new ActionLifecycleException("Unexpected exception parsing smooks resource: " + resourceConfig, saxe);
            }
        } else {
            throw new ActionLifecycleException("Invalid " + getClass().getSimpleName() + " action configuration.  No 'resource-config' specified on the action.");
        }
       
        logger.info("Smooks configurations are now loaded.");
    }

    public void destroy() throws ActionLifecycleException {
        SmooksResource.closeSmooksResource(smooks);
    }

  /* (non-Javadoc)
   * @see org.jboss.soa.esb.services.transform.TransformationService#transform(org.jboss.soa.esb.message.Message)
   */
  public Message transform(Message message) throws TransformationException {
    try {
      return process(message);
    } catch (ActionProcessingException e) {
      throw new TransformationException(e);
    }
  }

    /* (non-Javadoc)
     * @see org.jboss.soa.esb.actions.ActionProcessor#process(java.lang.Object)
     */
    public Message process(Message message) throws ActionProcessingException {
        String messageProfile = "";

        long startTime = System.nanoTime();

        Object payload = null;
        try {
            payload = payloadProxy.getPayload(message);
        } catch (MessageDeliverException e) {
            throw new ActionProcessingException(e);
        }
     
        try {
          if(payload instanceof byte[]) {
            payload = new String((byte[])payload, "UTF-8");
          }

            if(payload == null) {
                logger.warn("Null message payload.  Returning message unmodified.");
            } else if(payload instanceof String) {
              long start = System.currentTimeMillis();
                ExecutionContext executionContext;

                // Register the message profile with Smooks (if there is one and it's not already registered)...
                messageProfile = registerMessageProfile(message, smooks);

              // Filter and Serialise...
                if(messageProfile == null) {
                    // Not using profiles on this transformation.
                    executionContext = smooks.createExecutionContext();
                } else {
                    executionContext = smooks.createExecutionContext(messageProfile);
                }
               
                StringResult result = new StringResult();
                smooks.filterSource(executionContext, new StringSource((String) payload), result);

                HashMap beanHash = new HashMap(executionContext.getBeanContext().getBeanMap());
              if(beanHash != null) {
                message.getBody().add(EXTRACTED_BEANS_HASH, beanHash); // Backward compatibility.
              } else {
                    message.getBody().remove(EXTRACTED_BEANS_HASH); // Backward compatibility.
                }
             
              if(logger.isDebugEnabled()) {
                long timeTaken = System.currentTimeMillis() - start;
                logger.debug("Transformed message for profile [" + messageProfile + "]. Time taken: "
                    + timeTaken + ".  Message in:\n[" + payload.toString()+ "].  \nMessage out:\n[" + result.toString() + "].");
              }

                setTransformationOutput(message, result.toString(), executionContext);
            } else {
              logger.warn("Only java.lang.String payload types supported.  Input message was of type [" + payload.getClass().getName() + "].  Returning message untransformed.");
          }
           
        } catch(Throwable thrown) {
        throw new ActionProcessingException("Message transformation failed.", thrown);
      }
       
        // TODO: Cater for more message input types e.g. InputStream, DOM Document...
        // TODO: Cater for more message output types e.g. InputStream, DOM Document...
     
      return message;
    }

    private void setTransformationOutput(Message message, String transformedMessage, ExecutionContext executionContext) throws ActionProcessingException {
        // Set the transformation text output...
        try {
            payloadProxy.setPayload(message, transformedMessage);
        } catch (MessageDeliverException e) {
            throw new ActionProcessingException(e);
        }

        // Set the transformation Java output.  Will be the individual
        // java objects directly on the message and (optionally) the map itself...
        Map beanMap = executionContext.getBeanContext().getBeanMap();
        if(beanMap != null) {
            Iterator<Map.Entry> beans = beanMap.entrySet().iterator();
            while (beans.hasNext()) {
                Map.Entry entry = beans.next();
                String key = (String) entry.getKey();
                Object value = entry.getValue();

                if(value != null) {
                  if(message.getBody().get(key) != null) {
                      logger.debug("Outputting Java object to '" + key + "'.  Overwritting existing value.");
                  }
                  message.getBody().add(key, value);
                }
            }
        }

        // Now the map itself, if configured for output....
        if(javaOutputLocation != null) {
            if(beanMap != null) {
                String location = javaOutputLocation;
                if(location.equals("$default")) {
                    location = Body.DEFAULT_LOCATION;
                }
                if(message.getBody().get(location) != null) {
                    logger.debug("Outputting Java object Map to '" + location + "'.  Overwritting existing value.");
                }
                message.getBody().add(location, beanMap);
            } else {
                logger.debug("Transformation Javabean spec '" + javaOutputLocation + "' doesn't evaluate to any bean map for the current message.");
            }
        }
    }

    /**
   * Register the Message Exchange as a profile within Smooks.
   * @param message The message.
   * @param smooks The Smooks instance.
     * @return The Smooks "profile" string that uniquely identifies the message flow associated
   * with the message.
   * @throws ActionProcessingException Failed to register the message flow for the message.
   */
  private String registerMessageProfile(Message message, Smooks smooks) throws ActionProcessingException {
    String messageProfile;
      String messageFromType;
        String messageFrom;
        String messageToType;
        String messageTo;

        // Get the routing info from the message...
        messageFrom = (String)message.getProperties().getProperty(FROM, defaultMessageFrom);
        messageTo = (String)message.getProperties().getProperty(TO, defaultMessageTo);

        // Get the message typing info from the message...
    messageFromType = (String)message.getProperties().getProperty(FROM_TYPE, defaultMessageFromType);
    messageToType = (String)message.getProperties().getProperty(TO_TYPE, defaultMessageToType);

    // Construct the message profile string for use with Smooks.  This is basically the
    // name of the Message Exchange on which transformations are to be performed...
        messageProfile = getMessageProfileString(messageFromType, messageFrom, messageToType, messageTo);

        // If this transformation instance requires profiling, make sure the profile is registered on the
        // Smooks instance.
        if(messageProfile != null) {
            // Register this message flow if it isn't already registered...
            try {
                ProfileStore profileStore = smooks.getApplicationContext().getProfileStore();
                profileStore.getProfileSet(messageProfile);
            } catch(UnknownProfileMemberException e) {
                String[] profiles = getMessageProfiles(messageFromType, messageFrom, messageToType, messageTo);

                synchronized (SmooksTransformer.class) {
                    // Register the message flow within the Smooks context....
                    logger.info("Registering JBoss ESB Message-Exchange as Smooks Profile: [" + messageProfile + "].  Profiles: [" + Arrays.asList(profiles) + "]");
                    SmooksUtil.registerProfileSet(DefaultProfileSet.create( messageProfile, profiles ), smooks);
                }
            }
        }

        return messageProfile;
  }

    /**
     * Get the profile list based on the supplied message flow properties.
   * @param messageFromType The type string for the message source.
   * @param messageFrom The Message Exchange Participant name for the message source.
   * @param messageToType The type string for the message target.
   * @param messageTo The Message Exchange Participant name for the message target.
   * @return The list of profiles.
   */
  protected static String[] getMessageProfiles(String messageFromType, String messageFrom, String messageToType, String messageTo) {
    List<String> profiles = new ArrayList<String>();
    String[] profileArray;

    if(messageFromType != null) {
      profiles.add(FROM_TYPE + ":" + messageFromType);
    }
    if(messageFrom != null) {
      profiles.add(FROM + ":" + messageFrom);
    }
    if(messageToType != null) {
      profiles.add(TO_TYPE + ":" + messageToType);
    }
    if(messageTo != null) {
      profiles.add(TO + ":" + messageTo);
    }

    profileArray = new String[profiles.size()];
    profiles.toArray(profileArray);

    return profileArray;
  }

    /**
   * Construct the Smooks profile string based on the supplied message flow properties.
   * @param messageFromType The type string for the message source.
   * @param messageFrom The EPR string for the message source.
   * @param messageToType The type string for the message target.
   * @param messageTo The EPR srting for the message target.
   * @return Smooks profile string for the message flow.
   */
  protected static String getMessageProfileString(String messageFromType, String messageFrom, String messageToType, String messageTo) {
    StringBuffer string = new StringBuffer();

    if(messageFromType != null) {
      string.append(FROM_TYPE + ":" + messageFromType);
      string.append((messageFrom!=null || messageToType!=null || messageTo!=null?":":""));
    }
    if(messageFrom != null) {
      string.append(FROM + ":" + messageFrom);
      string.append((messageToType!=null || messageTo!=null?":":""));
    }
    if(messageToType != null) {
      string.append(TO_TYPE + ":" + messageToType);
      string.append((messageTo!=null?":":""));
    }
    if(messageTo != null) {
      string.append(TO + ":" + messageTo);
    }

        if(string.length() == 0) {
            return null;
        }

        return string.toString();
  }

    public void processException(final Message message, final Throwable th) {
    }

    public void processSuccess(final Message message) {
    }
}
TOP

Related Classes of org.jboss.soa.esb.actions.converters.SmooksTransformer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.