blob: 3e079d43a9a0583c967b2fe41662f9875a9c1ac4 [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.camel.component.spring.ws.bean;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.component.spring.ws.type.EndpointMappingKey;
import org.apache.camel.component.spring.ws.type.EndpointMappingType;
import org.apache.camel.converter.jaxp.XmlConverter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.EndpointInvocationChain;
import org.springframework.ws.server.EndpointMapping;
import org.springframework.ws.server.endpoint.MessageEndpoint;
import org.springframework.ws.server.endpoint.mapping.AbstractEndpointMapping;
import org.springframework.ws.server.endpoint.support.PayloadRootUtils;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.server.SoapEndpointInvocationChain;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.context.TransportContext;
import org.springframework.ws.transport.context.TransportContextHolder;
import org.springframework.xml.xpath.XPathExpression;
/**
* Spring {@link EndpointMapping} for mapping messages to corresponding Camel endpoints.
* This class needs to be registered in the Spring <tt>ApplicationContext</tt> when
* consuming messages using any of the following URI schemes:
* <p/>
* <ul>
* <li><tt>spring-ws:rootqname:</tt><br/>
* Equivalent to endpoint mappings specified through {@link org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping}
* <p/>
* <li><tt>spring-ws:soapaction:</tt><br/>
* Equivalent to endpoint mappings specified through {@link org.springframework.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping}
* <p/>
* <li><tt>spring-ws:uri:</tt><br/>
* Equivalent to endpoint mappings specified through {@link org.springframework.ws.server.endpoint.mapping.UriEndpointMapping}
* <p/>
* <li><tt>spring-ws:xpathresult:</tt><br/>
* Equivalent to endpoint mappings specified through {@link org.springframework.ws.server.endpoint.mapping.XPathPayloadEndpointMapping}
* </ul>
*
* @see org.springframework.ws.server.endpoint.mapping.AbstractEndpointMapping
* @see org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping
* @see org.springframework.ws.server.endpoint.mapping.UriEndpointMapping
* @see org.springframework.ws.server.endpoint.mapping.XPathPayloadEndpointMapping
* @see org.springframework.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping
*/
public class CamelEndpointMapping extends AbstractEndpointMapping implements InitializingBean {
private static final String DOUBLE_QUOTE = "\"";
private Map<EndpointMappingKey, MessageEndpoint> endpoints = new ConcurrentHashMap<EndpointMappingKey, MessageEndpoint>();
private TransformerFactory transformerFactory;
private XmlConverter xmlConverter;
@Override
protected Object getEndpointInternal(MessageContext messageContext) throws Exception {
for (EndpointMappingKey key : endpoints.keySet()) {
Object messageKey = null;
switch (key.getType()) {
case ROOT_QNAME:
messageKey = getRootQName(messageContext);
break;
case SOAP_ACTION:
messageKey = getSoapAction(messageContext);
break;
case XPATHRESULT:
messageKey = getXPathResult(messageContext, key.getExpression());
break;
case URI:
messageKey = getUri();
break;
default:
throw new RuntimeCamelException("Invalid mapping type specified. Supported types are: root QName, SOAP action, XPath expression and URI");
}
if (messageKey != null && key.getLookupKey().equals(messageKey)) {
return endpoints.get(key);
}
}
return null;
}
@Override
protected final EndpointInvocationChain createEndpointInvocationChain(MessageContext messageContext, Object endpoint, EndpointInterceptor[] interceptors) {
for (EndpointMappingKey key : endpoints.keySet()) {
if (EndpointMappingType.SOAP_ACTION.equals(key.getType())) {
Object messageKey = getSoapAction(messageContext);
if (messageKey != null && key.getLookupKey().equals(messageKey)) {
return new SoapEndpointInvocationChain(endpoint, interceptors);
// possibly add support for SOAP actors/roles and ultimate
// receiver in the future
}
}
}
return super.createEndpointInvocationChain(messageContext, endpoint, interceptors);
}
private String getSoapAction(MessageContext messageContext) {
if (messageContext.getRequest() instanceof SoapMessage) {
SoapMessage request = (SoapMessage) messageContext.getRequest();
String soapAction = request.getSoapAction();
if (StringUtils.hasLength(soapAction) && soapAction.startsWith(DOUBLE_QUOTE) && soapAction.endsWith(DOUBLE_QUOTE)) {
return soapAction.substring(1, soapAction.length() - 1);
}
return soapAction;
}
return null;
}
private String getUri() throws URISyntaxException {
TransportContext transportContext = TransportContextHolder.getTransportContext();
if (transportContext != null) {
WebServiceConnection webServiceConnection = transportContext.getConnection();
if (webServiceConnection != null) {
return webServiceConnection.getUri().toString();
}
}
return null;
}
private String getRootQName(MessageContext messageContext) throws TransformerException, XMLStreamException {
QName qName = PayloadRootUtils.getPayloadRootQName(messageContext.getRequest().getPayloadSource(), transformerFactory);
return qName != null ? qName.toString() : null;
}
private String getXPathResult(MessageContext messageContext, XPathExpression expression) throws TransformerException, XMLStreamException, ParserConfigurationException, IOException, SAXException {
if (expression != null) {
Node domNode = xmlConverter.toDOMNode(messageContext.getRequest().getPayloadSource());
if (domNode != null) {
return expression.evaluateAsString(domNode.getFirstChild());
}
}
return null;
}
/**
* Used by Camel Spring Web Services endpoint to register consumers
*
* @param key unique consumer key
* @param endpoint consumer
*/
public void addConsumer(EndpointMappingKey key, MessageEndpoint endpoint) {
endpoints.put(key, endpoint);
}
/**
* Used by Camel Spring Web Services endpoint to unregister consumers
*
* @param key unique consumer key
*/
public void removeConsumer(Object key) {
endpoints.remove(key);
}
/**
* Gets the configured TransformerFactory
*
* @return instance of TransformerFactory
*/
public TransformerFactory getTransformerFactory() {
return transformerFactory;
}
/**
* Optional setter to override default TransformerFactory
*
* @param transformerFactory non-default TransformerFactory
*/
public void setTransformerFactory(TransformerFactory transformerFactory) {
this.transformerFactory = transformerFactory;
}
public void afterPropertiesSet() throws Exception {
xmlConverter = new XmlConverter();
if (transformerFactory != null) {
xmlConverter.setTransformerFactory(transformerFactory);
} else {
transformerFactory = TransformerFactory.newInstance();
}
}
}