View Javadoc

1   /*******************************************************************************
2    *  Copyright (c) 2005, 2006, 2007 Imola Informatica.
3    *  All rights reserved. This program and the accompanying materials
4    *  are made available under the terms of the LGPL License v2.1
5    *  which accompanies this distribution, and is available at
6    *  http://www.gnu.org/licenses/lgpl.html
7    *******************************************************************************/
8   package it.imolinfo.jbi4ejb.webservice.generator;
9   
10  import it.imolinfo.jbi4ejb.Logger;
11  import it.imolinfo.jbi4ejb.LoggerFactory;
12  import it.imolinfo.jbi4ejb.configuration.InterfaceExtractorUtil;
13  import it.imolinfo.jbi4ejb.exception.ClassGenerationException;
14  import it.imolinfo.jbi4ejb.exception.EJBWSDLGenerationException;
15  import it.imolinfo.jbi4ejb.jbi.wsdl.Jbi4EjbAddress;
16  import it.imolinfo.jbi4ejb.jbi.wsdl.Jbi4EjbBinding;
17  import it.imolinfo.jbi4ejb.jbi.wsdl.Jbi4EjbExtension;
18  import it.imolinfo.jbi4ejb.jbi.wsdl.Jbi4EjbTypes;
19  
20  import java.io.File;
21  import java.io.FileOutputStream;
22  import java.io.FileWriter;
23  import java.io.IOException;
24  import java.io.ObjectStreamClass;
25  import java.net.MalformedURLException;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.Set;
33  
34  import javax.wsdl.Binding;
35  import javax.wsdl.BindingFault;
36  import javax.wsdl.BindingInput;
37  import javax.wsdl.BindingOperation;
38  import javax.wsdl.BindingOutput;
39  import javax.wsdl.Definition;
40  import javax.wsdl.Fault;
41  import javax.wsdl.Input;
42  import javax.wsdl.Operation;
43  import javax.wsdl.Output;
44  import javax.wsdl.Port;
45  import javax.wsdl.PortType;
46  import javax.wsdl.WSDLException;
47  import javax.wsdl.extensions.ExtensionRegistry;
48  import javax.wsdl.factory.WSDLFactory;
49  import javax.wsdl.xml.WSDLReader;
50  import javax.wsdl.xml.WSDLWriter;
51  import javax.xml.namespace.QName;
52  
53  import org.codehaus.xfire.aegis.AegisBindingProvider;
54  import org.codehaus.xfire.aegis.type.Configuration;
55  import org.codehaus.xfire.aegis.type.DefaultTypeMappingRegistry;
56  import org.codehaus.xfire.service.Service;
57  import org.codehaus.xfire.service.binding.ObjectServiceFactory;
58  import org.codehaus.xfire.soap.SoapConstants;
59  import org.codehaus.xfire.util.ServiceUtils;
60  import org.codehaus.xfire.wsdl.AbstractWSDL;
61  
62  import com.ibm.wsdl.Constants;
63  import com.ibm.wsdl.factory.WSDLFactoryImpl;
64  
65  /**
66   * Class to generate WSDL file with Jbi4Ejb extensions.
67   * <p>
68   *
69   * @author <a href="mailto:mpiraccini@imolinfo.it">Marco Piraccini</a>
70   */
71  public final class WSDLGenerator {
72      
73      /** Logger. */
74      private static final Logger LOG = LoggerFactory.getLogger(EJBUtils.class);
75  
76      /**
77       * Creates an instance of this class.
78       */
79      private WSDLGenerator() {}
80      
81  
82      /**
83       * Creates the wsdl interface, getting the classes from the jar.
84       * 
85       * @param className
86       *          The interface class name
87       * @param ejbJarPath
88       *          The ejb jar path
89       * @param wsdlFileName
90       *          The WSDL filename (to create)
91       * @param descriptor
92       *          The WSDL descriptor
93       * @param tempDir
94       *          The temporary directory to use
95       * 
96       * @return the WSDL file
97       * 
98       * @throws EJBWSDLGenerationException
99       *              If some problem occurs
100      */
101     @SuppressWarnings( { "deprecation", "unchecked" })
102     public static File createWsdlFromJar(String className, String ejbJarPath,
103                 String wsdlFileName, WSDLDescriptor descriptor, File tempDir)            
104             throws EJBWSDLGenerationException {
105 
106         // Unzip the jar in the temp directory
107         try {
108             JarUtil.unjar(new File(ejbJarPath), tempDir);
109         } catch (IOException e) {
110             // TODO i18n
111             LOG.error(e.getMessage());
112             throw new EJBWSDLGenerationException(e);
113         }
114         
115         // Creates the WSDL
116         return createWsdlFromClassesDirectory(className, tempDir, wsdlFileName, descriptor);
117     }
118     
119     /**
120      * Creates the wsdl interface, getting the classes from a Ear.
121      * 
122      * @param className
123      *            The interface class name
124      * @param earJarPath
125      *            The ear jar path
126      * @param wsdlFileName
127      *            The WSDL filename (to create)
128      * @param descriptor
129      *            The WSDL descriptor
130      * 
131      * @return the WSDL file
132      * 
133      * @throws EJBWSDLGenerationException
134      *             If some problem occurs
135      */
136     @SuppressWarnings( { "deprecation", "unchecked" })
137     public static File createWsdlFromEar(String className, String earJarPath,
138                 String wsdlFileName, WSDLDescriptor descriptor)            
139             throws EJBWSDLGenerationException {
140 
141         // Extract the classes form the ear in a temporary directory
142         String tempDirPath = InterfaceExtractorUtil.extractEarClassesInTempDirectory(earJarPath);
143        
144         // Creates the WSDL
145         return createWsdlFromClassesDirectory(className, new File(tempDirPath), wsdlFileName, descriptor);
146     }        
147     
148 
149     /**
150      * Creates the wsdl interface, getting the classes from a directory.
151      * The classes are copied to the temporary directory to omake all the needed bytecode transformations.
152      * 
153      * @param className
154      *           The interface class name
155      * @param classesDirectory
156      *           The directory where the classes are
157      * @param wsdlFileName
158      *          The WSDL filename (to create)
159      * @param descriptor
160      *          The WSDL descriptor
161      * @param tempDir
162      *          The temporary directory to use
163      * 
164      * @return the WSDL file
165      * 
166      * @throws EJBWSDLGenerationException
167      *              If some problem occurs
168      */
169     @SuppressWarnings( { "deprecation", "unchecked" })
170     public static File createWsdlFromClassesDirectory(String className, File classesDirectory, 
171             String wsdlFileName, WSDLDescriptor descriptor, File tempDir)
172             throws EJBWSDLGenerationException {
173 
174         // From the remote interface, creates the WSDL
175         try {
176             // Copy all the classes in the temporary directory
177             EJBUtils.copyDirectory(classesDirectory, tempDir);
178             
179             // Creates the WSDL
180             return createWsdlFromClassesDirectory(className, tempDir, wsdlFileName, descriptor);
181                   
182         } catch (IOException ioex) {
183             // TODO i18n
184             LOG.error(ioex.getMessage());
185             throw new EJBWSDLGenerationException(ioex);
186         }
187     }    
188     
189 
190     /**
191      * Creates the wsdl from classes directory.
192      * 
193      * @param className
194      *           The interface class name
195      * @param classesDirectory
196      *           The directory where the classes are
197      * @param wsdlFileName
198      * @param wsdlFileName
199      *          The WSDL filename (to create)
200      * @param descriptor
201      *          The WSDL descriptor
202      *          
203      * @return the WSDL file
204      *          
205      * @throws EJBWSDLGenerationException
206      *              If some problem occurs
207      */
208     @SuppressWarnings( { "deprecation", "unchecked" })
209     private  static File createWsdlFromClassesDirectory(String className, File classesDirectory,
210             String wsdlFileName, WSDLDescriptor descriptor)
211             throws EJBWSDLGenerationException {
212 
213         // From the remote interface, creates the WSDL
214         try {
215 
216             // Gets the SUIDs
217             Properties classesIDs = getClassesID(className, classesDirectory);                        
218             
219             // Removes the java.rmi.Remote/javax.ejb.EJBObject interface extension and the java.rmi.RemoteException
220             EJBUtils.removeEJBRemoteInterface(className, classesDirectory.getAbsolutePath());
221                                                         
222             // Get the classes classloader, setting the parent to null        
223             ClassLoader classLoader = null;
224             try {
225                 classLoader = Util.getURLClassLoader(classesDirectory.getAbsolutePath(), null);
226             } catch (MalformedURLException ex) {
227                 // TODO i18n
228                 String msg = ex.getMessage();
229                 LOG.error(msg);
230                 throw new ClassGenerationException(msg);
231             }
232                         
233             LOG.debug("Class name: " + className);
234             
235             // Loads the class using the URL classloader
236             Class remoteInterfaceClass = classLoader.loadClass(className);
237                                                                   
238             return createWsdlFromClass(remoteInterfaceClass, wsdlFileName, descriptor, classesIDs);            
239 
240         } catch (ClassNotFoundException cex) {
241             // TODO i18n
242             LOG.error(cex.getMessage());
243             throw new EJBWSDLGenerationException(cex);
244         } catch (ClassGenerationException cex) {
245             // TODO i18n
246             LOG.error(cex.getMessage());
247             throw new EJBWSDLGenerationException(cex);
248         }
249     }        
250         
251     
252     /**
253      * Creates the wsdl from class name in the tempDir.
254      * 
255      * @param wsdlFileName
256      *            The wsdl file name
257      * @param remoteInterfaceClass
258      *            The remote interface class
259      * @param descriptor
260      *            The WSDL extensions descriptor
261      * @param classesId
262      *            The classes UIDs
263      * @return the file
264      * 
265      * @throws EJBWSDLGenerationException
266      *             If some problems occurs
267      */
268     @SuppressWarnings( { "deprecation", "unchecked" })
269     private static File createWsdlFromClass(Class remoteInterfaceClass,
270             String wsdlFileName,  WSDLDescriptor descriptor, Properties classesId)
271             throws EJBWSDLGenerationException {
272 
273         // From the remote interface, creates the WSDL
274         try {
275 
276             ObjectServiceFactory factory = new ObjectServiceFactory();
277             
278 
279             Map<String, Object> props = new HashMap<String, Object>();
280             
281             // The portTypeName must be the same of the class name and of the service name
282             QName interfaceName = ServiceUtils.makeQualifiedNameFromClass(remoteInterfaceClass);
283             props.put(ObjectServiceFactory.PORT_TYPE, interfaceName);
284            
285             props.put(AbstractWSDL.GENERATE_IMPORTS, "true");
286             props.put(ObjectServiceFactory.STYLE, SoapConstants.STYLE_WRAPPED);
287             props.put(ObjectServiceFactory.USE, SoapConstants.USE_LITERAL);
288             
289             factory.getSoap11Transports().clear();
290             factory.getSoap12Transports().clear();
291             // No binding!!!!! Generates only the abstract part.
292             
293             AegisBindingProvider binder = (AegisBindingProvider)factory.getBindingProvider();
294             DefaultTypeMappingRegistry tmr = (DefaultTypeMappingRegistry)binder.getTypeMappingRegistry();
295             // here we disuade XFire from its rather annoying tendency to assume that, just because
296             // anything in Java can be null, that we want to advertise all that nullity all over.
297             Configuration configuration = tmr.getConfiguration();
298             configuration.setDefaultMinOccurs(1);           
299             configuration.setDefaultNillable(true);            
300 
301             Service service = factory.create(remoteInterfaceClass, interfaceName.getLocalPart(), interfaceName.getNamespaceURI(), props);
302             
303             File wsdlFile = new File(wsdlFileName);
304             wsdlFile.createNewFile();
305 
306             FileOutputStream f = new FileOutputStream(wsdlFile);
307             service.getWSDLWriter().write(f);                        
308             
309             addJbi4EjbExtensionsToWSDL(wsdlFile, descriptor, classesId, service);
310             
311             // hack the wsdl (subs minoccurs=0 to minoccurs=1).
312             // hackWsdl(wsdlFile);
313             
314             return wsdlFile;
315         } catch (MalformedURLException mex) {
316             // TODO i18n
317             LOG.error(mex.getMessage());
318             throw new EJBWSDLGenerationException(mex);
319         }  catch (IOException ioex) {
320             // TODO i18n
321             LOG.error(ioex.getMessage());
322             throw new EJBWSDLGenerationException(ioex);
323         } catch (WSDLException ex) {
324             // TODO i18n
325             LOG.error(ex.getMessage());
326             throw new EJBWSDLGenerationException(ex);
327         }
328     }          
329     
330 
331     
332     /**
333      * Adds jbi4ejb extensions to wsdl. This code expects that the PortTypeName
334      * is
335      * 
336      * @param wsdlDescriptor
337      *            The wsdl descriptor
338      * @param wsdlFile
339      *            The WSDL file
340      * @param xfireService
341      *            The xfire service
342      * @param classesId
343      *            The classes UIDs
344      * @throws EJBWSDLGenerationException
345      *             if something go wrong
346      * @throws WSDLException
347      *             if there are problem in WSDL resding/writing
348      */
349     @SuppressWarnings( { "deprecation", "unchecked" })
350     private static void addJbi4EjbExtensionsToWSDL(File wsdlFile, WSDLDescriptor wsdlDescriptor, Properties classesId, Service xfireService)
351             throws EJBWSDLGenerationException, WSDLException {
352         
353         // Creates the address extensibility elements
354         Jbi4EjbAddress ejbAddress = new Jbi4EjbAddress();
355         ejbAddress.setName(wsdlDescriptor.getName());
356         ejbAddress.setLocalizationType(wsdlDescriptor.getLocalizationType());
357         ejbAddress.setElementType(Jbi4EjbExtension.Q_ELEM_JBI4EJB_ADDRESS);
358         
359         // Creates the binding extensibility elements
360         Jbi4EjbBinding ejbBinding = new Jbi4EjbBinding();
361         if (wsdlDescriptor.isCorbaName()) {
362             ejbBinding.setOrbProperties(wsdlDescriptor.getOrbProperties());
363         } else {
364             // JNDI
365             ejbBinding.setJndiProperties(wsdlDescriptor.getJndiProperties());
366         }
367         ejbBinding.setElementType(Jbi4EjbExtension.Q_ELEM_JBI4EJB_BINDING);
368         
369         // Creates the types extensibility elements
370         Jbi4EjbTypes ejbTypes = new Jbi4EjbTypes();
371         ejbTypes.setTypesSerialVersionUIDs(classesId);
372         ejbTypes.setElementType(Jbi4EjbExtension.Q_ELEM_JBI4EJB_TYPES);
373 
374         Definition wsdl = readWsdl(wsdlFile);               
375         
376         // Adds the WSDL extensions
377         wsdl.addNamespace(Jbi4EjbExtension.DEFAULT_PREFIX,
378                 Jbi4EjbExtension.NS_URI_JBI4EJB);
379         
380         javax.wsdl.Service service = wsdl.getService(xfireService.getName());
381         
382         // The port name is the name of the service + "Port"
383         String portName = service.getQName().getLocalPart() + "Port";
384         Port port = wsdl.createPort();
385         port.setName(portName);
386         service.addPort(port);                
387 
388         // The binding name is the name of the service + "Binding"
389         String bindingName = service.getQName().getLocalPart() + "Binding";
390         Binding binding = wsdl.createBinding();
391         wsdl.addBinding(binding);
392                 
393         QName bindingQName = new QName(service.getQName().getNamespaceURI(), bindingName);
394         binding.setQName(bindingQName);
395         port.setBinding(binding);
396                                                  
397         // Gets the portType reference        
398         QName portTypeQName = xfireService.getServiceInfo().getPortType();
399         PortType portType = wsdl.getPortType(portTypeQName);
400         if (portType == null) {
401             // TODO i18n
402             String msg = "No PortType found wth name: " + portTypeQName;
403             LOG.error(msg);
404             throw new EJBWSDLGenerationException(msg);
405         }
406         
407         binding.setPortType(portType);        
408         // Add the bindingOperations (with input,output and faults)
409         List<Operation>  operations = portType.getOperations();
410         for (Operation operation: operations) {            
411             BindingOperation bop = wsdl.createBindingOperation();
412             bop.setName(operation.getName());
413             Input input = operation.getInput();
414             if (input != null) {
415                 BindingInput bindingInput = wsdl.createBindingInput();
416                 bindingInput.setName(input.getName());
417                 bop.setBindingInput(bindingInput);
418             }
419             Output output = operation.getOutput();
420             if (output != null) {
421                 BindingOutput bindingOutput = wsdl.createBindingOutput();
422                 bindingOutput.setName(output.getName());
423                 bop.setBindingOutput(bindingOutput);
424             }
425                         
426             Map faults = operation.getFaults();
427             Iterator faultIt = faults.entrySet().iterator();
428             while (faultIt.hasNext()) {                
429                 Fault fault = (Fault)((Map.Entry)faultIt.next()).getValue();                
430                 BindingFault bindingFault = wsdl.createBindingFault();
431                 bindingFault.setName(fault.getName());
432                 bop.addBindingFault(bindingFault);
433             }
434             binding.addBindingOperation(bop);                
435         }        
436         
437         binding.setUndefined(false);
438         
439         // Adds the ejb extensibility elements
440         port.addExtensibilityElement(ejbAddress);
441         binding.addExtensibilityElement(ejbBinding);
442         wsdl.addExtensibilityElement(ejbTypes);
443         
444         // Deletes the previous file 
445         wsdlFile.delete();        
446         
447         try {
448             writeWsdl(wsdl, wsdlFile);
449         } catch (IOException e) {
450             // TODO i18n
451             String msg = "Error in writing definition on file system";
452             LOG.error(msg);
453             throw new EJBWSDLGenerationException(msg);
454         }
455        
456     }    
457     
458     /**
459      * Reads a <code>Definition</code> from a <code>File</code>.
460      * @param f the file to read
461      * @return the WSDL definition
462      * @throws javax.wsdl.WSDLException if there are problem in reading the WSDL
463      */
464     private static Definition readWsdl(final File f) throws WSDLException {
465         final WSDLFactory wsdlFactory = WSDLFactory.newInstance();
466         ExtensionRegistry registry = wsdlFactory
467         .newPopulatedExtensionRegistry();        
468         final WSDLReader reader = ((WSDLFactoryImpl) wsdlFactory)
469         .newWSDLReader();
470         reader.setFeature(Constants.FEATURE_VERBOSE, false);
471         reader.setFeature(Constants.FEATURE_IMPORT_DOCUMENTS, true);
472         Jbi4EjbExtension.register(registry);
473         LOG.debug("Extension QName: " + Jbi4EjbExtension.NS_URI_JBI4EJB);
474         reader.setExtensionRegistry(registry);
475         final Definition def = reader.readWSDL(f.getAbsolutePath());
476         return def;
477     }  
478     
479     /**
480      * Reads a <code>Definition</code> from a <code>File</code>.
481      * 
482      * @param wsdlFile
483      *            the file to read
484      * @param wsdl
485      *            the WSDL definition to write
486      * 
487      * @throws javax.wsdl.WSDLException
488      *             if there are problem in reading the WSDL
489      * @throws IOException
490      *             if somethong go wrong in wile writing
491      * @throws WSDLException
492      *             if something go wrong in WSDL writing
493      */
494     private static void writeWsdl(Definition wsdl, final File wsdlFile) throws WSDLException, IOException {
495         final WSDLFactory wsdlFactory = WSDLFactory.newInstance();
496         
497         ExtensionRegistry registry = wsdlFactory
498             .newPopulatedExtensionRegistry();
499         Jbi4EjbExtension.register(registry);
500         wsdl.setExtensionRegistry(registry);
501         final WSDLWriter writer = wsdlFactory.newWSDLWriter();        
502         writer.writeWSDL(wsdl, new FileWriter(wsdlFile));        
503     }     
504     
505 
506     /**
507      * Gets the classes ID.
508      * 
509      * @param remoteInterface
510      *              The remote interface
511      * @param classesDir
512      *              Where the classes are
513      * @return the classes ID
514      * 
515      * @throws EJBWSDLGenerationException
516      *              If some problem occurs
517      */
518     @SuppressWarnings("unchecked")
519     public static Properties getClassesID(String remoteInterface, File classesDir) throws EJBWSDLGenerationException {
520         
521         LOG.debug("looking for " + remoteInterface + " in directory: " + classesDir.getAbsolutePath());
522         
523         Properties classesID = new Properties();
524 
525         String remoteInterfaceFileName = remoteInterface.replace('.',File.separatorChar);
526 
527         LOG.debug("remoteInterfaceFileName: " + remoteInterfaceFileName);
528         List<File> list = new ArrayList<File>();
529         list.add(new File(classesDir.getAbsolutePath() + File.separatorChar + remoteInterfaceFileName));
530 
531         // Find the classes used by the remote interface (with recursion).
532         Set<Class> classesToSerialize;
533         try {
534             classesToSerialize = Util.findClassUsed(classesDir.getAbsolutePath(), list);
535         } catch (ClassGenerationException e) {
536             // TODO i18n
537             LOG.error(e.getMessage());
538             throw new EJBWSDLGenerationException(e);
539         }
540 
541         Iterator iter = classesToSerialize.iterator();
542         while (iter.hasNext()) {
543             Class classToSerialize = (Class)iter.next();
544             LOG.debug("Looking for class: " + classToSerialize.getName());
545                         
546             ObjectStreamClass objectStreamClass = ObjectStreamClass.lookup(classToSerialize);
547             
548             if (objectStreamClass == null) {
549                 // TOSO i18n
550                 LOG.warn("The objectStreamClass is null for class: " + classToSerialize.getName()+ 
551                         " the class is Serializable?");
552                 
553             } 
554             if (objectStreamClass != null) {
555 
556                 long uid = objectStreamClass.getSerialVersionUID();
557 
558                 /*
559                 // With com.sun.corba.se.impl.io.ObjectStreamClass;
560                 long uid = ObjectStreamClass.getSerialVersionUID(classToSerialize);
561     
562                 if (uid == 0L) {
563                     uid = ObjectStreamClass.getActualSerialVersionUID(classToSerialize);
564                 }
565                  */            
566 
567                 LOG.debug(classToSerialize.getName() + " uid: " + uid);                
568                 classesID.put(classToSerialize.getName() , Long.valueOf(uid));
569             }
570         }            
571 
572         return classesID;        
573     }           
574         
575 }