001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.soap;
018    
019    import java.net.URI;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    
026    import javax.jbi.JBIException;
027    import javax.jbi.component.ComponentContext;
028    import javax.jbi.messaging.DeliveryChannel;
029    import javax.jbi.messaging.MessageExchange;
030    import javax.jbi.messaging.MessageExchangeFactory;
031    import javax.jbi.messaging.MessagingException;
032    import javax.jbi.messaging.NormalizedMessage;
033    import javax.jbi.servicedesc.ServiceEndpoint;
034    import javax.wsdl.Binding;
035    import javax.wsdl.Definition;
036    import javax.wsdl.Operation;
037    import javax.wsdl.Part;
038    import javax.wsdl.Port;
039    import javax.wsdl.PortType;
040    import javax.wsdl.Service;
041    import javax.wsdl.WSDLException;
042    import javax.wsdl.factory.WSDLFactory;
043    import javax.wsdl.xml.WSDLReader;
044    import javax.xml.namespace.QName;
045    
046    import org.apache.commons.logging.Log;
047    import org.apache.commons.logging.LogFactory;
048    import org.apache.servicemix.common.JbiConstants;
049    import org.apache.servicemix.jbi.jaxp.W3CDOMStreamWriter;
050    import org.apache.servicemix.soap.marshalers.JBIMarshaler;
051    import org.apache.servicemix.soap.marshalers.SoapMarshaler;
052    import org.apache.servicemix.soap.marshalers.SoapMessage;
053    import org.apache.servicemix.soap.marshalers.SoapWriter;
054    
055    import org.w3c.dom.Document;
056    
057    import com.ibm.wsdl.Constants;
058    
059    /**
060     * Helper class for working with soap endpoints
061     * 
062     * @author Guillaume Nodet
063     * @version $Revision: 1.5 $
064     * @since 3.0
065     */
066    public class SoapHelper {
067    
068        private static final Log logger = LogFactory.getLog(SoapHelper.class);
069    
070        private SoapEndpoint endpoint;
071        private List policies;
072        private JBIMarshaler jbiMarshaler;
073        private SoapMarshaler soapMarshaler;
074        private Map definitions;
075        private Map operationNames;
076    
077        public SoapHelper(SoapEndpoint endpoint) {
078            this.policies = endpoint.getPolicies();
079            if (this.policies == null) {
080                this.policies = Collections.EMPTY_LIST;
081            }
082            this.definitions = new HashMap();
083            this.operationNames = new HashMap();
084            this.jbiMarshaler = new JBIMarshaler();
085            this.endpoint = endpoint;
086            boolean requireDom = false;
087            for (Iterator iter = policies.iterator(); iter.hasNext();) {
088                Handler handler = (Handler) iter.next();
089                requireDom |= handler.requireDOM();
090            }
091            this.soapMarshaler = new SoapMarshaler(endpoint.isSoap(), requireDom);
092            if (endpoint.isSoap() && "1.1".equals(endpoint.getSoapVersion())) {
093                this.soapMarshaler.setSoapUri(SoapMarshaler.SOAP_11_URI);
094            }
095        }
096        
097        public SoapMarshaler getSoapMarshaler() {
098            return this.soapMarshaler;
099        }
100        
101        public JBIMarshaler getJBIMarshaler() {
102            return this.jbiMarshaler;
103        }
104    
105        public MessageExchange onReceive(Context context) throws Exception {
106            if (policies != null) {
107                for (Iterator it = policies.iterator(); it.hasNext();) {
108                    Handler policy = (Handler) it.next();
109                    policy.onReceive(context);
110                }
111            }
112    
113            // If WS-A has not set informations, use the default ones
114            if (context.getProperty(Context.SERVICE) == null && context.getProperty(Context.INTERFACE) == null) {
115                // If no target endpoint / service / interface is defined
116                // we assume we use the same informations has defined on the
117                // external endpoint
118                if (endpoint.getTargetInterfaceName() == null && endpoint.getTargetService() == null
119                                && endpoint.getTargetEndpoint() == null) {
120                    context.setProperty(Context.INTERFACE, endpoint.getInterfaceName());
121                    context.setProperty(Context.SERVICE, endpoint.getService());
122                    context.setProperty(Context.ENDPOINT, endpoint.getEndpoint());
123                } else {
124                    context.setProperty(Context.INTERFACE, endpoint.getTargetInterfaceName());
125                    context.setProperty(Context.SERVICE, endpoint.getTargetService());
126                    context.setProperty(Context.ENDPOINT, endpoint.getTargetEndpoint());
127                }
128            }
129            Operation operation = findOperation(context);
130            if (context.getProperty(Context.OPERATION) == null) {
131                if (operation != null) {
132                    // the operation QName must be retrieved from the map
133                    // so that we can have the right namespace
134                    context.setProperty(Context.OPERATION, operationNames.get(operation));
135                } else if (endpoint.getDefaultOperation() != null) {
136                    context.setProperty(Context.OPERATION, endpoint.getDefaultOperation());
137                } else {
138                    // By default, use name of body element (i.e., RPC-style)
139                    QName bodyName = context.getInMessage().getBodyName();
140                    context.setProperty(Context.OPERATION, bodyName);
141                }
142            }
143            URI mep = null; 
144            if ( operation != null ) {
145                mep = getMep(operation);
146            }
147            if (mep == null) {
148                mep = endpoint.getDefaultMep();
149            }
150            MessageExchange exchange = createExchange(mep);
151            exchange.setService((QName) context.getProperty(Context.SERVICE));
152            exchange.setInterfaceName((QName) context.getProperty(Context.INTERFACE));
153            exchange.setOperation((QName) context.getProperty(Context.OPERATION));
154            if (context.getProperty(Context.ENDPOINT) != null) {
155                ComponentContext componentContext = endpoint.getServiceUnit().getComponent().getComponentContext();
156                QName serviceName = (QName) context.getProperty(Context.SERVICE);
157                String endpointName = (String) context.getProperty(Context.ENDPOINT);
158                ServiceEndpoint se = componentContext.getEndpoint(serviceName, endpointName);
159                if (se != null) {
160                    exchange.setEndpoint(se);
161                }
162            }
163            NormalizedMessage inMessage = exchange.createMessage();
164            jbiMarshaler.toNMS(inMessage, context.getInMessage());
165            exchange.setMessage(inMessage, "in");
166            return exchange;
167        }
168    
169        public SoapMessage onReply(Context context, NormalizedMessage outMsg) throws Exception {
170            SoapMessage out = new SoapMessage();
171            if (context.getInMessage() != null) {
172                out.setEnvelopeName(context.getInMessage().getEnvelopeName());
173            }
174            jbiMarshaler.fromNMS(out, outMsg);
175            context.setOutMessage(out);
176            if (policies != null) {
177                for (Iterator it = policies.iterator(); it.hasNext();) {
178                    Handler policy = (Handler) it.next();
179                    policy.onReply(context);
180                }
181            }
182            return out;
183        }
184    
185        public SoapMessage onFault(Context context, SoapFault fault) throws Exception {
186            SoapMessage soapFault = new SoapMessage();
187            soapFault.setFault(fault);
188            if (context == null) {
189                context = new Context();
190            }
191            if (context.getInMessage() != null) {
192                soapFault.setEnvelopeName(context.getInMessage().getEnvelopeName());
193            }
194            context.setFaultMessage(soapFault);
195            if (policies != null) {
196                for (Iterator it = policies.iterator(); it.hasNext();) {
197                    Handler policy = (Handler) it.next();
198                    policy.onFault(context);
199                }
200            }
201            return soapFault;
202        }
203    
204        public void onSend(Context context) throws Exception {
205            if (policies != null) {
206                for (Iterator it = policies.iterator(); it.hasNext();) {
207                    Handler policy = (Handler) it.next();
208                    if (policy.requireDOM()) {
209                        SoapWriter writer = soapMarshaler.createWriter(context.getInMessage());
210                        W3CDOMStreamWriter domWriter = new W3CDOMStreamWriter(); 
211                        writer.writeSoapEnvelope(domWriter);
212                        context.getInMessage().setDocument(domWriter.getDocument());
213                    }
214                    policy.onSend(context);
215                }
216            }
217        }
218    
219        public void onAnswer(Context context) throws Exception {
220            if (policies != null) {
221                for (Iterator it = policies.iterator(); it.hasNext();) {
222                    Handler policy = (Handler) it.next();
223                    policy.onAnswer(context);
224                }
225            }
226        }
227    
228        public Context createContext(SoapMessage message) {
229            Context context = createContext();
230            context.setInMessage(message);
231            return context;
232        }
233        
234        public Context createContext() {
235            Context context = new Context();
236            context.setProperty(Context.AUTHENTICATION_SERVICE, endpoint.getAuthenticationService());
237            context.setProperty(Context.KEYSTORE_MANAGER, endpoint.getKeystoreManager());
238            return context;
239        }
240    
241        protected MessageExchange createExchange(URI mep) throws MessagingException {
242            ComponentContext context = endpoint.getServiceUnit().getComponent().getComponentContext();
243            DeliveryChannel channel = context.getDeliveryChannel();
244            MessageExchangeFactory factory = channel.createExchangeFactory();
245            MessageExchange exchange = factory.createExchange(mep);
246            return exchange;
247        }
248    
249        private URI getMep(Operation oper) {
250            URI mep = null;
251            if (oper != null) {
252                boolean output = oper.getOutput() != null && oper.getOutput().getMessage() != null
253                                && oper.getOutput().getMessage().getParts().size() > 0;
254                boolean faults = oper.getFaults().size() > 0;
255                if (output) {
256                    mep = JbiConstants.IN_OUT;
257                } else if (faults) {
258                    mep = JbiConstants.ROBUST_IN_ONLY;
259                } else {
260                    mep = JbiConstants.IN_ONLY;
261                }
262            }
263            return mep;
264        }        
265    
266        protected Operation findOperation(Context context) throws Exception {
267            QName interfaceName = (QName) context.getProperty(Context.INTERFACE);
268            QName serviceName = (QName) context.getProperty(Context.SERVICE);
269            String endpointName = (String) context.getProperty(Context.ENDPOINT);
270            ComponentContext componentContext = endpoint.getServiceUnit().getComponent().getComponentContext();
271            QName bodyName = context.getInMessage().getBodyName();
272            
273            // Find target endpoint
274            ServiceEndpoint se = null;
275            if (serviceName != null && endpointName != null) {
276                se = componentContext.getEndpoint(serviceName, endpointName);
277            }
278            if (se == null && interfaceName != null) {
279                ServiceEndpoint[] ses = componentContext.getEndpoints(interfaceName);
280                if (ses != null && ses.length > 0) {
281                    se = ses[0];
282                }
283            }
284            
285            // Find WSDL description
286            Definition definition = null;
287            if (se != null) {
288                // Find endpoint description from the component context
289                definition = getDefinition(se);
290            }
291            if (definition == null) {
292                // Get this endpoint definition
293                definition = endpoint.getDefinition();
294            }
295    
296            // Find operation matching 
297            if (definition != null) {
298                if (interfaceName != null) {
299                    PortType portType = definition.getPortType(interfaceName);
300                    if (portType != null) {
301                        return findOperationFor(portType, bodyName);
302                    }
303                } else if (definition.getService(serviceName) != null) {
304                    Service service = definition.getService(serviceName);
305                    if (endpointName != null) {
306                        Port port = service.getPort(endpointName);
307                        if (port != null) {
308                            Binding binding = port.getBinding();
309                            if (binding != null) {
310                                PortType portType = binding.getPortType();
311                                if (portType != null) {
312                                    return findOperationFor(portType, bodyName);
313                                }
314                            }
315                        }
316                    } else if (service.getPorts().size() == 1) {
317                        Port port = (Port) service.getPorts().values().iterator().next();
318                        Binding binding = port.getBinding();
319                        if (binding != null) {
320                            PortType portType = binding.getPortType();
321                            if (portType != null) {
322                                return findOperationFor(portType, bodyName);
323                            }
324                        }
325                    }
326                } else if (definition.getPortTypes().size() == 1) {
327                    PortType portType = (PortType) definition.getPortTypes().values().iterator().next();
328                    return findOperationFor(portType, bodyName);
329                }
330            }
331            return null;
332        }
333        
334        protected Operation findOperationFor(PortType portType, QName bodyName) {
335            List list = portType.getOperations();
336            for (int i = 0; i < list.size(); i++) {
337                Operation operation = (Operation) list.get(i);
338                if (operation.getInput() != null && operation.getInput().getMessage() != null) {
339                    Map parts = operation.getInput().getMessage().getParts();
340                    Iterator iter = parts.values().iterator();
341                    while (iter.hasNext()) {
342                        Part part = (Part) iter.next();
343                        QName elementName = part.getElementName();
344                        if (elementName != null && elementName.equals(bodyName)) {
345                            // found
346                            operationNames.put(operation, new QName(portType.getQName().getNamespaceURI(), operation.getName()));
347                            return operation;
348                        }
349                    }
350                }
351            }
352            return null;
353        }
354    
355        protected Definition getDefinition(ServiceEndpoint se) throws WSDLException, JBIException {
356            Definition definition;
357            ComponentContext componentContext = endpoint.getServiceUnit().getComponent().getComponentContext();
358                String key = se.getServiceName() + se.getEndpointName();
359                synchronized (definitions) {
360                    definition = (Definition) definitions.get(key);
361                    if (definition == null) {
362                        WSDLFactory factory = WSDLFactory.newInstance();
363                        Document description = componentContext.getEndpointDescriptor(se);
364                        if (description != null) {
365                            // Parse WSDL
366                            WSDLReader reader = factory.newWSDLReader(); 
367                            reader.setFeature(Constants.FEATURE_VERBOSE, false);
368                            try {
369                                definition = reader.readWSDL(null, description);
370                                definitions.put(key, definition);
371                            } catch (WSDLException e) {
372                                logger.info("Could not read wsdl from endpoint descriptor: " + e.getMessage());
373                                if (logger.isDebugEnabled()) {
374                                    logger.debug("Could not read wsdl from endpoint descriptor", e);
375                                }
376                            }
377                        }
378                    }
379                }
380            return definition;
381        }
382    
383    }