View Javadoc

1   /*
2    *  Copyright (c) 2005, 2006 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   
9   
10  package it.imolinfo.jbi4cics.webservices.runtime;
11  
12  import it.imolinfo.jbi4cics.Logger;
13  import it.imolinfo.jbi4cics.LoggerFactory;
14  import it.imolinfo.jbi4cics.connection.jca.cics.CICSInteractionDescription;
15  import it.imolinfo.jbi4cics.exception.Jbi4cicsException;
16  import it.imolinfo.jbi4cics.exception.LocationException;
17  import it.imolinfo.jbi4cics.jbi.BCELClassLoader;
18  import it.imolinfo.jbi4cics.jbi.wsdl.Jbi4CicsAddress;
19  import it.imolinfo.jbi4cics.jbi.wsdl.Jbi4CicsBinding;
20  import it.imolinfo.jbi4cics.jbi.wsdl.Jbi4CicsExtension;
21  import it.imolinfo.jbi4cics.jbi.xfire.JbiTransport;
22  import it.imolinfo.jbi4cics.locator.SimpleLocation;
23  import it.imolinfo.jbi4cics.security.Account;
24  import it.imolinfo.jbi4cics.webservices.descriptor.ServiceDescriptor;
25  import it.imolinfo.jbi4cics.webservices.utils.generators.ServiceBeanGenerator;
26  import it.imolinfo.jbi4cics.webservices.utils.generators.ServiceInterfaceGenerator;
27  import java.io.ByteArrayInputStream;
28  import java.io.ByteArrayOutputStream;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.util.HashMap;
32  import java.util.Map;
33  import javax.jbi.component.ComponentContext;
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.Operation;
41  import javax.wsdl.Port;
42  import javax.wsdl.WSDLException;
43  import javax.wsdl.extensions.ExtensionRegistry;
44  import javax.wsdl.factory.WSDLFactory;
45  import javax.wsdl.xml.WSDLReader;
46  import javax.xml.namespace.QName;
47  import org.codehaus.xfire.DefaultXFire;
48  import org.codehaus.xfire.XFire;
49  import org.codehaus.xfire.aegis.AegisBindingProvider;
50  import org.codehaus.xfire.aegis.type.TypeMapping;
51  import org.codehaus.xfire.service.Service;
52  import org.codehaus.xfire.service.binding.ObjectServiceFactory;
53  import org.codehaus.xfire.soap.SoapConstants;
54  import org.codehaus.xfire.transport.Transport;
55  import org.codehaus.xfire.transport.TransportManager;
56  import org.codehaus.xfire.wsdl.AbstractWSDL;
57  import org.xml.sax.InputSource;
58  
59  /**
60   * Helper class to create XFire service starting from {@link ServiceDescriptor}.
61   */
62  public final class ServiceCreator {
63  
64      /**
65       * Initial buffer size, expressed in bytes.
66       */
67      private static final int BUFFER_SIZE = 4096;
68  
69      /**
70       * The logger for this class and its instances.
71       */
72      private static final Logger LOG
73              = LoggerFactory.getLogger(ServiceCreator.class);
74  
75      /**
76       * Creates an instance of this class.
77       */
78      public ServiceCreator() {
79      }
80  
81      /**
82       * Creates the XFire service from specified parameters.
83       *
84       * @param   desc   the service descriptor.
85       * @param   xfire  the XFire instance to use for creation.
86       * @return  the newly created XFire service.
87       */
88      public Service createService(final ServiceDescriptor desc,
89                                   final XFire xfire) {
90          QName interfaceName = new QName(desc.getServiceNameSpace(),
91                                          desc.getServiceInterfaceName());
92  
93          return doCreateService(desc, xfire, interfaceName, false);
94      }
95  
96      /**
97       * Creates the JBI XFire service from specified parameters.
98       *
99       * @param   desc           the service descriptor.
100      * @param   xfire          the XFire instance to use for creation.
101      * @param   interfaceName  the interface qualified name.
102      * @return  the newly created JBI XFire service.
103      */
104     public Service createJbiService(final ServiceDescriptor desc,
105             final XFire xfire, final QName interfaceName) {
106         return doCreateService(desc, xfire, interfaceName, true);
107     }
108 
109     /**
110      * Creates the XFire service from specified parameters.
111      *
112      * @param   desc           the service descriptor.
113      * @param   xfire          the XFire instance to use for creation.
114      * @param   interfaceName  the interface qualified name.
115      * @param   isJbiService   <code>true</code> to create a JBI service (i.e.
116      *                         under a JBI container), <code>false</code>
117      *                         otherwise (outside a JBI environment).
118      * @return  the newly created XFire service.
119      */
120     private Service doCreateService(final ServiceDescriptor desc,
121             final XFire xfire, final QName interfaceName,
122             final boolean isJbiService) {
123         ObjectServiceFactory factory
124                 = new ObjectServiceFactory(xfire.getTransportManager());
125         AegisBindingProvider bindingProvider
126                 = (AegisBindingProvider) factory.getBindingProvider();
127         Map<String, Object> props = null;
128         Service service;
129         TypeMapping typeMapping;
130 
131         if (isJbiService) {
132             props = new HashMap<String, Object>();
133             props.put(ObjectServiceFactory.PORT_TYPE, interfaceName);
134             props.put(ObjectServiceFactory.STYLE, SoapConstants.STYLE_WRAPPED);
135             props.put(ObjectServiceFactory.USE, SoapConstants.USE_LITERAL);
136 
137             factory.getSoap12Transports().clear();
138             factory.getSoap11Transports().clear();
139             factory.getSoap11Transports().add(JbiTransport.JBI_BINDING);
140         }
141 
142         service = factory.create(desc.getServiceInterface(),
143                 desc.getServiceName(),  desc.getServiceNameSpace(), props);
144         service.setInvoker(new ServiceInvoker(desc));
145 
146         // Adds the import in WSDL schema
147         service.setProperty(AbstractWSDL.GENERATE_IMPORTS, "true");
148 
149         typeMapping = bindingProvider.getTypeMapping(service);
150         typeMapping.register(new BigIntegerType());
151         typeMapping.register(new BigDecimalType());
152 
153         return service;
154     }
155 
156     /**
157      * Creates a WSDL object from specified copy Cobol. This method may be used
158      * to generate a WSDL (file) starting from a CPY file, for example.
159      *
160      * @param   copyCobol          the copy Cobol to generates the WSDL. Must be
161      *                             not <code>null</code>.
162      * @param   outputCopyCobol    the optional output copy Cobol to generates
163      *                             the WSDL. If <code>null</code> or blank, is
164      *                             like to not specify this parameter, so only
165      *                             <code>copyCobol</code> will be considered and
166      *                             will be used for input and for output during
167      *                             CICS call.
168      * @param   desc               the WSDL descriptor. Must be not
169      *                             <code>null</code>.
170      * @return  the WSDL document corresponding to the copy Cobol received.
171      * @throws  Jbi4cicsException  in case of errors that prevent WSDL creation.
172      */
173     public Definition createWsdlFromCopyCobol(final String copyCobol,
174             final String outputCopyCobol, final ServiceDescriptor desc)
175             throws Jbi4cicsException {
176         BCELClassLoader loader
177                 = new BCELClassLoader(getClass().getClassLoader());
178         Service service;
179         ByteArrayOutputStream buffer = new ByteArrayOutputStream(BUFFER_SIZE);
180 
181         // Creates service input bean
182         new ServiceBeanGenerator(desc, true).generateBeanClass(loader);
183 
184         // Creates service output bean
185         new ServiceBeanGenerator(desc, false).generateBeanClass(loader);
186 
187         // Creates the service interface
188         new ServiceInterfaceGenerator(desc).generateServiceInterface(loader);
189 
190         // Creates the service
191         service = createJbiService(desc, createXFire(null), new QName(
192                 desc.getServiceNameSpace(), desc.getServiceInterfaceName()));
193 
194         // Modifies the service adding CICS elements
195         try {
196             service.getWSDLWriter().write(buffer);
197             return bindToCics(new ByteArrayInputStream(buffer.toByteArray()),
198                               copyCobol, outputCopyCobol, desc);
199         } catch (WSDLException e) {
200             LOG.error(e.getLocalizedMessage(), e);
201             throw new Jbi4cicsException(e.getMessage(), e);
202         } catch (IOException e) {
203             LOG.error(e.getLocalizedMessage(), e);
204             throw new Jbi4cicsException(e.getMessage(), e);
205         }
206     }
207 
208     // Leave this method here and don't put it inside Jbi4cicsLifeCycle class,
209     // because that class uses ServiceMix classes and will cause another library
210     // to be added to Netbeans plugin
211     /**
212      * Creates a new XFire instance.
213      *
214      * @param   componentContext  the JBI component context. May be
215      *                            <code>null</code>.
216      * @return  the newly created XFire instance, with a JBI transport related
217      *          to <code>componentContext</code>.
218      */
219     public static XFire createXFire(final ComponentContext componentContext) {
220         XFire xfire = new DefaultXFire();
221         TransportManager manager = xfire.getTransportManager();
222 
223         // Iterates over the array instead of the Collection to avoid
224         // java.util.ConcurrentModificationException
225         for (Object o : manager.getTransports().toArray()) {
226             manager.unregister((Transport) o);
227         }
228         manager.register(new JbiTransport(componentContext));
229         return xfire;
230     }
231 
232     private Definition bindToCics(final InputStream wsdl,
233             final String copyCobol, final String outputCopyCobol,
234             final ServiceDescriptor desc)
235             throws WSDLException, IOException, LocationException {
236         WSDLFactory factory = WSDLFactory.newInstance();
237         WSDLReader reader = factory.newWSDLReader();
238         ExtensionRegistry registry = factory.newPopulatedExtensionRegistry();
239         Definition def;
240         javax.wsdl.Service service;
241         Port port;
242         Binding binding;
243 
244         Jbi4CicsExtension.register(registry);
245         reader.setExtensionRegistry(registry);
246         def = reader.readWSDL(null, new InputSource(wsdl));
247         def.setExtensionRegistry(registry);
248         def.addNamespace(Jbi4CicsExtension.DEFAULT_PREFIX,
249                          Jbi4CicsExtension.NS_URI_JBI4CICS);
250 
251         service = def.getService(new QName(desc.getServiceNameSpace(),
252                                            desc.getServiceName()));
253 
254         // Remove all ports
255         service.getPorts().clear();
256 
257         // Adds the port
258         port = def.createPort();
259         port.setName(desc.getServiceName() + "CicsPort");
260         service.addPort(port);
261 
262         // Adds the extended address
263         port.addExtensibilityElement(createJbi4CicsAddressElement(desc));
264 
265         // Adds the binding, using an existing port type
266         binding = def.createBinding();
267         binding.setUndefined(false);
268         binding.setQName(new QName(desc.getServiceNameSpace(),
269                                    desc.getServiceName() + "CicsBinding"));
270         binding.setPortType(def.getPortType(new QName(
271                 desc.getServiceNameSpace(), desc.getServiceInterfaceName())));
272         port.setBinding(binding);
273         def.addBinding(binding);
274         removeWsdlSoapElements(def, binding, desc);
275 
276         // Adds the extended binding
277         binding.addExtensibilityElement(
278                 createJbi4CicsBindingElement(copyCobol, outputCopyCobol, desc));
279 
280         return def;
281     }
282 
283     /**
284      * Creates a new <code>Jbi4CicsAddress</code> element from the specified
285      * service descriptor.
286      *
287      * @param   desc  the service descriptor.
288      * @return  the newly created Cics address element, representing the service
289      *          described by <code>desc</code>.
290      * @throws  LocationException  if the CICS connection type contained in the
291      *                             service descriptor is not valid.
292      */
293     private static Jbi4CicsAddress createJbi4CicsAddressElement(
294             final ServiceDescriptor desc) throws LocationException {
295         Jbi4CicsAddress addr = new Jbi4CicsAddress();
296         Account account = desc.getAccount();
297 
298         // XXX Now we use only SimpleLocation, but in the future...
299         SimpleLocation location = (SimpleLocation) desc.getServiceLocation();
300 
301         // XXX InteractionDescription has no methods: design problem?
302         CICSInteractionDescription interactionDesc
303                 = (CICSInteractionDescription) desc.getInteractionDescription();
304 
305         addr.setElementType(Jbi4CicsExtension.Q_ELEM_JBI4CICS_ADDRESS);
306         addr.setUsername(account.getUsername());
307         addr.setPassword(account.getPassword());
308         addr.setConnectionType(location.getConnectionTypeName());
309         addr.setJNDIConnectionName(location.getLocationName());
310         addr.setProgramName(interactionDesc.getProgramName());
311         addr.setTransactionName(interactionDesc.getTransactionName());
312         addr.setTpn(Boolean.valueOf(interactionDesc.isTpn()));
313         return addr;
314     }
315 
316     /**
317      * Removes any SOAP element from the specified WSDL service.
318      *
319      * @param  def      the WSDL definition.
320      * @param  binding  the new CICS binding added to <code>def</code>.
321      * @param  desc     the service descriptor used to create the WSDL
322      *                  definition <code>def</code>.
323      */
324     private static void removeWsdlSoapElements(final Definition def,
325             final Binding binding, final ServiceDescriptor desc) {
326         QName oldBindingName = new QName(desc.getServiceNameSpace(),
327                                          desc.getServiceName() + "JBIBinding");
328 
329         for (Object o : def.getBinding(oldBindingName).getBindingOperations()) {
330             BindingOperation bindingOp = (BindingOperation) o;
331             Operation operation = bindingOp.getOperation();
332             BindingInput input = bindingOp.getBindingInput();
333             BindingOutput output = bindingOp.getBindingOutput();
334 
335             // Clones the <wsdl:operation> element to remove inner elements
336             // contained in the "wsdlsoap" namespace
337             BindingOperation newBindingOp = def.createBindingOperation();
338             Operation newOperation = def.createOperation();
339             BindingInput newInput = def.createBindingInput();
340             BindingOutput newOutput = def.createBindingOutput();
341 
342             newBindingOp.setName(bindingOp.getName());
343             newOperation.setName(operation.getName());
344             newInput.setName(input.getName());
345             newOutput.setName(output.getName());
346             newBindingOp.setOperation(newOperation);
347             newBindingOp.setBindingInput(newInput);
348             newBindingOp.setBindingOutput(newOutput);
349             for (Object obj : bindingOp.getBindingFaults().values()) {
350                 BindingFault fault = (BindingFault) obj;
351                 BindingFault newFault = def.createBindingFault();
352 
353                 newFault.setName(fault.getName());
354                 newBindingOp.addBindingFault(newFault);
355             }
356 
357             binding.addBindingOperation(newBindingOp);
358         }
359         def.removeBinding(oldBindingName);
360     }
361 
362     /**
363      * Creates a new <code>Jbi4CicsBinding</code> element from the specified
364      * values.
365      *
366      * @param   copyCobol        the copy Cobol, used for the input and also for
367      *                           the output step if <code>outputCopyCobol</code>
368      *                           is <code>null</code> or blank.
369      * @param   outputCopyCobol  the optional output copy Cobol to generates the
370      *                           WSDL. If <code>null</code> or blank, is like to
371      *                           not specify this parameter, so only
372      *                           <code>copyCobol</code> will be considered and
373      *                           will be used for input and for output during
374      *                           CICS call.
375      * @param   desc             the service descriptor.
376      * @return  the newly created Cics binding element, containing the values
377      *          received as parameters.
378      */
379     private static Jbi4CicsBinding createJbi4CicsBindingElement(
380             final String copyCobol, final String outputCopyCobol,
381             final ServiceDescriptor desc) {
382         Jbi4CicsBinding binding = new Jbi4CicsBinding();
383 
384         binding.setElementType(Jbi4CicsExtension.Q_ELEM_JBI4CICS_BINDING);
385         binding.setServicePackageName(desc.getServiceInterfacePackageName());
386         binding.setCodePage(desc.getCodePage());
387         binding.setCopyCobol(copyCobol);
388         if ((outputCopyCobol != null) && !isBlank(outputCopyCobol)) {
389             binding.setSameCopyCobol(Boolean.FALSE);
390             binding.setOutputCopyCobol(outputCopyCobol);
391         }
392 
393         return binding;
394     }
395 
396     /**
397      * Tests if the specified string is empty or contains only blank characters.
398      * <p>
399      * A <i>blank</i> character has code &lt;= <code>'&#92;u0020'</code> (the
400      * space character).
401      *
402      * @param   str  the string to test.
403      * @return  <code>true</code> if and only if <code>str</code> has length
404      *          zero or is made only by blank characters.
405      */
406     private static boolean isBlank(String str) {
407         for (int i = str.length() - 1; i >= 0; --i) {
408             if (str.charAt(i) > ' ') {
409                 return false;
410             }
411         }
412         return true;
413     }
414 }