View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.soap.axis;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.construct.FlowConstruct;
12  import org.mule.api.context.notification.MuleContextNotificationListener;
13  import org.mule.api.endpoint.EndpointBuilder;
14  import org.mule.api.endpoint.EndpointURI;
15  import org.mule.api.endpoint.ImmutableEndpoint;
16  import org.mule.api.endpoint.InboundEndpoint;
17  import org.mule.api.lifecycle.InitialisationException;
18  import org.mule.api.processor.MessageProcessor;
19  import org.mule.api.security.SecurityFilter;
20  import org.mule.api.service.Service;
21  import org.mule.api.source.CompositeMessageSource;
22  import org.mule.api.transport.MessageReceiver;
23  import org.mule.component.DefaultJavaComponent;
24  import org.mule.config.ExceptionHelper;
25  import org.mule.config.i18n.CoreMessages;
26  import org.mule.context.notification.MuleContextNotification;
27  import org.mule.endpoint.AbstractEndpointBuilder;
28  import org.mule.endpoint.EndpointURIEndpointBuilder;
29  import org.mule.model.seda.SedaService;
30  import org.mule.object.SingletonObjectFactory;
31  import org.mule.processor.SecurityFilterMessageProcessor;
32  import org.mule.routing.MessageFilter;
33  import org.mule.service.ServiceCompositeMessageSource;
34  import org.mule.transport.AbstractConnector;
35  import org.mule.transport.service.TransportFactory;
36  import org.mule.transport.servlet.ServletConnector;
37  import org.mule.transport.soap.axis.extensions.MuleConfigProvider;
38  import org.mule.transport.soap.axis.extensions.MuleTransport;
39  import org.mule.transport.soap.axis.extensions.WSDDFileProvider;
40  import org.mule.transport.soap.axis.extensions.WSDDJavaMuleProvider;
41  import org.mule.transport.soap.axis.i18n.AxisMessages;
42  import org.mule.util.ClassUtils;
43  import org.mule.util.CollectionUtils;
44  import org.mule.util.MuleUrlStreamHandlerFactory;
45  
46  import java.lang.reflect.Field;
47  import java.util.ArrayList;
48  import java.util.HashMap;
49  import java.util.Iterator;
50  import java.util.List;
51  import java.util.Map;
52  
53  import javax.xml.namespace.QName;
54  
55  import org.apache.axis.AxisProperties;
56  import org.apache.axis.client.Call;
57  import org.apache.axis.configuration.SimpleProvider;
58  import org.apache.axis.deployment.wsdd.WSDDConstants;
59  import org.apache.axis.deployment.wsdd.WSDDProvider;
60  import org.apache.axis.encoding.TypeMappingRegistryImpl;
61  import org.apache.axis.encoding.ser.BeanDeserializerFactory;
62  import org.apache.axis.encoding.ser.BeanSerializerFactory;
63  import org.apache.axis.handlers.soap.SOAPService;
64  import org.apache.axis.server.AxisServer;
65  import org.apache.axis.wsdl.fromJava.Namespaces;
66  import org.apache.axis.wsdl.fromJava.Types;
67  
68  /**
69   * <code>AxisConnector</code> is used to maintain one or more Services for Axis
70   * server instance.
71   * <p/>
72   * Some of the Axis specific service initialisation code was adapted from the Ivory
73   * project (http://ivory.codehaus.org). Thanks guys :)
74   */
75  public class AxisConnector extends AbstractConnector implements MuleContextNotificationListener<MuleContextNotification>
76  {
77      /* Register the AxisFault Exception reader if this class gets loaded */
78      static
79      {
80          ExceptionHelper.registerExceptionReader(new AxisFaultExceptionReader());
81      }
82  
83      public static final QName QNAME_MULE_PROVIDER = new QName(WSDDConstants.URI_WSDD_JAVA, "Mule");
84      public static final QName QNAME_MULE_TYPE_MAPPINGS = new QName("http://www.muleumo.org/ws/mappings",
85              "Mule");
86      public static final String DEFAULT_MULE_NAMESPACE_URI = "http://www.muleumo.org";
87  
88      public static final String DEFAULT_MULE_AXIS_SERVER_CONFIG = "mule-axis-server-config.wsdd";
89      public static final String DEFAULT_MULE_AXIS_CLIENT_CONFIG = "mule-axis-client-config.wsdd";
90      public static final String AXIS_SERVICE_COMPONENT_NAME = "_axisServiceComponent";
91      public static final String AXIS_SERVICE_PROPERTY = "_axisService";
92      public static final String AXIS_CLIENT_CONFIG_PROPERTY = "clientConfig";
93  
94      public static final String SERVICE_PROPERTY_COMPONENT_NAME = "componentName";
95      public static final String SERVICE_PROPERTY_SERVCE_PATH = "servicePath";
96  
97      public static final String AXIS = "axis";
98  
99      // used by dispatcher and receiver
100     public static final String SOAP_METHODS = "soapMethods";
101     public static final String STYLE = "style";
102     public static final String USE = "use";
103 
104     private String serverConfig = DEFAULT_MULE_AXIS_SERVER_CONFIG;
105 
106     private AxisServer axis = null;
107     private SimpleProvider serverProvider = null;
108     private String clientConfig = DEFAULT_MULE_AXIS_CLIENT_CONFIG;
109     private SimpleProvider clientProvider = null;
110 
111     private List beanTypes;
112     private Service axisComponent;
113 
114     //this will store the name of the descriptor of the current connector's AxisServiceComponent
115     //private String specificAxisServiceComponentName;
116 
117     /**
118      * These protocols will be set on client invocations. By default Mule uses it's
119      * own transports rather that Axis's. This is only because it gives us more
120      * flexibility inside Mule and simplifies the code
121      */
122     private Map axisTransportProtocols = null;
123 
124     /**
125      * A store of registered servlet services that need to have their endpoints
126      * re-written with the 'real' http url instead of the servlet:// one. This is
127      * only required to ensure wsdl is generated correctly. I would like a clearer
128      * way of doing this so I can remove this workaround
129      */
130     private List servletServices = new ArrayList();
131 
132     private List supportedSchemes = null;
133 
134     private boolean doAutoTypes = true;
135 
136     private boolean treatMapAsNamedParams = true;
137 
138     public AxisConnector(MuleContext context)
139     {
140         super(context);
141         this.registerProtocols();
142     }
143 
144     protected void registerProtocols()
145     {
146         if (supportedSchemes == null)
147         {
148             // Default supported schemes, these can be restricted
149             // through configuration
150             supportedSchemes = new ArrayList();
151             supportedSchemes.add("http");
152             supportedSchemes.add("https");
153             supportedSchemes.add("servlet");
154             supportedSchemes.add("vm");
155             supportedSchemes.add("jms");
156             supportedSchemes.add("xmpp");
157             supportedSchemes.add("ssl");
158             supportedSchemes.add("tcp");
159             supportedSchemes.add("smtp");
160             supportedSchemes.add("smtps");
161             supportedSchemes.add("pop3");
162             supportedSchemes.add("pop3s");
163             supportedSchemes.add("imap");
164             supportedSchemes.add("imaps");
165         }
166 
167         for (Iterator iterator = supportedSchemes.iterator(); iterator.hasNext();)
168         {
169             String s = (String) iterator.next();
170             registerSupportedProtocol(s);
171         }
172     }
173 
174     @Override
175     protected void doInitialise() throws InitialisationException
176     {
177         axisTransportProtocols = new HashMap();
178         //specificAxisServiceComponentName = AXIS_SERVICE_COMPONENT_NAME + "_" + name;
179 
180         axisTransportProtocols = new HashMap();
181         try
182         {
183             for (Iterator iterator = supportedSchemes.iterator(); iterator.hasNext();)
184             {
185                 String s = (String) iterator.next();
186                 axisTransportProtocols.put(s, MuleTransport.getTransportClass(s));
187                 registerSupportedProtocol(s);
188             }
189             muleContext.registerListener(this);
190         }
191         catch (Exception e)
192         {
193             throw new InitialisationException(e, this);
194         }
195         // TODO DO: call registerSupportedProtocol if axisTransportProtocols are set from external?
196 
197         if (clientProvider == null)
198         {
199             clientProvider = createAxisProvider(clientConfig);
200         }
201         else
202         {
203             if (!DEFAULT_MULE_AXIS_CLIENT_CONFIG.equals(clientConfig))
204             {
205                 logger.warn(AxisMessages.clientProviderAndClientConfigConfigured());
206             }
207         }
208 
209         if (axis == null)
210         {
211             if (serverProvider == null)
212             {
213                 serverProvider = this.createAxisProvider(serverConfig);
214             }
215             else
216             {
217                 if (!DEFAULT_MULE_AXIS_SERVER_CONFIG.equals(serverConfig))
218                 {
219                     logger.warn(AxisMessages.serverProviderAndServerConfigConfigured());
220                 }
221             }
222 
223             // Create the AxisServer
224             axis = new AxisServer(serverProvider);
225             // principle of least surprise: doAutoTypes only has effect on our self-configured AxisServer
226             axis.setOption("axis.doAutoTypes", Boolean.valueOf(doAutoTypes));
227         }
228 
229         // Register the Mule service serverProvider
230         WSDDProvider.registerProvider(QNAME_MULE_PROVIDER, new WSDDJavaMuleProvider(this));
231 
232         try
233         {
234             registerTransportTypes();
235         }
236         catch (ClassNotFoundException e)
237         {
238             throw new InitialisationException(
239                     CoreMessages.cannotLoadFromClasspath(e.getMessage()), e, this);
240         }
241 
242         // Register all our UrlStreamHandlers here so they can be resolved. This is necessary
243         // to make Mule work in situations where modification of system properties at runtime
244         // is not reliable, e.g. when running in maven's surefire test executor.
245         MuleUrlStreamHandlerFactory.registerHandler("jms", new org.mule.transport.soap.axis.transport.jms.Handler());
246         MuleUrlStreamHandlerFactory.registerHandler("pop3", new org.mule.transport.soap.axis.transport.pop3.Handler());
247         MuleUrlStreamHandlerFactory.registerHandler("smtp", new org.mule.transport.soap.axis.transport.smtp.Handler());
248         MuleUrlStreamHandlerFactory.registerHandler("vm", new org.mule.transport.soap.axis.transport.vm.Handler());
249 
250         try
251         {
252             registerTypes((TypeMappingRegistryImpl) axis.getTypeMappingRegistry(), beanTypes);
253         }
254         catch (ClassNotFoundException e)
255         {
256             throw new InitialisationException(e, this);
257         }
258     }
259 
260     protected void registerTransportTypes() throws ClassNotFoundException
261     {
262         // Register Transport handlers
263         // By default these will all be handled by Mule, however some companies may
264         // have their own they wish to use
265         for (Iterator iterator = getAxisTransportProtocols().keySet().iterator(); iterator.hasNext();)
266         {
267             String protocol = (String) iterator.next();
268             Object temp = getAxisTransportProtocols().get(protocol);
269             Class clazz;
270             if (temp instanceof String)
271             {
272                 clazz = ClassUtils.loadClass(temp.toString(), getClass());
273             }
274             else
275             {
276                 clazz = (Class) temp;
277             }
278             Call.setTransportForProtocol(protocol, clazz);
279         }
280     }
281 
282     protected SimpleProvider createAxisProvider(String config) throws InitialisationException
283     {
284         // Use our custom file provider that does not require services to be declared
285         // in the WSDD. This only affects the
286         // client side as the client will fallback to the FileProvider when invoking
287         // a service.
288         WSDDFileProvider fileProvider = new WSDDFileProvider(config);
289         fileProvider.setSearchClasspath(true);
290         /*
291          * Wrap the FileProvider with a SimpleProvider so we can programmatically
292          * configure the Axis server (you can only use wsdd descriptors with the
293          * FileProvider)
294          */
295         return new MuleConfigProvider(fileProvider);
296     }
297 
298     public String getProtocol()
299     {
300         return AXIS;
301     }
302 
303     /**
304      * The method determines the key used to store the receiver against.
305      *
306      * @param flowConstruct the component for which the endpoint is being registered
307      * @param endpoint  the endpoint being registered for the component
308      * @return the key to store the newly created receiver against. In this case it
309      *         is the component name, which is equivalent to the Axis service name.
310      */
311     @Override
312     protected Object getReceiverKey(FlowConstruct flowConstruct, InboundEndpoint endpoint)
313     {
314         if (endpoint.getEndpointURI().getPort() == -1)
315         {
316             return flowConstruct.getName();
317         }
318         else
319         {
320             return endpoint.getEndpointURI().getAddress() + "/" + flowConstruct.getName();
321         }
322     }
323 
324     protected void unregisterReceiverWithMuleService(MessageReceiver receiver, EndpointURI ep)
325             throws MuleException
326     {
327         String endpointKey = getCounterEndpointKey(receiver.getEndpointURI());
328 
329         for (Iterator iterator = ((ServiceCompositeMessageSource) axisComponent.getMessageSource()).getEndpoints().iterator(); iterator.hasNext();)
330         {
331             ImmutableEndpoint endpoint = (ImmutableEndpoint) iterator.next();
332             if (endpointKey.startsWith(endpoint.getEndpointURI().getAddress()))
333             {
334                 logger.info("Unregistering Axis endpoint: " + endpointKey + " for service: "
335                         + ((AxisMessageReceiver) receiver).getSoapService().getName());
336             }
337             try
338             {
339                 endpoint.getConnector()
340                         .unregisterListener(receiver.getEndpoint(), axisComponent);
341             }
342             catch (Exception e)
343             {
344                 logger.error("Failed to unregister Axis endpoint: " + endpointKey + " for service: "
345                         + receiver.getFlowConstruct().getName() + ". Error is: "
346                         + e.getMessage(), e);
347             }
348         }
349     }
350 
351     protected void registerReceiverWithMuleService(MessageReceiver receiver, EndpointURI ep)
352             throws MuleException
353     {
354         // If this is the first receiver we need to create the Axis service
355         // component this will be registered with Mule when the Connector starts
356         // See if the axis descriptor has already been added. This allows
357         // developers to override the default configuration, say to increase
358         // the threadpool
359         if (axisComponent == null)
360         {
361             axisComponent = getOrCreateAxisComponent();
362         }
363         else
364         {
365             // Lets unregister the 'template' instance, configure it and
366             // then register again later
367             muleContext.getRegistry().unregisterService(AXIS_SERVICE_PROPERTY + getName());
368         }
369 
370         String serviceName = ((AxisMessageReceiver) receiver).getSoapService().getName();
371         // No determine if the endpointUri requires a new connector to be
372         // registed in the case of http we only need to register the new endpointUri
373         // if the port is different If we're using VM or Jms we just use the resource
374         // info directly without appending a service name
375         String endpoint;
376         String scheme = ep.getScheme().toLowerCase();
377         if (scheme.equals("jms") || scheme.equals("vm") || scheme.equals("servlet"))
378         {
379             endpoint = ep.toString();
380         }
381         else
382         {
383             endpoint = receiver.getEndpointURI().getAddress() + "/" + serviceName;
384         }
385         if (logger.isDebugEnabled())
386         {
387             logger.debug("Modified endpoint with " + scheme + " scheme to " + endpoint);
388         }
389 
390         EndpointBuilder serviceEndpointbuilder = new EndpointURIEndpointBuilder(endpoint, muleContext);
391         serviceEndpointbuilder.setExchangePattern(receiver.getEndpoint().getExchangePattern());
392         serviceEndpointbuilder.setName(ep.getScheme() + ":" + serviceName);
393         // Set the transformers on the endpoint too
394         serviceEndpointbuilder.setTransformers(receiver.getEndpoint().getTransformers().isEmpty() ? null
395                                                                                                   : receiver.getEndpoint().getTransformers());
396         serviceEndpointbuilder.setResponseTransformers(receiver.getEndpoint().getResponseTransformers().isEmpty() ? null
397                                                                                                                  : receiver.getEndpoint().getResponseTransformers());
398         // set the filter on the axis endpoint on the real receiver endpoint
399         serviceEndpointbuilder.addMessageProcessor(new MessageFilter(receiver.getEndpoint().getFilter()));
400         // set the Security filter on the axis endpoint on the real receiver
401         // endpoint
402         SecurityFilter securityFilter = receiver.getEndpoint().getSecurityFilter();
403         if (securityFilter != null)
404         {
405             serviceEndpointbuilder.addMessageProcessor(new SecurityFilterMessageProcessor(securityFilter));
406         }
407 
408         // TODO Do we really need to modify the existing receiver endpoint? What happens if we don't security,
409         // filters and transformers will get invoked twice?
410         AbstractEndpointBuilder receiverEndpointBuilder = new EndpointURIEndpointBuilder(receiver.getEndpoint());
411         // Remove the Axis filter now
412 
413         List<MessageProcessor> procs = new ArrayList(receiverEndpointBuilder.getMessageProcessors());
414         CollectionUtils.removeType(procs, MessageFilter.class);
415         CollectionUtils.removeType(procs, SecurityFilterMessageProcessor.class);
416         receiverEndpointBuilder.setMessageProcessors(procs);
417 
418         InboundEndpoint serviceEndpoint = muleContext.getEndpointFactory()
419             .getInboundEndpoint(serviceEndpointbuilder);
420 
421         InboundEndpoint receiverEndpoint = muleContext.getEndpointFactory()
422             .getInboundEndpoint(receiverEndpointBuilder);
423 
424         receiver.setEndpoint(receiverEndpoint);
425 
426         ((CompositeMessageSource) axisComponent.getMessageSource()).addSource(serviceEndpoint);
427     }
428 
429     private String getCounterEndpointKey(EndpointURI endpointURI)
430     {
431         StringBuffer endpointKey = new StringBuffer(64);
432 
433         endpointKey.append(endpointURI.getScheme());
434         endpointKey.append("://");
435         endpointKey.append(endpointURI.getHost());
436         if (endpointURI.getPort() > -1)
437         {
438             endpointKey.append(":");
439             endpointKey.append(endpointURI.getPort());
440         }
441         return endpointKey.toString();
442     }
443 
444     // This initialization could be performed in the initialize() method.  Putting it here essentially makes
445     // it a lazy-create/lazy-init
446     // Another option would be to put it in the default-axis-config.xml (MULE-2102) with lazy-init="true"
447     // but that makes us depend on Spring.
448     // Another consideration is how/when this implicit component gets disposed.
449     protected Service getOrCreateAxisComponent() throws MuleException
450     {
451         Service service = muleContext.getRegistry().lookupService(AXIS_SERVICE_PROPERTY + getName());
452 
453         if (service == null)
454         {
455             // TODO MULE-2228 Simplify this API
456             service = new SedaService(muleContext);
457             service.setName(AXIS_SERVICE_PROPERTY + getName());
458             service.setModel(muleContext.getRegistry().lookupSystemModel());
459 
460             Map props = new HashMap();
461             props.put(AXIS, axis);
462             SingletonObjectFactory of = new SingletonObjectFactory(AxisServiceComponent.class, props);
463             final DefaultJavaComponent component = new DefaultJavaComponent(of);
464             component.setMuleContext(muleContext);
465             service.setComponent(component);
466         }
467         return service;
468     }
469 
470     /**
471      * Template method to perform any work when starting the connectoe
472      *
473      * @throws org.mule.api.MuleException if the method fails
474      */
475     @Override
476     protected void doStart() throws MuleException
477     {
478         axis.start();
479     }
480 
481     /**
482      * Template method to perform any work when stopping the connectoe
483      *
484      * @throws org.mule.api.MuleException if the method fails
485      */
486     @Override
487     protected void doStop() throws MuleException
488     {
489         axis.stop();
490         // Model model = muleContext.getRegistry().lookupModel();
491         // model.unregisterComponent(model.getDescriptor(AXIS_SERVICE_COMPONENT_NAME));
492     }
493 
494     @Override
495     protected void doConnect() throws Exception
496     {
497         // template method
498     }
499 
500     @Override
501     protected void doDisconnect() throws Exception
502     {
503         // template method
504     }
505 
506     @Override
507     protected void doDispose()
508     {
509         // clear static references to classloaders to prevent loading classes from
510         // closed jars and avoid memory leaks on application redeploy.
511         try
512         {
513             Field field = AxisProperties.class.getDeclaredField("loaders");
514             field.setAccessible(true);
515             field.set(null, null);
516 
517             field = AxisProperties.class.getDeclaredField("nameDiscoverer");
518             field.setAccessible(true);
519             field.set(null, null);
520         }
521         catch (Exception e)
522         {
523             logger.error(
524                 "Error disposing AxisConnector, this may cause a memory leak. Error is: " + e.getMessage(), e);
525         }
526 
527     }
528 
529     public String getServerConfig()
530     {
531         return serverConfig;
532     }
533 
534     public void setServerConfig(String serverConfig)
535     {
536         this.serverConfig = serverConfig;
537     }
538 
539     public List getBeanTypes()
540     {
541         return beanTypes;
542     }
543 
544     public void setBeanTypes(List beanTypes)
545     {
546         this.beanTypes = beanTypes;
547     }
548 
549     public String getClientConfig()
550     {
551         return clientConfig;
552     }
553 
554     public void setClientConfig(String clientConfig)
555     {
556         this.clientConfig = clientConfig;
557     }
558 
559     public AxisServer getAxis()
560     {
561         return axis;
562     }
563 
564     public void setAxis(AxisServer axisServer)
565     {
566         this.axis = axisServer;
567     }
568 
569     public SimpleProvider getServerProvider()
570     {
571         return serverProvider;
572     }
573 
574     public void setServerProvider(SimpleProvider serverProvider)
575     {
576         this.serverProvider = serverProvider;
577     }
578 
579     public SimpleProvider getClientProvider()
580     {
581         return clientProvider;
582     }
583 
584     public void setClientProvider(SimpleProvider clientProvider)
585     {
586         this.clientProvider = clientProvider;
587     }
588 
589     public Map getAxisTransportProtocols()
590     {
591         return axisTransportProtocols;
592     }
593 
594     public void setAxisTransportProtocols(Map axisTransportProtocols)
595     {
596         this.axisTransportProtocols.putAll(axisTransportProtocols);
597     }
598 
599     void addServletService(SOAPService service)
600     {
601         servletServices.add(service);
602     }
603 
604     public List getSupportedSchemes()
605     {
606         return supportedSchemes;
607     }
608 
609     public void setSupportedSchemes(List supportedSchemes)
610     {
611         this.supportedSchemes = supportedSchemes;
612     }
613 
614     public boolean isDoAutoTypes()
615     {
616         return doAutoTypes;
617     }
618 
619     public void setDoAutoTypes(boolean doAutoTypes)
620     {
621         this.doAutoTypes = doAutoTypes;
622     }
623 
624     void registerTypes(TypeMappingRegistryImpl registry, List types) throws ClassNotFoundException
625     {
626         if (types != null)
627         {
628             Class clazz;
629             for (Iterator iterator = types.iterator(); iterator.hasNext();)
630             {
631                 clazz = ClassUtils.loadClass(iterator.next().toString(), getClass());
632                 String localName = Types.getLocalNameFromFullName(clazz.getName());
633                 QName xmlType = new QName(Namespaces.makeNamespace(clazz.getName()), localName);
634 
635                 registry.getDefaultTypeMapping().register(clazz, xmlType,
636                         new BeanSerializerFactory(clazz, xmlType), new BeanDeserializerFactory(clazz, xmlType));
637             }
638         }
639     }
640 
641     public boolean isTreatMapAsNamedParams()
642     {
643         return treatMapAsNamedParams;
644     }
645 
646     public void setTreatMapAsNamedParams(boolean treatMapAsNamedParams)
647     {
648         this.treatMapAsNamedParams = treatMapAsNamedParams;
649     }
650 
651     public void onNotification(MuleContextNotification notification)
652     {
653         try
654         {
655             if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED)
656             {
657                 // We need to register the Axis service component once the muleContext
658                 // starts because when the model starts listeners on components are started, thus
659                 // all listener need to be registered for this connector before the Axis service
660                 // component is registered.
661                 // The implication of this is that to add a new service and a
662                 // different http port the model needs to be restarted before the listener is available
663                 if (muleContext.getRegistry().lookupService(AXIS_SERVICE_PROPERTY + getName()) == null)
664                 {
665                     // Descriptor might be null if no inbound endpoints have been
666                     // register for the Axis connector
667                     if (axisComponent == null)
668                     {
669                         axisComponent = getOrCreateAxisComponent();
670                     }
671                     muleContext.getRegistry().registerService(axisComponent);
672 
673                     // We have to perform a small hack here to rewrite servlet://
674                     // endpoints with the
675                     // real http:// address
676                     for (Iterator iterator = servletServices.iterator(); iterator.hasNext();)
677                     {
678                         SOAPService service = (SOAPService) iterator.next();
679                         ServletConnector servletConnector = (ServletConnector) new TransportFactory(muleContext).getConnectorByProtocol("servlet");
680                         String url = servletConnector.getServletUrl();
681                         if (url != null)
682                         {
683                             service.getServiceDescription().setEndpointURL(url + "/" + service.getName());
684                         }
685                         else
686                         {
687                             logger.error("The servletUrl property on the ServletConntector has not been set this means that wsdl generation for service '"
688                                     + service.getName() + "' may be incorrect");
689                         }
690                     }
691                     servletServices.clear();
692                 }
693             }
694         }
695         catch (MuleException e)
696         {
697             handleException(e);
698         }
699     }
700 }