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