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.http.endpoints;
018    
019    import java.io.IOException;
020    import java.io.InputStream;
021    
022    import javax.jbi.management.DeploymentException;
023    import javax.jbi.servicedesc.ServiceEndpoint;
024    import javax.wsdl.Definition;
025    import javax.wsdl.Port;
026    import javax.wsdl.Service;
027    import javax.wsdl.WSDLException;
028    import javax.wsdl.extensions.soap.SOAPAddress;
029    import javax.wsdl.extensions.soap12.SOAP12Address;
030    import javax.xml.namespace.QName;
031    
032    import org.w3c.dom.Element;
033    
034    import org.apache.servicemix.common.DefaultComponent;
035    import org.apache.servicemix.common.ServiceUnit;
036    import org.apache.servicemix.soap.api.Policy;
037    import org.apache.servicemix.soap.util.DomUtil;
038    import org.apache.servicemix.soap.wsdl.BindingFactory;
039    import org.apache.servicemix.soap.wsdl.WSDLUtils;
040    import org.apache.servicemix.soap.wsdl.validator.WSIBPValidator;
041    import org.apache.woden.WSDLFactory;
042    import org.apache.woden.WSDLReader;
043    import org.apache.woden.types.NCName;
044    import org.apache.woden.wsdl20.Description;
045    import org.apache.woden.wsdl20.Endpoint;
046    import org.apache.woden.wsdl20.xml.DescriptionElement;
047    import org.springframework.core.io.Resource;
048    import org.xml.sax.InputSource;
049    
050    /**
051     * @author gnodet
052     * @since 3.2
053     * @org.apache.xbean.XBean element="soap-consumer" description=
054     *                         "an HTTP consumer endpoint that is optimized to work with SOAP messages"
055     */
056    public class HttpSoapConsumerEndpoint extends HttpConsumerEndpoint {
057    
058        private Resource wsdl;
059        private boolean useJbiWrapper = true;
060        private boolean validateWsdl = true;
061        private Policy[] policies;
062    
063        public HttpSoapConsumerEndpoint() {
064            super();
065        }
066    
067        public HttpSoapConsumerEndpoint(DefaultComponent component, ServiceEndpoint endpoint) {
068            super(component, endpoint);
069        }
070    
071        public HttpSoapConsumerEndpoint(ServiceUnit serviceUnit, QName service, String endpoint) {
072            super(serviceUnit, service, endpoint);
073        }
074    
075        public Resource getWsdl() {
076            return wsdl;
077        }
078    
079        /**
080         * Specifies the WSDL document defining the messages sent by the endpoint.
081         * 
082         * @param wsdl a <code>Resource</code> object that points to the WSDL document
083         * @org.apache.xbean.Property description="the URL of the WSDL document defining the endpoint's messages"
084         */
085        public void setWsdl(Resource wsdl) {
086            this.wsdl = wsdl;
087        }
088    
089        public boolean isValidateWsdl() {
090            return validateWsdl;
091        }
092    
093        /**
094         * Specifies in the WSDL is validated against the WS-I basic profile.
095         * 
096         * @param validateWsdl <code>true</code> if WSDL is to be validated
097         * @org.apache.xbean.Property description="Specifies if the WSDL is checked for WSI-BP compliance. Default is <code>true</code>."
098         */
099        public void setValidateWsdl(boolean validateWsdl) {
100            this.validateWsdl = validateWsdl;
101        }
102    
103        public boolean isUseJbiWrapper() {
104            return useJbiWrapper;
105        }
106    
107        /**
108         * Specifies if the SOAP messages are wrapped in a JBI wrapper.
109         * 
110         * @param useJbiWrapper <code>true</code> if the SOAP messages are wrapped
111         * @org.apache.xbean.Property description= "Specifies if the JBI wrapper is sent in the body of the message. Default is
112         *                            <code>true</code>."
113         */
114        public void setUseJbiWrapper(boolean useJbiWrapper) {
115            this.useJbiWrapper = useJbiWrapper;
116        }
117    
118        public Policy[] getPolicies() {
119            return policies;
120        }
121    
122        /**
123         * Specifies a list of interceptors that will process messages for the endpoint.
124         * 
125         * @param policies an array of <code>Policy</code> objects
126         * @org.apache.xbean.Property description="a list of interceptors that will process messages"
127         */
128        public void setPolicies(Policy[] policies) {
129            this.policies = policies;
130        }
131    
132        @Override
133        public void validate() throws DeploymentException {
134            if (wsdl == null) {
135                throw new DeploymentException("wsdl property must be set");
136            }
137            HttpSoapConsumerMarshaler marshaler;
138            if (getMarshaler() instanceof HttpSoapConsumerMarshaler) {
139                marshaler = (HttpSoapConsumerMarshaler)getMarshaler();
140            } else if (getMarshaler() == null) {
141                marshaler = new HttpSoapConsumerMarshaler();
142            } else {
143                throw new DeploymentException("The configured marshaler must inherit HttpSoapConsumerMarshaler");
144            }
145            try {
146                description = DomUtil.parse(wsdl.getInputStream());
147                Element elem = description.getDocumentElement();
148                if (WSDLUtils.WSDL1_NAMESPACE.equals(elem.getNamespaceURI())) {
149                    validateWsdl1(marshaler);
150                } else if (WSDLUtils.WSDL2_NAMESPACE.equals(elem.getNamespaceURI())) {
151                    validateWsdl2(marshaler);
152                } else {
153                    throw new DeploymentException("Unrecognized wsdl namespace: " + elem.getNamespaceURI());
154                }
155                marshaler.setUseJbiWrapper(useJbiWrapper);
156                marshaler.setPolicies(policies);
157                setMarshaler(marshaler);
158            } catch (DeploymentException e) {
159                throw e;
160            } catch (Exception e) {
161                throw new DeploymentException("Unable to read WSDL from: " + wsdl, e);
162            }
163            if (getTargetService() == null && getTargetInterface() == null && getTargetUri() == null) {
164                setTargetService(getService());
165                setTargetEndpoint(getEndpoint());
166            }
167            super.validate();
168        }
169    
170        protected void loadStaticResources() {
171            addResource(MAIN_WSDL, description);
172        }
173    
174        protected void validateWsdl1(HttpSoapConsumerMarshaler marshaler) throws Exception {
175            InputStream is = wsdl.getInputStream();
176            try {
177                InputSource inputSource = new InputSource(is);
178                inputSource.setSystemId(wsdl.getURL().toString());
179                Definition def = WSDLUtils.createWSDL11Reader().readWSDL(wsdl.getURL().toString(), inputSource);
180                if (validateWsdl) {
181                    WSIBPValidator validator = new WSIBPValidator(def);
182                    if (!validator.isValid()) {
183                        throw new DeploymentException("WSDL is not WS-I BP compliant: " + validator.getErrors());
184                    }
185                }
186                Service svc;
187                if (getService() != null) {
188                    svc = def.getService(getService());
189                    if (svc == null) {
190                        throw new DeploymentException("Could not find service '" + getService() + "' in wsdl");
191                    }
192                } else if (def.getServices().size() == 1) {
193                    svc = (Service)def.getServices().values().iterator().next();
194                    setService(svc.getQName());
195                } else {
196                    throw new DeploymentException(
197                                                  "If service is not set, the WSDL must contain a single service definition");
198                }
199                Port port;
200                if (getEndpoint() != null) {
201                    port = svc.getPort(getEndpoint());
202                    if (port == null) {
203                        throw new DeploymentException("Cound not find port '" + getEndpoint()
204                                                      + "' in wsdl for service '" + getService() + "'");
205                    }
206                } else if (svc.getPorts().size() == 1) {
207                    port = (Port)svc.getPorts().values().iterator().next();
208                    setEndpoint(port.getName());
209                } else {
210                    throw new DeploymentException("If endpoint is not set, the WSDL service '" + getService()
211                                                  + "' must contain a single port definition");
212                }
213                SOAPAddress soapAddress = WSDLUtils.getExtension(port, SOAPAddress.class);
214                if (soapAddress != null) {
215                    soapAddress.setLocationURI(getLocationURI());
216                } else {
217                    SOAP12Address soap12Address = WSDLUtils.getExtension(port, SOAP12Address.class);
218                    if (soap12Address != null) {
219                        soap12Address.setLocationURI(getLocationURI());
220                    }
221                }
222                description = WSDLUtils.getWSDL11Factory().newWSDLWriter().getDocument(def);
223                marshaler.setBinding(BindingFactory.createBinding(port));
224            } finally {
225                is.close();
226            }
227        }
228    
229        protected void validateWsdl2(HttpSoapConsumerMarshaler marshaler) throws Exception {
230            new Wsdl2Validator().validate(marshaler);
231        }
232    
233        /**
234         * Use an inner class to avoid having a strong dependency on Woden if not needed
235         */
236        protected class Wsdl2Validator {
237            public void validate(HttpSoapConsumerMarshaler marshaler) throws Exception {
238                WSDLReader reader = WSDLFactory.newInstance().newWSDLReader();
239                DescriptionElement descElement = reader.readWSDL(wsdl.getURL().toString());
240                Description desc = descElement.toComponent();
241                org.apache.woden.wsdl20.Service svc;
242                if (getService() != null) {
243                    svc = desc.getService(getService());
244                    if (svc == null) {
245                        throw new DeploymentException("Could not find service '" + getService() + "' in wsdl");
246                    }
247                } else if (desc.getServices().length == 1) {
248                    svc = desc.getServices()[0];
249                    setService(svc.getName());
250                } else {
251                    throw new DeploymentException(
252                                                  "If service is not set, the WSDL must contain a single service definition");
253                }
254                Endpoint endpoint;
255                if (getEndpoint() != null) {
256                    endpoint = svc.getEndpoint(new NCName(getEndpoint()));
257                    if (endpoint == null) {
258                        throw new DeploymentException("Cound not find endpoint '" + getEndpoint()
259                                                      + "' in wsdl for service '" + getService() + "'");
260                    }
261                } else if (svc.getEndpoints().length == 1) {
262                    endpoint = svc.getEndpoints()[0];
263                    setEndpoint(endpoint.getName().toString());
264                } else {
265                    throw new DeploymentException("If endpoint is not set, the WSDL service '" + getService()
266                                                  + "' must contain a single port definition");
267                }
268                marshaler.setBinding(BindingFactory.createBinding(endpoint));
269            }
270        }
271    
272    }