blob: ab7a591a8dc5f9333e7724e09814cf46f8753d2f [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.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import org.w3c.dom.Node;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
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.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
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.soap.server.SoapEndpointMapping;
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, CamelSpringWSEndpointMapping, SoapEndpointMapping, CamelContextAware {
private static final String DOUBLE_QUOTE = "\"";
private static final String URI_PATH_WILDCARD = "*";
private Map<EndpointMappingKey, MessageEndpoint> endpoints = new ConcurrentHashMap<>();
private TransformerFactory transformerFactory;
private CamelContext camelContext;
private String[] actorsOrRoles;
private boolean isUltimateReceiver = true;
@Override
public CamelContext getCamelContext() {
return camelContext;
}
@Override
public void setCamelContext(CamelContext camelContext) {
this.camelContext = camelContext;
}
@Override
protected Object getEndpointInternal(MessageContext messageContext) throws Exception {
for (EndpointMappingKey key : endpoints.keySet()) {
String messageKey;
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;
case URI_PATH:
messageKey = getUriPath();
if (messageKey != null && key.getLookupKey().endsWith(URI_PATH_WILDCARD)) {
String lookupKey = key.getLookupKey().substring(0, key.getLookupKey().length() - 1);
if (messageKey.startsWith(lookupKey)) {
return endpoints.get(key);
}
}
break;
default:
throw new RuntimeCamelException("Invalid mapping type specified. Supported types are: root QName, SOAP action, XPath expression and URI");
}
if (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 (key.getLookupKey().equals(messageKey)) {
return new SoapEndpointInvocationChain(endpoint, interceptors, actorsOrRoles, isUltimateReceiver);
}
}
}
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 {
WebServiceConnection webServiceConnection = getWeServiceConnection();
if (webServiceConnection != null) {
return webServiceConnection.getUri().toString();
}
return null;
}
private String getUriPath() throws URISyntaxException {
WebServiceConnection webServiceConnection = getWeServiceConnection();
if (webServiceConnection != null) {
return webServiceConnection.getUri().getPath();
}
return null;
}
private WebServiceConnection getWeServiceConnection() {
TransportContext transportContext = TransportContextHolder.getTransportContext();
if (transportContext != null) {
WebServiceConnection webServiceConnection = transportContext.getConnection();
if (webServiceConnection != null) {
return webServiceConnection;
}
}
return null;
}
private String getRootQName(MessageContext messageContext) throws TransformerException {
QName qName = PayloadRootUtils.getPayloadRootQName(messageContext.getRequest().getPayloadSource(), transformerFactory);
return qName != null ? qName.toString() : null;
}
private String getXPathResult(MessageContext messageContext, XPathExpression expression) {
if (expression != null) {
Node domNode = camelContext.getTypeConverter().convertTo(Node.class, messageContext.getRequest().getPayloadSource());
if (domNode != null) {
return expression.evaluateAsString(domNode.getFirstChild());
}
}
return null;
}
/**
* Used by Camel Spring Web Services endpoint to register consumers
*/
@Override
public void addConsumer(EndpointMappingKey key, MessageEndpoint endpoint) {
endpoints.put(key, endpoint);
}
/**
* Used by Camel Spring Web Services endpoint to unregister consumers
*/
@Override
public void removeConsumer(Object key) {
endpoints.remove(key);
}
/**
* Gets the configured TransformerFactory
*/
public TransformerFactory getTransformerFactory() {
return transformerFactory;
}
/**
* Optional setter to override default TransformerFactory
*/
public void setTransformerFactory(TransformerFactory transformerFactory) {
this.transformerFactory = transformerFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
if (transformerFactory == null) {
transformerFactory = TransformerFactory.newInstance();
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
}
}
@Override
public final void setActorOrRole(String actorOrRole) {
Assert.notNull(actorOrRole, "actorOrRole must not be null");
actorsOrRoles = new String[] {actorOrRole};
}
@Override
public final void setActorsOrRoles(String[] actorsOrRoles) {
Assert.notEmpty(actorsOrRoles, "actorsOrRoles must not be empty");
this.actorsOrRoles = actorsOrRoles;
}
@Override
public final void setUltimateReceiver(boolean ultimateReceiver) {
this.isUltimateReceiver = ultimateReceiver;
}
}