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