001: /*
002: * $Id: AxisConnector.java 11376 2008-03-16 17:44:10Z dfeist $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.transport.soap.axis;
012:
013: import org.mule.api.MuleException;
014: import org.mule.api.context.notification.MuleContextNotificationListener;
015: import org.mule.api.context.notification.ServerNotification;
016: import org.mule.api.endpoint.EndpointBuilder;
017: import org.mule.api.endpoint.EndpointURI;
018: import org.mule.api.endpoint.ImmutableEndpoint;
019: import org.mule.api.endpoint.InboundEndpoint;
020: import org.mule.api.lifecycle.InitialisationException;
021: import org.mule.api.service.Service;
022: import org.mule.api.transport.MessageReceiver;
023: import org.mule.component.DefaultJavaComponent;
024: import org.mule.config.ExceptionHelper;
025: import org.mule.config.i18n.CoreMessages;
026: import org.mule.context.notification.MuleContextNotification;
027: import org.mule.endpoint.EndpointURIEndpointBuilder;
028: import org.mule.model.seda.SedaService;
029: import org.mule.object.SingletonObjectFactory;
030: import org.mule.transport.AbstractConnector;
031: import org.mule.transport.http.servlet.ServletConnector;
032: import org.mule.transport.service.TransportFactory;
033: import org.mule.transport.soap.axis.extensions.MuleConfigProvider;
034: import org.mule.transport.soap.axis.extensions.MuleTransport;
035: import org.mule.transport.soap.axis.extensions.WSDDFileProvider;
036: import org.mule.transport.soap.axis.extensions.WSDDJavaMuleProvider;
037: import org.mule.transport.soap.axis.i18n.AxisMessages;
038: import org.mule.util.ClassUtils;
039: import org.mule.util.MuleUrlStreamHandlerFactory;
040:
041: import java.util.ArrayList;
042: import java.util.HashMap;
043: import java.util.Iterator;
044: import java.util.List;
045: import java.util.Map;
046:
047: import javax.xml.namespace.QName;
048:
049: import org.apache.axis.client.Call;
050: import org.apache.axis.configuration.SimpleProvider;
051: import org.apache.axis.deployment.wsdd.WSDDConstants;
052: import org.apache.axis.deployment.wsdd.WSDDProvider;
053: import org.apache.axis.encoding.TypeMappingRegistryImpl;
054: import org.apache.axis.encoding.ser.BeanDeserializerFactory;
055: import org.apache.axis.encoding.ser.BeanSerializerFactory;
056: import org.apache.axis.handlers.soap.SOAPService;
057: import org.apache.axis.server.AxisServer;
058: import org.apache.axis.wsdl.fromJava.Namespaces;
059: import org.apache.axis.wsdl.fromJava.Types;
060:
061: /**
062: * <code>AxisConnector</code> is used to maintain one or more Services for Axis
063: * server instance.
064: * <p/>
065: * Some of the Axis specific service initialisation code was adapted from the Ivory
066: * project (http://ivory.codehaus.org). Thanks guys :)
067: */
068: public class AxisConnector extends AbstractConnector implements
069: MuleContextNotificationListener {
070: /* Register the AxisFault Exception reader if this class gets loaded */
071: static {
072: ExceptionHelper
073: .registerExceptionReader(new AxisFaultExceptionReader());
074: }
075:
076: public static final QName QNAME_MULE_PROVIDER = new QName(
077: WSDDConstants.URI_WSDD_JAVA, "Mule");
078: public static final QName QNAME_MULE_TYPE_MAPPINGS = new QName(
079: "http://www.muleumo.org/ws/mappings", "Mule");
080: public static final String DEFAULT_MULE_NAMESPACE_URI = "http://www.muleumo.org";
081:
082: public static final String DEFAULT_MULE_AXIS_SERVER_CONFIG = "mule-axis-server-config.wsdd";
083: public static final String DEFAULT_MULE_AXIS_CLIENT_CONFIG = "mule-axis-client-config.wsdd";
084: public static final String AXIS_SERVICE_COMPONENT_NAME = "_axisServiceComponent";
085: public static final String AXIS_SERVICE_PROPERTY = "_axisService";
086: public static final String AXIS_CLIENT_CONFIG_PROPERTY = "clientConfig";
087:
088: public static final String SERVICE_PROPERTY_COMPONENT_NAME = "componentName";
089: public static final String SERVICE_PROPERTY_SERVCE_PATH = "servicePath";
090:
091: public static final String AXIS = "axis";
092:
093: // used by dispatcher and receiver
094: public static final String SOAP_METHODS = "soapMethods";
095: public static final String STYLE = "style";
096: public static final String USE = "use";
097:
098: private String serverConfig = DEFAULT_MULE_AXIS_SERVER_CONFIG;
099:
100: private AxisServer axis = null;
101: private SimpleProvider serverProvider = null;
102: private String clientConfig = DEFAULT_MULE_AXIS_CLIENT_CONFIG;
103: private SimpleProvider clientProvider = null;
104:
105: private List beanTypes;
106: private Service axisComponent;
107:
108: //this will store the name of the descriptor of the current connector's AxisServiceComponent
109: //private String specificAxisServiceComponentName;
110:
111: /**
112: * These protocols will be set on client invocations. By default Mule uses it's
113: * own transports rather that Axis's. This is only because it gives us more
114: * flexibility inside Mule and simplifies the code
115: */
116: private Map axisTransportProtocols = null;
117:
118: /**
119: * A store of registered servlet services that need to have their endpoints
120: * re-written with the 'real' http url instead of the servlet:// one. This is
121: * only required to ensure wsdl is generated correctly. I would like a clearer
122: * way of doing this so I can remove this workaround
123: */
124: private List servletServices = new ArrayList();
125:
126: private List supportedSchemes = null;
127:
128: private boolean doAutoTypes = true;
129:
130: private boolean treatMapAsNamedParams = true;
131:
132: public AxisConnector() {
133: super ();
134: this .registerProtocols();
135: }
136:
137: protected void registerProtocols() {
138: if (supportedSchemes == null) {
139: // Default supported schemes, these can be restricted
140: // through configuration
141: supportedSchemes = new ArrayList();
142: supportedSchemes.add("http");
143: supportedSchemes.add("https");
144: supportedSchemes.add("servlet");
145: supportedSchemes.add("vm");
146: supportedSchemes.add("jms");
147: supportedSchemes.add("xmpp");
148: supportedSchemes.add("ssl");
149: supportedSchemes.add("tcp");
150: supportedSchemes.add("smtp");
151: supportedSchemes.add("smtps");
152: supportedSchemes.add("pop3");
153: supportedSchemes.add("pop3s");
154: supportedSchemes.add("imap");
155: supportedSchemes.add("imaps");
156: }
157:
158: for (Iterator iterator = supportedSchemes.iterator(); iterator
159: .hasNext();) {
160: String s = (String) iterator.next();
161: registerSupportedProtocol(s);
162: }
163: }
164:
165: protected void doInitialise() throws InitialisationException {
166: axisTransportProtocols = new HashMap();
167: //specificAxisServiceComponentName = AXIS_SERVICE_COMPONENT_NAME + "_" + name;
168:
169: axisTransportProtocols = new HashMap();
170: try {
171: for (Iterator iterator = supportedSchemes.iterator(); iterator
172: .hasNext();) {
173: String s = (String) iterator.next();
174: axisTransportProtocols.put(s, MuleTransport
175: .getTransportClass(s));
176: registerSupportedProtocol(s);
177: }
178: muleContext.registerListener(this );
179: } catch (Exception e) {
180: throw new InitialisationException(e, this );
181: }
182: // TODO DO: call registerSupportedProtocol if axisTransportProtocols are set from external?
183:
184: if (clientProvider == null) {
185: clientProvider = createAxisProvider(clientConfig);
186: } else {
187: if (!DEFAULT_MULE_AXIS_CLIENT_CONFIG.equals(clientConfig)) {
188: logger.warn(AxisMessages
189: .clientProviderAndClientConfigConfigured());
190: }
191: }
192:
193: if (axis == null) {
194: if (serverProvider == null) {
195: serverProvider = this .createAxisProvider(serverConfig);
196: } else {
197: if (!DEFAULT_MULE_AXIS_SERVER_CONFIG
198: .equals(serverConfig)) {
199: logger.warn(AxisMessages
200: .serverProviderAndServerConfigConfigured());
201: }
202: }
203:
204: // Create the AxisServer
205: axis = new AxisServer(serverProvider);
206: // principle of least surprise: doAutoTypes only has effect on our self-configured AxisServer
207: axis.setOption("axis.doAutoTypes", Boolean
208: .valueOf(doAutoTypes));
209: }
210:
211: // Register the Mule service serverProvider
212: WSDDProvider.registerProvider(QNAME_MULE_PROVIDER,
213: new WSDDJavaMuleProvider(this ));
214:
215: try {
216: registerTransportTypes();
217: } catch (ClassNotFoundException e) {
218: throw new InitialisationException(CoreMessages
219: .cannotLoadFromClasspath(e.getMessage()), e, this );
220: }
221:
222: // Register all our UrlStreamHandlers here so they can be resolved. This is necessary
223: // to make Mule work in situations where modification of system properties at runtime
224: // is not reliable, e.g. when running in maven's surefire test executor.
225: MuleUrlStreamHandlerFactory
226: .registerHandler(
227: "jms",
228: new org.mule.transport.soap.axis.transport.jms.Handler());
229: MuleUrlStreamHandlerFactory
230: .registerHandler(
231: "pop3",
232: new org.mule.transport.soap.axis.transport.pop3.Handler());
233: MuleUrlStreamHandlerFactory
234: .registerHandler(
235: "smtp",
236: new org.mule.transport.soap.axis.transport.smtp.Handler());
237: MuleUrlStreamHandlerFactory
238: .registerHandler(
239: "vm",
240: new org.mule.transport.soap.axis.transport.vm.Handler());
241:
242: try {
243: registerTypes((TypeMappingRegistryImpl) axis
244: .getTypeMappingRegistry(), beanTypes);
245: } catch (ClassNotFoundException e) {
246: throw new InitialisationException(e, this );
247: }
248: }
249:
250: protected void registerTransportTypes()
251: throws ClassNotFoundException {
252: // Register Transport handlers
253: // By default these will all be handled by Mule, however some companies may
254: // have their own they wish to use
255: for (Iterator iterator = getAxisTransportProtocols().keySet()
256: .iterator(); iterator.hasNext();) {
257: String protocol = (String) iterator.next();
258: Object temp = getAxisTransportProtocols().get(protocol);
259: Class clazz;
260: if (temp instanceof String) {
261: clazz = ClassUtils.loadClass(temp.toString(),
262: getClass());
263: } else {
264: clazz = (Class) temp;
265: }
266: Call.setTransportForProtocol(protocol, clazz);
267: }
268: }
269:
270: protected SimpleProvider createAxisProvider(String config)
271: throws InitialisationException {
272: // Use our custom file provider that does not require services to be declared
273: // in the WSDD. This only affects the
274: // client side as the client will fallback to the FileProvider when invoking
275: // a service.
276: WSDDFileProvider fileProvider = new WSDDFileProvider(config);
277: fileProvider.setSearchClasspath(true);
278: /*
279: * Wrap the FileProvider with a SimpleProvider so we can programmatically
280: * configure the Axis server (you can only use wsdd descriptors with the
281: * FileProvider)
282: */
283: return new MuleConfigProvider(fileProvider);
284: }
285:
286: public String getProtocol() {
287: return AXIS;
288: }
289:
290: /**
291: * The method determines the key used to store the receiver against.
292: *
293: * @param component the component for which the endpoint is being registered
294: * @param endpoint the endpoint being registered for the component
295: * @return the key to store the newly created receiver against. In this case it
296: * is the component name, which is equivalent to the Axis service name.
297: */
298: protected Object getReceiverKey(Service component,
299: InboundEndpoint endpoint) {
300: if (endpoint.getEndpointURI().getPort() == -1) {
301: return component.getName();
302: } else {
303: return endpoint.getEndpointURI().getAddress() + "/"
304: + component.getName();
305: }
306: }
307:
308: protected void unregisterReceiverWithMuleService(
309: MessageReceiver receiver, EndpointURI ep)
310: throws MuleException {
311: String endpointKey = getCounterEndpointKey(receiver
312: .getEndpointURI());
313:
314: for (Iterator iterator = axisComponent.getInboundRouter()
315: .getEndpoints().iterator(); iterator.hasNext();) {
316: ImmutableEndpoint umoEndpoint = (ImmutableEndpoint) iterator
317: .next();
318: if (endpointKey.startsWith(umoEndpoint.getEndpointURI()
319: .getAddress())) {
320: logger.info("Unregistering Axis endpoint: "
321: + endpointKey
322: + " for service: "
323: + ((AxisMessageReceiver) receiver)
324: .getSoapService().getName());
325: }
326: try {
327: umoEndpoint.getConnector().unregisterListener(
328: receiver.getService(), receiver.getEndpoint());
329: } catch (Exception e) {
330: logger.error("Failed to unregister Axis endpoint: "
331: + endpointKey + " for service: "
332: + receiver.getService().getName()
333: + ". Error is: " + e.getMessage(), e);
334: }
335: }
336: }
337:
338: protected void registerReceiverWithMuleService(
339: MessageReceiver receiver, EndpointURI ep)
340: throws MuleException {
341: // If this is the first receiver we need to create the Axis service
342: // component this will be registered with Mule when the Connector starts
343: // See if the axis descriptor has already been added. This allows
344: // developers to override the default configuration, say to increase
345: // the threadpool
346: if (axisComponent == null) {
347: axisComponent = getOrCreateAxisComponent();
348: } else {
349: // Lets unregister the 'template' instance, configure it and
350: // then register again later
351: muleContext.getRegistry().unregisterService(
352: AXIS_SERVICE_PROPERTY + getName());
353: }
354:
355: String serviceName = ((AxisMessageReceiver) receiver)
356: .getSoapService().getName();
357: // No determine if the endpointUri requires a new connector to be
358: // registed in the case of http we only need to register the new endpointUri
359: // if the port is different If we're using VM or Jms we just use the resource
360: // info directly without appending a service name
361: String endpoint;
362: String scheme = ep.getScheme().toLowerCase();
363: if (scheme.equals("jms") || scheme.equals("vm")) {
364: endpoint = ep.toString();
365: } else {
366: endpoint = receiver.getEndpointURI().getAddress() + "/"
367: + serviceName;
368: }
369: if (logger.isDebugEnabled()) {
370: logger.debug("Modified endpoint with " + scheme
371: + " scheme to " + endpoint);
372: }
373:
374: boolean sync = receiver.getEndpoint().isSynchronous();
375:
376: EndpointBuilder serviceEndpointbuilder = new EndpointURIEndpointBuilder(
377: endpoint, muleContext);
378: serviceEndpointbuilder.setSynchronous(sync);
379: serviceEndpointbuilder.setName(ep.getScheme() + ":"
380: + serviceName);
381: // Set the transformers on the endpoint too
382: serviceEndpointbuilder.setTransformers(receiver.getEndpoint()
383: .getTransformers().isEmpty() ? null : receiver
384: .getEndpoint().getTransformers());
385: serviceEndpointbuilder
386: .setResponseTransformers(receiver.getEndpoint()
387: .getResponseTransformers().isEmpty() ? null
388: : receiver.getEndpoint()
389: .getResponseTransformers());
390: // set the filter on the axis endpoint on the real receiver endpoint
391: serviceEndpointbuilder.setFilter(receiver.getEndpoint()
392: .getFilter());
393: // set the Security filter on the axis endpoint on the real receiver
394: // endpoint
395: serviceEndpointbuilder.setSecurityFilter(receiver.getEndpoint()
396: .getSecurityFilter());
397:
398: // TODO Do we really need to modify the existing receiver endpoint? What happens if we don't security,
399: // filters and transformers will get invoked twice?
400: EndpointBuilder receiverEndpointBuilder = new EndpointURIEndpointBuilder(
401: receiver.getEndpoint(), muleContext);
402: // Remove the Axis filter now
403: receiverEndpointBuilder.setFilter(null);
404: // Remove the Axis Receiver Security filter now
405: receiverEndpointBuilder.setSecurityFilter(null);
406:
407: InboundEndpoint serviceEndpoint = muleContext.getRegistry()
408: .lookupEndpointFactory().getInboundEndpoint(
409: serviceEndpointbuilder);
410:
411: InboundEndpoint receiverEndpoint = muleContext.getRegistry()
412: .lookupEndpointFactory().getInboundEndpoint(
413: receiverEndpointBuilder);
414:
415: receiver.setEndpoint(receiverEndpoint);
416:
417: axisComponent.getInboundRouter().addEndpoint(serviceEndpoint);
418: }
419:
420: private String getCounterEndpointKey(EndpointURI endpointURI) {
421: StringBuffer endpointKey = new StringBuffer(64);
422:
423: endpointKey.append(endpointURI.getScheme());
424: endpointKey.append("://");
425: endpointKey.append(endpointURI.getHost());
426: if (endpointURI.getPort() > -1) {
427: endpointKey.append(":");
428: endpointKey.append(endpointURI.getPort());
429: }
430: return endpointKey.toString();
431: }
432:
433: // This initialization could be performed in the initialize() method. Putting it here essentially makes
434: // it a lazy-create/lazy-init
435: // Another option would be to put it in the default-axis-config.xml (MULE-2102) with lazy-init="true"
436: // but that makes us depend on Spring.
437: // Another consideration is how/when this implicit component gets disposed.
438: protected Service getOrCreateAxisComponent() throws MuleException {
439: Service c = muleContext.getRegistry().lookupService(
440: AXIS_SERVICE_PROPERTY + getName());
441:
442: if (c == null) {
443: // TODO MULE-2228 Simplify this API
444: c = new SedaService();
445: c.setName(AXIS_SERVICE_PROPERTY + getName());
446: c.setModel(muleContext.getRegistry().lookupSystemModel());
447:
448: Map props = new HashMap();
449: props.put(AXIS, axis);
450: SingletonObjectFactory of = new SingletonObjectFactory(
451: AxisServiceComponent.class, props);
452: of.initialise();
453: c.setComponent(new DefaultJavaComponent(of));
454: }
455: return c;
456: }
457:
458: /**
459: * Template method to perform any work when starting the connectoe
460: *
461: * @throws org.mule.api.MuleException if the method fails
462: */
463: protected void doStart() throws MuleException {
464: axis.start();
465: }
466:
467: /**
468: * Template method to perform any work when stopping the connectoe
469: *
470: * @throws org.mule.api.MuleException if the method fails
471: */
472: protected void doStop() throws MuleException {
473: axis.stop();
474: // Model model = muleContext.getRegistry().lookupModel();
475: // model.unregisterComponent(model.getDescriptor(AXIS_SERVICE_COMPONENT_NAME));
476: }
477:
478: protected void doConnect() throws Exception {
479: // template method
480: }
481:
482: protected void doDisconnect() throws Exception {
483: // template method
484: }
485:
486: protected void doDispose() {
487: // template method
488: }
489:
490: public String getServerConfig() {
491: return serverConfig;
492: }
493:
494: public void setServerConfig(String serverConfig) {
495: this .serverConfig = serverConfig;
496: }
497:
498: public List getBeanTypes() {
499: return beanTypes;
500: }
501:
502: public void setBeanTypes(List beanTypes) {
503: this .beanTypes = beanTypes;
504: }
505:
506: public String getClientConfig() {
507: return clientConfig;
508: }
509:
510: public void setClientConfig(String clientConfig) {
511: this .clientConfig = clientConfig;
512: }
513:
514: public AxisServer getAxis() {
515: return axis;
516: }
517:
518: public void setAxis(AxisServer axisServer) {
519: this .axis = axisServer;
520: }
521:
522: public SimpleProvider getServerProvider() {
523: return serverProvider;
524: }
525:
526: public void setServerProvider(SimpleProvider serverProvider) {
527: this .serverProvider = serverProvider;
528: }
529:
530: public SimpleProvider getClientProvider() {
531: return clientProvider;
532: }
533:
534: public void setClientProvider(SimpleProvider clientProvider) {
535: this .clientProvider = clientProvider;
536: }
537:
538: public Map getAxisTransportProtocols() {
539: return axisTransportProtocols;
540: }
541:
542: public void setAxisTransportProtocols(Map axisTransportProtocols) {
543: this .axisTransportProtocols.putAll(axisTransportProtocols);
544: }
545:
546: void addServletService(SOAPService service) {
547: servletServices.add(service);
548: }
549:
550: public List getSupportedSchemes() {
551: return supportedSchemes;
552: }
553:
554: public void setSupportedSchemes(List supportedSchemes) {
555: this .supportedSchemes = supportedSchemes;
556: }
557:
558: public boolean isDoAutoTypes() {
559: return doAutoTypes;
560: }
561:
562: public void setDoAutoTypes(boolean doAutoTypes) {
563: this .doAutoTypes = doAutoTypes;
564: }
565:
566: void registerTypes(TypeMappingRegistryImpl registry, List types)
567: throws ClassNotFoundException {
568: if (types != null) {
569: Class clazz;
570: for (Iterator iterator = types.iterator(); iterator
571: .hasNext();) {
572: clazz = ClassUtils.loadClass(
573: iterator.next().toString(), getClass());
574: String localName = Types.getLocalNameFromFullName(clazz
575: .getName());
576: QName xmlType = new QName(Namespaces
577: .makeNamespace(clazz.getName()), localName);
578:
579: registry.getDefaultTypeMapping().register(clazz,
580: xmlType,
581: new BeanSerializerFactory(clazz, xmlType),
582: new BeanDeserializerFactory(clazz, xmlType));
583: }
584: }
585: }
586:
587: public boolean isTreatMapAsNamedParams() {
588: return treatMapAsNamedParams;
589: }
590:
591: public void setTreatMapAsNamedParams(boolean treatMapAsNamedParams) {
592: this .treatMapAsNamedParams = treatMapAsNamedParams;
593: }
594:
595: public void onNotification(ServerNotification notification) {
596: if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED) {
597: // We need to register the Axis service component once the muleContext
598: // starts because when the model starts listeners on components are started, thus
599: // all listener need to be registered for this connector before the Axis service
600: // component is registered.
601: // The implication of this is that to add a new service and a
602: // different http port the model needs to be restarted before the listener is available
603: if (muleContext.getRegistry().lookupService(
604: AXIS_SERVICE_PROPERTY + getName()) == null) {
605: try {
606: // Descriptor might be null if no inbound endpoints have been
607: // register for the Axis connector
608: if (axisComponent == null) {
609: axisComponent = getOrCreateAxisComponent();
610: }
611: muleContext.getRegistry().registerService(
612: axisComponent);
613:
614: // We have to perform a small hack here to rewrite servlet://
615: // endpoints with the
616: // real http:// address
617: for (Iterator iterator = servletServices.iterator(); iterator
618: .hasNext();) {
619: SOAPService service = (SOAPService) iterator
620: .next();
621: ServletConnector servletConnector = (ServletConnector) TransportFactory
622: .getConnectorByProtocol("servlet");
623: String url = servletConnector.getServletUrl();
624: if (url != null) {
625: service
626: .getServiceDescription()
627: .setEndpointURL(
628: url + "/"
629: + service.getName());
630: } else {
631: logger
632: .error("The servletUrl property on the ServletConntector has not been set this means that wsdl generation for service '"
633: + service.getName()
634: + "' may be incorrect");
635: }
636: }
637: servletServices.clear();
638: } catch (MuleException e) {
639: handleException(e);
640: }
641: }
642: }
643: }
644:
645: public boolean isSyncEnabled(String protocol) {
646: protocol = protocol.toLowerCase();
647: if (protocol.equals("http") || protocol.equals("https")
648: || protocol.equals("ssl") || protocol.equals("tcp")
649: || protocol.equals("servlet")) {
650: return true;
651: } else {
652: return super.isSyncEnabled(protocol);
653: }
654: }
655:
656: }
|