/** * 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.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 { private static final String DOUBLE_QUOTE = "\""; private static final String URI_PATH_WILDCARD = "*"; private Map<EndpointMappingKey, MessageEndpoint> endpoints = new ConcurrentHashMap<EndpointMappingKey, MessageEndpoint>(); private TransformerFactory transformerFactory; private XmlConverter xmlConverter; private String[] actorsOrRoles; private boolean isUltimateReceiver = true; @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 (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, 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, 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(); } } /** * @see {@link AbstractAddressingEndpointMapping} * @param actorOrRole */ public final void setActorOrRole(String actorOrRole) { Assert.notNull(actorOrRole, "actorOrRole must not be null"); actorsOrRoles = new String[] {actorOrRole}; } /** * @see {@link AbstractAddressingEndpointMapping} * @param actorsOrRoles */ public final void setActorsOrRoles(String[] actorsOrRoles) { Assert.notEmpty(actorsOrRoles, "actorsOrRoles must not be empty"); this.actorsOrRoles = actorsOrRoles; } /** * @see {@link AbstractAddressingEndpointMapping} * @param ultimateReceiver */ public final void setUltimateReceiver(boolean ultimateReceiver) { this.isUltimateReceiver = ultimateReceiver; } }