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.component.ComponentContext;
027 import javax.jbi.messaging.MessageExchange.Role;
028 import javax.jbi.messaging.MessageExchange;
029 import javax.jbi.servicedesc.ServiceEndpoint;
030 import javax.wsdl.Definition;
031 import javax.wsdl.Import;
032 import javax.wsdl.Types;
033 import javax.wsdl.WSDLException;
034 import javax.wsdl.extensions.ExtensibilityElement;
035 import javax.wsdl.extensions.ExtensionRegistry;
036 import javax.wsdl.extensions.schema.Schema;
037 import javax.wsdl.extensions.schema.SchemaImport;
038 import javax.wsdl.extensions.schema.SchemaReference;
039 import javax.wsdl.factory.WSDLFactory;
040 import javax.wsdl.xml.WSDLReader;
041 import javax.wsdl.xml.WSDLWriter;
042 import javax.xml.namespace.QName;
043
044 import org.apache.servicemix.common.JbiConstants;
045 import org.apache.servicemix.common.endpoints.AbstractEndpoint;
046 import org.apache.servicemix.common.security.KeystoreManager;
047 import org.apache.servicemix.common.security.AuthenticationService;
048 import org.apache.servicemix.common.wsdl1.JbiExtension;
049 import org.apache.servicemix.soap.handlers.addressing.AddressingHandler;
050 import org.springframework.core.io.Resource;
051 import org.w3c.dom.Document;
052
053 import com.ibm.wsdl.Constants;
054
055 public abstract class SoapEndpoint extends AbstractEndpoint {
056
057 protected ServiceEndpoint activated;
058 protected SoapExchangeProcessor processor;
059 protected Role role;
060 protected URI defaultMep = JbiConstants.IN_OUT;
061 protected boolean soap;
062 protected String soapVersion;
063 protected Resource wsdlResource;
064 protected QName defaultOperation;
065 protected QName targetInterfaceName;
066 protected QName targetService;
067 protected String targetEndpoint;
068 protected List policies;
069 protected Map wsdls = new HashMap();
070 protected boolean dynamic;
071
072 public SoapEndpoint() {
073 policies = Collections.singletonList(new AddressingHandler());
074 }
075
076 public SoapEndpoint(boolean dynamic) {
077 this();
078 this.dynamic = dynamic;
079 }
080
081 public AuthenticationService getAuthenticationService() {
082 return null;
083 }
084
085 public KeystoreManager getKeystoreManager() {
086 return null;
087 }
088
089 /**
090 * @return the policies
091 */
092 public List getPolicies() {
093 return policies;
094 }
095 /**
096 * @param policies the policies to set
097 */
098 public void setPolicies(List policies) {
099 this.policies = policies;
100 }
101 /**
102 * @return Returns the defaultMep.
103 */
104 public URI getDefaultMep() {
105 return defaultMep;
106 }
107 /**
108 * @param defaultMep The defaultMep to set.
109 */
110 public void setDefaultMep(URI defaultMep) {
111 this.defaultMep = defaultMep;
112 }
113 /**
114 * @return Returns the defaultOperation.
115 */
116 public QName getDefaultOperation() {
117 return defaultOperation;
118 }
119 /**
120 * @param defaultOperation The defaultOperation to set.
121 */
122 public void setDefaultOperation(QName defaultOperation) {
123 this.defaultOperation = defaultOperation;
124 }
125 /**
126 * @return Returns the role.
127 */
128 public Role getRole() {
129 return role;
130 }
131 /**
132 * @param role The role to set.
133 */
134 public void setRole(Role role) {
135 this.role = role;
136 }
137 /**
138 * @return Returns the soap.
139 */
140 public boolean isSoap() {
141 return soap;
142 }
143 /**
144 * @param soap The soap to set.
145 */
146 public void setSoap(boolean soap) {
147 this.soap = soap;
148 }
149 /**
150 * @return Returns the soapVersion.
151 */
152 public String getSoapVersion() {
153 return soapVersion;
154 }
155 /**
156 * @param soapVersion The soapVersion to set.
157 */
158 public void setSoapVersion(String soapVersion) {
159 this.soapVersion = soapVersion;
160 }
161 /**
162 * @return Returns the targetEndpoint.
163 */
164 public String getTargetEndpoint() {
165 return targetEndpoint;
166 }
167 /**
168 * @param targetEndpoint The targetEndpoint to set.
169 */
170 public void setTargetEndpoint(String targetEndpoint) {
171 this.targetEndpoint = targetEndpoint;
172 }
173 /**
174 * @return Returns the targetInterfaceName.
175 */
176 public QName getTargetInterfaceName() {
177 return targetInterfaceName;
178 }
179 /**
180 * @param targetInterfaceName The targetInterfaceName to set.
181 */
182 public void setTargetInterfaceName(QName targetInterfaceName) {
183 this.targetInterfaceName = targetInterfaceName;
184 }
185 /**
186 * @return Returns the targetServiceName.
187 */
188 public QName getTargetService() {
189 return targetService;
190 }
191 /**
192 * @param targetServiceName The targetServiceName to set.
193 */
194 public void setTargetService(QName targetServiceName) {
195 this.targetService = targetServiceName;
196 }
197 /**
198 * @return Returns the wsdlResource.
199 */
200 public Resource getWsdlResource() {
201 return wsdlResource;
202 }
203 /**
204 * @param wsdlResource The wsdlResource to set.
205 */
206 public void setWsdlResource(Resource wsdlResource) {
207 this.wsdlResource = wsdlResource;
208 }
209 /**
210 * @org.apache.xbean.Property alias="role"
211 * @param role
212 */
213 public void setRoleAsString(String role) {
214 if (role == null) {
215 throw new IllegalArgumentException("Role must be specified");
216 } else if (JbiExtension.ROLE_CONSUMER.equals(role)) {
217 setRole(Role.CONSUMER);
218 } else if (JbiExtension.ROLE_PROVIDER.equals(role)) {
219 setRole(Role.PROVIDER);
220 } else {
221 throw new IllegalArgumentException("Unrecognized role: " + role);
222 }
223 }
224
225 /**
226 * Load the wsdl for this endpoint.
227 */
228 protected void loadWsdl() {
229 // Load WSDL from the resource
230 if (description == null && wsdlResource != null) {
231 ClassLoader cl = Thread.currentThread().getContextClassLoader();
232 try {
233 Thread.currentThread().setContextClassLoader(serviceUnit.getConfigurationClassLoader());
234 WSDLReader reader = WSDLFactory.newInstance().newWSDLReader();
235 reader.setFeature(Constants.FEATURE_VERBOSE, false);
236 Definition def = reader.readWSDL(wsdlResource.getURL().toString());
237 overrideDefinition(def);
238 } catch (Exception e) {
239 logger.warn("Could not load description from resource", e);
240 } finally {
241 Thread.currentThread().setContextClassLoader(cl);
242 }
243 }
244 // If the endpoint is a consumer, try to find
245 // the proxied endpoint description
246 if (description == null && definition == null && getRole() == Role.CONSUMER) {
247 retrieveProxiedEndpointDefinition();
248 }
249 // If the wsdl definition is provided,
250 // convert it to a DOM document
251 if (description == null && definition != null) {
252 try {
253 description = WSDLFactory.newInstance().newWSDLWriter().getDocument(definition);
254 } catch (Exception e) {
255 logger.warn("Could not create document from wsdl description", e);
256 }
257 }
258 // If the dom description is provided
259 // convert it to a WSDL definition
260 if (definition == null && description != null) {
261 try {
262 definition = WSDLFactory.newInstance().newWSDLReader().readWSDL(null, description);
263 } catch (Exception e) {
264 logger.warn("Could not create wsdl definition from dom document", e);
265 }
266 }
267 if (definition != null) {
268 try {
269 mapDefinition(definition);
270 } catch (Exception e) {
271 logger.warn("Could not map wsdl definition to documents", e);
272 }
273 }
274 }
275
276 /**
277 * Create a wsdl definition for a consumer endpoint.
278 * Loads the target endpoint definition and add http binding
279 * informations to it.
280 */
281 protected void retrieveProxiedEndpointDefinition() {
282 if (logger.isDebugEnabled()) {
283 logger.debug("Retrieving proxied endpoint definition");
284 }
285 try {
286 ComponentContext ctx = this.serviceUnit.getComponent().getComponentContext();
287 ServiceEndpoint ep = null;
288 if (targetService != null && targetEndpoint != null) {
289 ep = ctx.getEndpoint(targetService, targetEndpoint);
290 if (ep == null && logger.isDebugEnabled()) {
291 logger.debug("Could not retrieve endpoint targetService/targetEndpoint");
292 }
293 }
294 if (ep == null && targetService != null) {
295 ServiceEndpoint[] eps = ctx.getEndpointsForService(targetService);
296 if (eps != null && eps.length > 0) {
297 ep = eps[0];
298 }
299 if (ep == null && logger.isDebugEnabled()) {
300 logger.debug("Could not retrieve endpoint for targetService");
301 }
302 }
303 if (ep == null && targetInterfaceName != null) {
304 ServiceEndpoint[] eps = ctx.getEndpoints(targetInterfaceName);
305 if (eps != null && eps.length > 0) {
306 ep = eps[0];
307 }
308 if (ep == null && logger.isDebugEnabled()) {
309 logger.debug("Could not retrieve endpoint for targetInterfaceName");
310 }
311 }
312 if (ep == null && service != null && endpoint != null) {
313 ep = ctx.getEndpoint(service, endpoint);
314 if (ep == null && logger.isDebugEnabled()) {
315 logger.debug("Could not retrieve endpoint for service/endpoint");
316 }
317 }
318 if (ep != null) {
319 Document doc = ctx.getEndpointDescriptor(ep);
320 if (doc != null) {
321 WSDLReader reader = WSDLFactory.newInstance().newWSDLReader();
322 reader.setFeature(Constants.FEATURE_VERBOSE, false);
323 Definition def = reader.readWSDL(null, doc);
324 if (def != null) {
325 overrideDefinition(def);
326 }
327 }
328 }
329 } catch (Exception e) {
330 logger.debug("Unable to retrieve target endpoint descriptor", e);
331 }
332 }
333
334 /* (non-Javadoc)
335 * @see org.servicemix.common.Endpoint#activate()
336 */
337 public void activate() throws Exception {
338 if (dynamic) {
339 if (getRole() == Role.PROVIDER) {
340 processor = createProviderProcessor();
341 } else {
342 processor = createConsumerProcessor();
343 }
344 } else {
345 ComponentContext ctx = this.serviceUnit.getComponent().getComponentContext();
346 loadWsdl();
347 if (getRole() == Role.PROVIDER) {
348 activated = ctx.activateEndpoint(service, endpoint);
349 processor = createProviderProcessor();
350 } else {
351 activated = createExternalEndpoint();
352 ctx.registerExternalEndpoint(activated);
353 processor = createConsumerProcessor();
354 }
355 }
356 processor.init();
357 }
358
359 public void start() throws Exception {
360 processor.start();
361 }
362
363 public void stop() throws Exception {
364 processor.stop();
365 }
366
367 public void process(MessageExchange exchange) throws Exception {
368 processor.process(exchange);
369 }
370
371 /* (non-Javadoc)
372 * @see org.servicemix.common.Endpoint#deactivate()
373 */
374 public void deactivate() throws Exception {
375 processor.shutdown();
376 if (activated != null) {
377 ComponentContext ctx = this.serviceUnit.getComponent().getComponentContext();
378 if (getRole() == Role.PROVIDER) {
379 ServiceEndpoint ep = activated;
380 activated = null;
381 ctx.deactivateEndpoint(ep);
382 } else {
383 ServiceEndpoint ep = activated;
384 activated = null;
385 ctx.deregisterExternalEndpoint(ep);
386 }
387 }
388 }
389
390 protected abstract void overrideDefinition(Definition def) throws Exception;
391
392 protected abstract SoapExchangeProcessor createProviderProcessor();
393
394 protected abstract SoapExchangeProcessor createConsumerProcessor();
395
396 protected abstract ServiceEndpoint createExternalEndpoint();
397
398 protected WSDLReader createWsdlReader() throws WSDLException {
399 WSDLFactory factory = WSDLFactory.newInstance();
400 ExtensionRegistry registry = factory.newPopulatedExtensionRegistry();
401 registerExtensions(registry);
402 WSDLReader reader = factory.newWSDLReader();
403 reader.setFeature(Constants.FEATURE_VERBOSE, false);
404 reader.setExtensionRegistry(registry);
405 return reader;
406 }
407
408 protected WSDLWriter createWsdlWriter() throws WSDLException {
409 WSDLFactory factory = WSDLFactory.newInstance();
410 ExtensionRegistry registry = factory.newPopulatedExtensionRegistry();
411 registerExtensions(registry);
412 WSDLWriter writer = factory.newWSDLWriter();
413 //writer.setExtensionRegistry(registry);
414 return writer;
415 }
416
417 protected void registerExtensions(ExtensionRegistry registry) {
418 JbiExtension.register(registry);
419 }
420
421
422 protected void mapDefinition(Definition def) throws WSDLException {
423 wsdls.put("main.wsdl", createWsdlWriter().getDocument(def));
424 mapImports(def, "");
425 }
426
427 protected void mapImports(Definition def, String contextPath) throws WSDLException {
428 // Add other imports to mapping
429 Map imports = def.getImports();
430 for (Iterator iter = imports.values().iterator(); iter.hasNext();) {
431 List imps = (List) iter.next();
432 for (Iterator iterator = imps.iterator(); iterator.hasNext();) {
433 Import imp = (Import) iterator.next();
434 Definition impDef = imp.getDefinition();
435 String impLoc = imp.getLocationURI();
436 if (impDef != null && impLoc != null && !URI.create(impLoc).isAbsolute()) {
437 impLoc = resolveRelativeURI(contextPath, impLoc);
438 wsdls.put(impLoc, createWsdlWriter().getDocument(impDef));
439 mapImports(impDef, getURIParent(impLoc));
440 }
441 }
442 }
443 // Add schemas to mapping
444 Types types = def.getTypes();
445 if (types != null) {
446 for (Iterator it = types.getExtensibilityElements().iterator(); it.hasNext();) {
447 ExtensibilityElement ee = (ExtensibilityElement) it.next();
448 if (ee instanceof Schema) {
449 Schema schema = (Schema) ee;
450 mapSchema(schema, "");
451 }
452 }
453 }
454 }
455
456 private void mapSchema(Schema schema, String contextPath) {
457 Map schemaImports = schema.getImports();
458 for (Iterator iter = schemaImports.values().iterator(); iter.hasNext();) {
459 List imps = (List) iter.next();
460 for (Iterator iterator = imps.iterator(); iterator.hasNext();) {
461 SchemaImport schemaImport = (SchemaImport) iterator.next();
462 Schema schemaImp = schemaImport.getReferencedSchema();
463 String schemaLoc = schemaImport.getSchemaLocationURI();
464 if (schemaLoc != null && schemaImp != null && schemaImp.getElement() != null && !URI.create(schemaLoc).isAbsolute()) {
465 schemaLoc = resolveRelativeURI(contextPath, schemaLoc);
466 wsdls.put(schemaLoc, schemaImp.getElement());
467 // recursively map imported schemas
468 mapSchema(schemaImp, getURIParent(schemaLoc));
469 }
470 }
471 }
472 List schemaIncludes = schema.getIncludes();
473 for (Iterator iter = schemaIncludes.iterator(); iter.hasNext();) {
474 SchemaReference schemaInclude = (SchemaReference) iter.next();
475 Schema schemaImp = schemaInclude.getReferencedSchema();
476 String schemaLoc = schemaInclude.getSchemaLocationURI();
477 if (schemaLoc != null && schemaImp != null && schemaImp.getElement() != null && !URI.create(schemaLoc).isAbsolute()) {
478 schemaLoc = resolveRelativeURI(contextPath, schemaLoc);
479 wsdls.put(schemaLoc, schemaImp.getElement());
480 // recursively map included schemas
481 mapSchema(schemaImp, getURIParent(schemaLoc));
482 }
483 }
484 }
485
486 /**
487 * Combines a relative path with a current directory, normalising any
488 * relative pathnames like "." and "..".
489 * <p>
490 * Example:
491 * <table>
492 * <tr><th>context</th><th>path</th><th>resolveRelativeURI(context, path)</th></tr>
493 * <tr><td>addressModification</td><td>../common/DataType.xsd</td><td>common/DataType.xsd</td></tr>
494 * </table>
495 *
496 * @param context The current directory.
497 * @param path The relative path to resolve against the current directory.
498 * @return the normalised path.
499 */
500 private static String resolveRelativeURI(String context, String path) {
501 if (context.length() > 0) {
502 return URI.create(context + "/" + path).normalize().getPath();
503 } else {
504 return path;
505 }
506 }
507
508 /**
509 * Removes the filename part of a URI path.
510 *
511 * @param path A URI path part.
512 * @return The URI path part with the filename part removed.
513 */
514 private static String getURIParent(String path) {
515 return URI.create(path + "/..").normalize().getPath();
516 }
517
518 /**
519 * @return Returns the wsdls.
520 */
521 public Map getWsdls() {
522 return wsdls;
523 }
524
525 }