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    }