blob: fa6aa4ce84156652097f866ad3dd1f3535b1fa7f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.servicemix.expression;
import java.io.IOException;
import javax.jbi.messaging.MessageExchange;
import javax.jbi.messaging.MessagingException;
import javax.jbi.messaging.NormalizedMessage;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.servicemix.jbi.jaxp.SourceTransformer;
import org.apache.servicemix.jbi.jaxp.StringSource;
import org.jaxen.FunctionContext;
import org.jaxen.JaxenException;
import org.jaxen.NamespaceContext;
import org.jaxen.XPath;
import org.jaxen.dom.DOMXPath;
import org.springframework.beans.factory.InitializingBean;
/**
* Evalutes an XPath expression on the given message using <a
* href="http://jaxen.org/"/>Jaxen</a>
*
* @version $Revision: 564900 $
*/
public class JaxenXPathExpression implements Expression, InitializingBean {
private static final transient Log LOG = LogFactory.getLog(JaxenXPathExpression.class);
private String xpath;
private boolean useMessageContent = true;
private SourceTransformer transformer = new SourceTransformer();
private JaxenVariableContext variableContext = new JaxenVariableContext();
private XPath xpathObject;
private NamespaceContext namespaceContext;
private FunctionContext functionContext;
public JaxenXPathExpression() {
}
/**
* A helper constructor to make a fully created expression. This constructor
* will call the {@link #afterPropertiesSet()} method to ensure this POJO is
* properly constructed.
*/
public JaxenXPathExpression(String xpath) throws Exception {
this.xpath = xpath;
init();
}
public void afterPropertiesSet() throws Exception {
init();
}
private void init() throws JaxenException {
if (xpathObject == null) {
if (xpath == null) {
throw new IllegalArgumentException("You must specify the xpath property");
}
xpathObject = createXPath(xpath);
xpathObject.setVariableContext(variableContext);
if (namespaceContext != null) {
xpathObject.setNamespaceContext(namespaceContext);
}
if (functionContext != null) {
xpathObject.setFunctionContext(functionContext);
}
}
}
public Object evaluate(MessageExchange exchange, NormalizedMessage message) throws MessagingException {
try {
Object object = getXMLNode(exchange, message);
if (object == null) {
return null;
}
synchronized (this) {
variableContext.setExchange(exchange);
variableContext.setMessage(message);
return evaluateXPath(object);
}
} catch (TransformerException e) {
throw new MessagingException(e);
} catch (JaxenException e) {
throw new MessagingException(e);
} catch (ParserConfigurationException e) {
throw new MessagingException(e);
} catch (IOException e) {
throw new MessagingException(e);
} catch (SAXException e) {
throw new MessagingException(e);
}
}
public boolean matches(MessageExchange exchange, NormalizedMessage message) throws MessagingException {
try {
Object object = getXMLNode(exchange, message);
if (object == null) {
return false;
}
synchronized (this) {
variableContext.setExchange(exchange);
variableContext.setMessage(message);
return evaluateXPathAsBoolean(object);
}
} catch (TransformerException e) {
throw new MessagingException(e);
} catch (JaxenException e) {
throw new MessagingException(e);
} catch (ParserConfigurationException e) {
throw new MessagingException(e);
} catch (IOException e) {
throw new MessagingException(e);
} catch (SAXException e) {
throw new MessagingException(e);
}
}
// Properties
// -------------------------------------------------------------------------
public XPath getXpathObject() {
return xpathObject;
}
public void setXpathObject(XPath xpathObject) {
this.xpathObject = xpathObject;
}
public String getXpath() {
return xpath;
}
public void setXpath(String xpath) {
this.xpath = xpath;
}
public boolean isUseMessageContent() {
return useMessageContent;
}
/**
* Specifies whether or not the XPath expression uses the message content.
*
* By default, this property is <code>true</code>, but you can set it to
* <code>false</code> to avoid that the message content is converted to
* {@link StringSource}
*
* @param useMessageContent
* specify <code>false</code> if this expression does not
* access the message content
*/
public void setUseMessageContent(boolean useMessageContent) {
this.useMessageContent = useMessageContent;
}
public SourceTransformer getTransformer() {
return transformer;
}
public void setTransformer(SourceTransformer transformer) {
this.transformer = transformer;
}
public JaxenVariableContext getVariableContext() {
return variableContext;
}
public void setVariableContext(JaxenVariableContext variableContext) {
this.variableContext = variableContext;
}
public NamespaceContext getNamespaceContext() {
return namespaceContext;
}
public void setNamespaceContext(NamespaceContext namespaceContext) {
this.namespaceContext = namespaceContext;
}
public FunctionContext getFunctionContext() {
return functionContext;
}
public void setFunctionContext(FunctionContext functionContext) {
this.functionContext = functionContext;
}
// Implementation methods
// -------------------------------------------------------------------------
protected final XPath createXPath(String xp) throws JaxenException {
return new DOMXPath(xp);
}
protected Object evaluateXPath(Object object) throws JaxenException {
return xpathObject.evaluate(object);
}
protected boolean evaluateXPathAsBoolean(Object object) throws JaxenException {
return xpathObject.booleanValueOf(object);
}
protected Object getXMLNode(MessageExchange exchange, NormalizedMessage message) throws TransformerException, MessagingException,
ParserConfigurationException, IOException, SAXException {
Node node = null;
// ensure re-readability of the content if the expression also needs to
// access the content
if (useMessageContent) {
enableContentRereadability(message);
}
if (message != null) {
node = transformer.toDOMNode(message);
} else {
LOG.warn("Null message for exchange: " + exchange);
}
if (node == null) {
// lets make an empty document to avoid Jaxen throwing a
// NullPointerException
node = transformer.createDocument();
}
return node;
}
/**
* Convert the given {@link NormalizedMessage} instance's content to a re-readable {@link javax.xml.transform.Source} This allows the
* content to be read more than once (e.g. for XPath evaluation or auditing).
*
* @param message
* the {@link NormalizedMessage} to convert the content for
* @throws MessagingException
*/
public void enableContentRereadability(NormalizedMessage message) throws MessagingException {
if (message.getContent() instanceof StreamSource) {
try {
String content = transformer.contentToString(message);
if (content != null) {
message.setContent(new StringSource(content));
}
} catch (TransformerException e) {
throw new MessagingException("Unable to convert message content into StringSource", e);
} catch (ParserConfigurationException e) {
throw new MessagingException("Unable to convert message content into StringSource", e);
} catch (IOException e) {
throw new MessagingException("Unable to convert message content into StringSource", e);
} catch (SAXException e) {
throw new MessagingException("Unable to convert message content into StringSource", e);
}
}
}
}