001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999-2005 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: JAxisServiceFactory.java 6860 2005-05-27 15:01:25Z sauthieg $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas.ws.axis;
025:
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.io.StringWriter;
029: import java.lang.reflect.Constructor;
030: import java.lang.reflect.Proxy;
031: import java.net.URL;
032: import java.util.Hashtable;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036: import java.util.Properties;
037: import java.util.StringTokenizer;
038:
039: import javax.naming.BinaryRefAddr;
040: import javax.naming.Context;
041: import javax.naming.InitialContext;
042: import javax.naming.Name;
043: import javax.naming.NamingException;
044: import javax.naming.RefAddr;
045: import javax.naming.Reference;
046: import javax.naming.StringRefAddr;
047: import javax.wsdl.Definition;
048: import javax.wsdl.Port;
049: import javax.wsdl.Service;
050: import javax.wsdl.extensions.ExtensibilityElement;
051: import javax.wsdl.extensions.soap.SOAPAddress;
052: import javax.wsdl.factory.WSDLFactory;
053: import javax.wsdl.xml.WSDLReader;
054: import javax.xml.namespace.QName;
055:
056: import org.w3c.dom.Document;
057: import org.w3c.dom.Element;
058: import org.w3c.dom.NodeList;
059:
060: import org.apache.axis.EngineConfiguration;
061: import org.apache.axis.client.AxisClient;
062: import org.apache.axis.configuration.XMLStringProvider;
063: import org.apache.axis.deployment.wsdd.WSDDConstants;
064: import org.apache.axis.deployment.wsdd.WSDDProvider;
065: import org.apache.axis.utils.XMLUtils;
066: import org.apache.axis.wsdl.toJava.Utils;
067:
068: import org.objectweb.jonas_lib.I18n;
069: import org.objectweb.jonas_lib.xml.XMLSerializer;
070:
071: import org.objectweb.jonas_ws.deployment.api.MappingFile;
072: import org.objectweb.jonas_ws.deployment.api.PortComponentDesc;
073: import org.objectweb.jonas_ws.deployment.api.PortComponentRefDesc;
074: import org.objectweb.jonas_ws.deployment.api.ServiceRefDesc;
075:
076: import org.objectweb.jonas.common.JNDIUtils;
077: import org.objectweb.jonas.common.Log;
078: import org.objectweb.jonas.ws.JServiceFactory;
079: import org.objectweb.jonas.ws.WSServiceException;
080:
081: import org.objectweb.util.monolog.api.BasicLevel;
082: import org.objectweb.util.monolog.api.Logger;
083:
084: /**
085: * Axis specific JServiceFactory. Create an Axis Service Reference. Used to
086: * recreate Axis Service bound in JNDI (ObjectFactory)
087: * @author Guillaume Sauthier
088: * @author Xavier Delplanque
089: */
090: public class JAxisServiceFactory implements JServiceFactory {
091:
092: /** logger */
093: private static Logger logger = Log.getLogger(Log.JONAS_WS_PREFIX);
094:
095: /** i18n instance */
096: private static I18n i18n = I18n
097: .getInstance(JAxisServiceFactory.class);
098:
099: /** The jonas-init-param name for client configuration file */
100: private static final String AXIS_CLIENT_CONFIG_PARAM = "axis.clientConfigFile";
101:
102: /** default client configuration file to be filled */
103: private static final String CLIENT_CONFIG_WSDD = "org/objectweb/jonas/ws/axis/client-config.wsdd";
104:
105: /** Service Implementation in JOnAS */
106: private static final String JONAS_SERVICE_CLASSNAME = "org.objectweb.jonas.ws.axis.JService";
107:
108: // ======================================================================
109:
110: /**
111: * Ref name for client-config wsdd
112: */
113: public static final String REF_CLIENT_CONFIG = "client.config.wsdd";
114:
115: /**
116: * Ref name for the service WSDL URL
117: */
118: public static final String REF_SERVICE_WSDL = "service.wsdl.url";
119:
120: /**
121: * Ref name for the service QName
122: */
123: public static final String REF_SERVICE_QNAME = "service.qname";
124:
125: /**
126: * Ref name for a Map linking service-endpoint-interface (from port-component) to wsdl:port
127: */
128: public static final String REF_SERVICE_PORT2WSDL = "port.2.wsdl.map";
129:
130: /**
131: * Ref name for the service call properties
132: */
133: public static final String REF_SERVICE_CALL_PROPS = "service.call.properties";
134:
135: /**
136: * Ref name for the service call properties
137: */
138: public static final String REF_SERVICE_STUB_PROPS = "service.stub.properties";
139:
140: /**
141: * Ref name for the service port list
142: */
143: public static final String REF_SERVICE_WSDL_PORT_LIST = "service.port.list";
144:
145: /**
146: * setEndpointAddress method signature
147: */
148: private static final Class[] SETENDPOINTADDRESS_SIG = new Class[] {
149: java.lang.String.class, java.lang.String.class };
150:
151: // ======================================================================
152:
153: /**
154: * Construct a new JAxisServiceFactory.
155: */
156: public JAxisServiceFactory() {
157: QName javaURI = new QName(WSDDConstants.URI_WSDD_JAVA,
158: WSDDNoopProvider.PROVIDER_NAME);
159: WSDDProvider.registerProvider(javaURI, new WSDDNoopProvider());
160: }
161:
162: /**
163: * Create a Reference on a Service from an Axis Service object.
164: * @param sr the service-ref instance where informations are extracted
165: * @param cl the classloader where config files can be loaded
166: * @return a Reference on a Service from an Axis Service object.
167: * @throws WSServiceException if wsdd cannot be load or merged
168: */
169: public Reference getServiceReference(ServiceRefDesc sr,
170: ClassLoader cl) throws WSServiceException {
171:
172: String classname = createServiceClassname(sr);
173:
174: logger.log(BasicLevel.DEBUG, "Service classname: '" + classname
175: + "'");
176:
177: // Create a reference on the Service class (generated or not)
178: Reference ref = new Reference(classname, getClass().getName(),
179: null);
180:
181: Document base = loadAxisDeployment(CLIENT_CONFIG_WSDD, cl);
182:
183: // get the WSDD configuration
184: String clientConfig = sr.getParam(AXIS_CLIENT_CONFIG_PARAM);
185:
186: // If ServiceRef has a specified ConfigFile
187: if (clientConfig != null) {
188: // load wsdd
189: Document doc = loadAxisDeployment(clientConfig, cl);
190:
191: // merge
192: mergeAxisDeployment(base, doc);
193: }
194:
195: // 1. client.wsdd
196: String str = null;
197: try {
198: str = serializeDOM(base);
199: if (logger.isLoggable(BasicLevel.DEBUG)) {
200: logger.log(BasicLevel.DEBUG,
201: "Client Descriptor file : \n" + str);
202: }
203: } catch (IOException ioe) {
204: throw new WSServiceException("Cannot serialize Document",
205: ioe);
206: }
207: ref.add(new StringRefAddr(REF_CLIENT_CONFIG, str));
208:
209: //2. manage port-component-link
210: for (Iterator i = sr.getPortComponentRefs().iterator(); i
211: .hasNext();) {
212: PortComponentRefDesc pcr = (PortComponentRefDesc) i.next();
213: PortComponentDesc referencedPortComponent = pcr
214: .getPortComponentDesc();
215:
216: // port has a link on a local port
217: if (referencedPortComponent != null) {
218:
219: logger.log(BasicLevel.DEBUG,
220: "Find a port-component-link in port-component-ref"
221: + pcr.getSEI());
222: // Get the Service WSDL URL !!!
223: URL url = referencedPortComponent.getEndpointURL();
224: if (url == null) {
225: // Component outside JOnAS server
226: // lookup endpoint URL value
227: try {
228: Context ic = new InitialContext();
229: url = (URL) ic.lookup(referencedPortComponent
230: .getName());
231: } catch (NamingException ne) {
232: throw new WSServiceException(
233: "Cannot find updated endpoint for port-component '"
234: + referencedPortComponent
235: .getName() + "'", ne);
236: }
237: }
238:
239: logger.log(BasicLevel.DEBUG, "Uptodate URL : '" + url
240: + "?JWSDL' ");
241: ref.add(new StringRefAddr(REF_SERVICE_WSDL, url
242: .toExternalForm()
243: + "?JWSDL"));
244: }
245: }
246:
247: // 3. service wsdl URL
248: if (sr.getAlternateWsdlURL() != null) {
249: logger.log(BasicLevel.DEBUG, "Using alternate WSDL URL : '"
250: + sr.getAlternateWsdlURL() + "'");
251: ref.add(new StringRefAddr(REF_SERVICE_WSDL, sr
252: .getAlternateWsdlURL().toString()));
253: } else if (sr.getLocalWSDLURL() != null) {
254: logger.log(BasicLevel.DEBUG, "Using WSDL URL : '"
255: + sr.getLocalWSDLURL() + "'");
256: ref.add(new StringRefAddr(REF_SERVICE_WSDL, sr
257: .getLocalWSDLURL().toExternalForm()));
258: }
259:
260: // 4. Service QName
261: if (sr.getServiceQName() != null) {
262: ref.add(new BinaryRefAddr(REF_SERVICE_QNAME, JNDIUtils
263: .getBytesFromObject(sr.getServiceQName())));
264: }
265:
266: // 5. classname -> wsdl:port map
267: List ports = sr.getPortComponentRefs();
268: if (!ports.isEmpty()) {
269: Map map = new Hashtable();
270: for (Iterator i = ports.iterator(); i.hasNext();) {
271: PortComponentRefDesc pcrd = (PortComponentRefDesc) i
272: .next();
273: QName wsdlPort = pcrd.getWsdlPort();
274: if (wsdlPort != null) {
275: map.put(pcrd.getSEI().getName(), wsdlPort);
276: }
277: }
278: if (!map.isEmpty()) {
279: ref.add(new BinaryRefAddr(REF_SERVICE_PORT2WSDL,
280: JNDIUtils.getBytesFromObject(map)));
281: }
282: }
283:
284: // 6. store call & stubs properties
285: String portNames = null;
286: for (Iterator i = sr.getPortComponentRefs().iterator(); i
287: .hasNext();) {
288: PortComponentRefDesc pcr = (PortComponentRefDesc) i.next();
289: Properties cProps = pcr.getCallProperties();
290: Properties sProps = pcr.getStubProperties();
291:
292: if (pcr.getWsdlPort() != null) {
293: // use call & stub property only if there is a wsdl:port for applying to
294: // TODO must be something to add into documentation for this
295: String name = pcr.getWsdlPort().getLocalPart();
296: if (!cProps.isEmpty()) {
297: ref.add(new BinaryRefAddr(REF_SERVICE_CALL_PROPS
298: + "_" + name, JNDIUtils
299: .getBytesFromObject(cProps)));
300: }
301: if (!sProps.isEmpty()) {
302: ref.add(new BinaryRefAddr(REF_SERVICE_STUB_PROPS
303: + "_" + name, JNDIUtils
304: .getBytesFromObject(sProps)));
305: }
306: if (!sProps.isEmpty() || !cProps.isEmpty()) {
307: if (portNames != null) {
308: // only the first addition does not need a comma
309: portNames += "," + name;
310: } else {
311: portNames = name;
312: }
313: }
314: }
315: }
316: if (portNames != null) {
317: ref.add(new StringRefAddr(REF_SERVICE_WSDL_PORT_LIST,
318: portNames));
319: }
320:
321: return ref;
322: }
323:
324: /**
325: * @param base the Document to serialize
326: * @return Returns a String representation of the given XML Document
327: * @throws IOException when Serializer cannot proceed
328: */
329: private String serializeDOM(Document base) throws IOException {
330: StringWriter sw = new StringWriter();
331: XMLSerializer ser = new XMLSerializer(base);
332: ser.serialize(sw);
333: return sw.getBuffer().toString();
334: }
335:
336: /**
337: * @param base father Document
338: * @param doc Document that will be incorpored inside base Document
339: */
340: private void mergeAxisDeployment(Document base, Document doc) {
341: Element importedDeploymentElement = (Element) base.importNode(
342: doc.getDocumentElement(), true);
343: NodeList list = importedDeploymentElement.getChildNodes();
344: // append each Element childnodes
345: for (int i = 0; i < list.getLength(); i++) {
346: if (list.item(i) instanceof Element) {
347: base.getDocumentElement().appendChild(list.item(i));
348: }
349: }
350: }
351:
352: /**
353: * Create the Axis Service Implementation class name from the QName (if
354: * generated interface found).
355: * TODO Use jaxrpc mapping information if available !
356: * @param sr the service-ref storing service informations
357: * @return the fully qualified Service implementation classname.
358: */
359: private String createServiceClassname(ServiceRefDesc sr) {
360:
361: String intfName = sr.getServiceInterface().getName();
362:
363: if (intfName.equals("javax.xml.rpc.Service")) {
364: // no generated class
365: return JONAS_SERVICE_CLASSNAME;
366: } else {
367: // generated class
368: QName qn = sr.getServiceQName();
369: MappingFile mf = sr.getMappingFile();
370:
371: String p = (String) mf.getMappings().get(
372: qn.getNamespaceURI());
373:
374: String classname = "";
375: if (p != null) {
376: classname = p + "."
377: + Utils.xmlNameToJavaClass(qn.getLocalPart())
378: + "Locator";
379: } else {
380: classname = Utils.xmlNameToJavaClass(qn.getLocalPart())
381: + "Locator";
382: }
383:
384: return classname;
385: }
386:
387: }
388:
389: /**
390: * Load the Axis Deployment Descriptor from the ClassLoader.
391: * @param filename the resource name to load
392: * @param cl The ClassLoader used to load the WSDD
393: * @return the Document Axis Deployment Descriptor from the ClassLoader.
394: * @throws WSServiceException if resource could not be found or if XML
395: * Document cannot be created
396: */
397: private Document loadAxisDeployment(String filename, ClassLoader cl)
398: throws WSServiceException {
399:
400: InputStream is = cl.getResourceAsStream(filename);
401:
402: //logger.log(BasicLevel.DEBUG, "classloader : " + cl);
403:
404: if (is == null) {
405: // exception !
406: String err = i18n
407: .getMessage(
408: "JAxisServiceFactory.loadAxisDeployment.configNotFound",
409: filename);
410: logger.log(BasicLevel.ERROR, err);
411: throw new WSServiceException(err);
412: }
413:
414: Document doc = null;
415: try {
416: doc = XMLUtils.newDocument(is);
417: } catch (Exception e) {
418: String err = i18n
419: .getMessage(
420: "JAxisServiceFactory.loadAxisDeployment.docCreation",
421: filename);
422: logger.log(BasicLevel.DEBUG, err);
423:
424: throw new WSServiceException(err, e);
425: }
426:
427: return doc;
428: }
429:
430: /**
431: * Create a new instance of Axis Service (or sub classes) from a Reference
432: * object.
433: * @param refObject the Reference to create an instance from
434: * @param name Object Name
435: * @param nameCtx Context
436: * @param env the environnment
437: * @return A Service instance configured
438: * @throws Exception when instanciation fail (check if this is a correct
439: * behavior !!!)
440: */
441: public Object getObjectInstance(Object refObject, Name name,
442: Context nameCtx, Hashtable env) throws Exception {
443:
444: JService instance = null;
445: Object proxy = null;
446:
447: if (refObject instanceof Reference) {
448: Reference ref = (Reference) refObject;
449:
450: // get ClassLoader
451: ClassLoader cl = Thread.currentThread()
452: .getContextClassLoader();
453: logger.log(BasicLevel.DEBUG, "Context ClassLoader : " + cl);
454:
455: // get Class
456: Class serviceClass = cl.loadClass(ref.getClassName());
457:
458: // new Service
459: BinaryRefAddr bRefQname = (BinaryRefAddr) ref
460: .get(REF_SERVICE_QNAME);
461:
462: QName serviceQname = null;
463: if (bRefQname != null) {
464: serviceQname = (QName) JNDIUtils
465: .getObjectFromBytes((byte[]) bRefQname
466: .getContent());
467: }
468:
469: RefAddr refServiceWSDL = ref.get(REF_SERVICE_WSDL);
470:
471: String serviceWsdl = null;
472: if (refServiceWSDL != null) {
473: serviceWsdl = (String) refServiceWSDL.getContent();
474: }
475:
476: // Prefill as much as we can
477: if (JONAS_SERVICE_CLASSNAME.equals(serviceClass.getName())) {
478:
479: // default service class
480: logger.log(BasicLevel.DEBUG, "default service class");
481:
482: if ((serviceQname == null) && (serviceWsdl == null)) {
483: logger
484: .log(BasicLevel.DEBUG,
485: "Create a new Service instance without params");
486: // no param instance
487: instance = new JService();
488:
489: } else if ((serviceQname != null)
490: && (serviceWsdl == null)) {
491: logger.log(BasicLevel.DEBUG,
492: "Create a new Service instance with only a QName "
493: + serviceQname);
494: // serviceQname only
495: instance = new JService(serviceQname);
496:
497: } else if ((serviceQname != null)
498: && (serviceWsdl != null)) {
499: logger.log(BasicLevel.DEBUG,
500: "Create a new Service instance with QName "
501: + serviceQname + "and WSDL "
502: + serviceWsdl);
503: // serviceQname + wsdl
504: instance = new JService(serviceWsdl, serviceQname);
505:
506: } else {
507: logger.log(BasicLevel.DEBUG,
508: "Create a new Service instance with WSDL "
509: + serviceWsdl);
510: logger.log(BasicLevel.DEBUG,
511: "Should not occurs !!!");
512: // wsdl
513: // serviceWsdl, updatedURLs, serviceQname);
514: instance = new JService();
515: }
516:
517: } else {
518: logger.log(BasicLevel.DEBUG,
519: "Create a new Generated Service instance");
520: logger
521: .log(BasicLevel.DEBUG, "serviceWSDL:"
522: + serviceWsdl + " serviceQName:"
523: + serviceQname);
524:
525: // generated class
526: Constructor ctr = serviceClass
527: .getConstructor(new Class[] { String.class,
528: QName.class });
529: instance = (JService) ctr.newInstance(new Object[] {
530: serviceWsdl, serviceQname });
531:
532: // we have to update ports endpoints
533: // we should have Service WSDL
534: // otherwise, no Generated WSDL !
535: if (serviceWsdl != null) {
536: WSDLFactory factory = WSDLFactory.newInstance();
537: WSDLReader reader = factory.newWSDLReader();
538: reader.setFeature("javax.wsdl.importDocuments",
539: true);
540: Definition def = reader.readWSDL(serviceWsdl);
541:
542: Service service = def.getService(serviceQname);
543: Map ports = service.getPorts();
544: java.lang.reflect.Method m = serviceClass
545: .getMethod("setEndpointAddress",
546: SETENDPOINTADDRESS_SIG);
547: for (Iterator i = ports.keySet().iterator(); i
548: .hasNext();) {
549: String portName = (String) i.next();
550: Port port = service.getPort(portName);
551: String endpoint = getSOAPLocation(port);
552: m.invoke(instance, new Object[] {
553: port.getName(), endpoint });
554: }
555: }
556: }
557:
558: // get port2wsdl map
559: BinaryRefAddr bRefp2w = (BinaryRefAddr) ref
560: .get(REF_SERVICE_PORT2WSDL);
561:
562: if (bRefp2w != null) {
563: Map map = (Map) JNDIUtils
564: .getObjectFromBytes((byte[]) bRefp2w
565: .getContent());
566: instance.assignSEIClassnameToWSDLPort(map);
567: }
568:
569: // get WSDL_PORT_NAMES
570: RefAddr portsRef = ref.get(REF_SERVICE_WSDL_PORT_LIST);
571: String listPorts = null;
572: if (portsRef != null) {
573: listPorts = (String) portsRef.getContent();
574:
575: if (listPorts != null) {
576: // get call properties
577: StringTokenizer strPort = new StringTokenizer(
578: listPorts, ",");
579: while (strPort.hasMoreTokens()) {
580: String port = strPort.nextToken();
581:
582: // call properties
583: BinaryRefAddr bRefcp = (BinaryRefAddr) ref
584: .get(REF_SERVICE_CALL_PROPS + "_"
585: + port);
586:
587: if (bRefcp != null) {
588: Properties callProperties = (Properties) JNDIUtils
589: .getObjectFromBytes((byte[]) bRefcp
590: .getContent());
591: instance.assignCallProperties(port,
592: callProperties);
593: }
594:
595: // stub properties
596: BinaryRefAddr bRefsp = (BinaryRefAddr) ref
597: .get(REF_SERVICE_STUB_PROPS + "_"
598: + port);
599:
600: if (bRefsp != null) {
601: Properties stubProperties = (Properties) JNDIUtils
602: .getObjectFromBytes((byte[]) bRefsp
603: .getContent());
604: instance.assignStubProperties(port,
605: stubProperties);
606: }
607: }
608: }
609: }
610: EngineConfiguration ec = getConfiguration(ref);
611:
612: // configure the instance
613: instance.setEngine(new AxisClient(ec));
614:
615: JServiceProxy handler = new JServiceProxy(instance);
616: Class[] serviceInterfaces = serviceClass.getInterfaces();
617: Class[] interfaces = new Class[serviceInterfaces.length + 1];
618: for (int i = 0; i < serviceInterfaces.length; i++) {
619: interfaces[i] = serviceInterfaces[i];
620: }
621: interfaces[serviceInterfaces.length] = javax.xml.rpc.Service.class;
622: proxy = Proxy.newProxyInstance(cl, interfaces, handler);
623:
624: }
625:
626: return proxy;
627: }
628:
629: /**
630: * @param port analyzed port
631: * @return Returns the endpoint URL of the given Port
632: */
633: private String getSOAPLocation(Port port) {
634: String endpoint = null;
635: List extensions = port.getExtensibilityElements();
636: for (Iterator i = extensions.iterator(); i.hasNext();) {
637: ExtensibilityElement ext = (ExtensibilityElement) i.next();
638: if (ext instanceof SOAPAddress) {
639: SOAPAddress addr = (SOAPAddress) ext;
640: endpoint = addr.getLocationURI();
641: }
642: }
643: return endpoint;
644: }
645:
646: /**
647: * Return the EngineConfiguration object found in the Reference
648: * @param ref The reference to an Axis Service instance
649: * @return the EngineConfiguration object found in the Reference
650: * @throws Exception when content of reference is not a valid WSDD
651: */
652: private EngineConfiguration getConfiguration(Reference ref)
653: throws Exception {
654: // create a Configuration Object
655: String conf = (String) ref.get(REF_CLIENT_CONFIG).getContent();
656: logger.log(BasicLevel.DEBUG, "loaded configuration : " + conf);
657: return new XMLStringProvider(conf);
658: }
659:
660: }
|