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 }