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   
9   
10  package it.imolinfo.jbi4cics.jbi;
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.LocationException;
16  import it.imolinfo.jbi4cics.jbi.wsdl.Jbi4CicsAddress;
17  import it.imolinfo.jbi4cics.jbi.wsdl.Jbi4CicsBinding;
18  import it.imolinfo.jbi4cics.jbi.wsdl.Jbi4CicsExtension;
19  import it.imolinfo.jbi4cics.locator.SimpleLocation;
20  import it.imolinfo.jbi4cics.security.J2CAccount;
21  import it.imolinfo.jbi4cics.webservices.descriptor.ServiceDescriptor;
22  import java.io.File;
23  import java.io.FileFilter;
24  import java.io.FilenameFilter;
25  import java.io.IOException;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.List;
29  import javax.jbi.management.DeploymentException;
30  import javax.wsdl.Binding;
31  import javax.wsdl.BindingOperation;
32  import javax.wsdl.Definition;
33  import javax.wsdl.Port;
34  import javax.wsdl.Service;
35  import javax.wsdl.WSDLException;
36  import javax.wsdl.extensions.ExtensionRegistry;
37  import javax.wsdl.factory.WSDLFactory;
38  import javax.wsdl.xml.WSDLReader;
39  import javax.xml.parsers.DocumentBuilderFactory;
40  import javax.xml.parsers.ParserConfigurationException;
41  import org.apache.servicemix.common.AbstractDeployer;
42  import org.apache.servicemix.common.BaseComponent;
43  import org.apache.servicemix.common.Endpoint;
44  import org.apache.servicemix.common.ServiceUnit;
45  import org.w3c.dom.Document;
46  import org.xml.sax.SAXException;
47  import com.ibm.wsdl.Constants;
48  
49  /**
50   * Deployer for the Jbi4cics JBI component. Deploys all the WSDL containing a
51   * Jbi4Cics extension.
52   *
53   * @author marcopiraccini
54   * @author <a href="mailto:mcimatti@imolinfo.it">Marco Cimatti</a>
55   */
56  public final class Jbi4cicsWSDLDeployer extends AbstractDeployer {
57  
58      /**
59       * Default for input/output bean class name.
60       */
61      private static final String IO_BEAN_NAME = "InputOutputBean";
62  
63      /**
64       * Default input bean class name when input and output bean names differ.
65       */
66      private static final String INPUT_BEAN_NAME = "InputBean";
67  
68      /**
69       * Default output bean class name when input and output bean names differ.
70       */
71      private static final String OUTPUT_BEAN_NAME = "OutputBean";
72  
73      /**
74       * The task name, used for error messages.
75       */
76      private static final String TASK = "deploy";
77  
78      /**
79       * The logger for this class and its instances.
80       */
81      private static final Logger LOG
82              = LoggerFactory.getLogger(Jbi4cicsWSDLDeployer.class);
83  
84      /**
85       * The responsible to translate localized messages.
86       */
87      private static final Messages MESSAGES
88              = Messages.getMessages(Jbi4cicsWSDLDeployer.class);
89  
90      /**
91       * The deployable WSDL file filter.
92       *
93       * @see #getWSDL(String)
94       */
95      private static final FilenameFilter DEPLOYABLE_FILTER = new WsdlFilter();
96  
97      /**
98       * Creates a <code>Jbi4cicsWSDLDeployer</code> for the specified component.
99       *
100      * @param  component  the component to be deployed by this instance.
101      */
102     public Jbi4cicsWSDLDeployer(final BaseComponent component) {
103         super(component);
104     }
105 
106     /**
107      * Check if this deployer is able to handle a given artifact.
108      *
109      * @param   suName      the name of the service unit.
110      * @param   suRootPath  the path of the exploded service unit.
111      * @return  <code>true</code> if this deployer can handle the given
112      *          artifact.
113      */
114     public boolean canDeploy(final String suName, final String suRootPath) {
115         File[] wsdls = getWSDL(suRootPath);
116 
117         if (wsdls.length == 0) {
118             LOG.info("CIC001032_No_WSDLs_file_found", suRootPath, suName);
119             return false;
120         }
121         return true;
122     }
123 
124     /**
125      * Actually deploys the given service unit and build a ServiceUnit object
126      * that contains endpoints.
127      *
128      * @param suName
129      *            the name of the service unit
130      * @param suRootPath
131      *            the path of the exploded service unit
132      *
133      * @return a service unit containing endpoints
134      *
135      * @throws DeploymentException
136      *             if an error occurs
137      */
138     public ServiceUnit deploy(final String suName, final String suRootPath)
139             throws DeploymentException {
140         File[] wsdls = getWSDL(suRootPath);
141         ServiceUnit serviceUnit;
142         List<Jbi4cicsEndpoint> endpoints = new ArrayList<Jbi4cicsEndpoint>();
143 
144         if (wsdls.length == 0) {
145             throw failure(TASK,
146                     MESSAGES.getString("CIC001033_No_valid_wsdl_found"), null);
147         }
148 
149         // Creates the ServiceUnit
150         serviceUnit = new ServiceUnit();
151         serviceUnit.setComponent(component);
152         serviceUnit.setName(suName);
153         serviceUnit.setRootPath(suRootPath);
154 
155         // For each WSDL, add the Endpoits to the Service unit
156         for (File wsdl : wsdls) {
157             endpoints.addAll(getEndpointFromWsdl(wsdl));
158         }
159         if (endpoints.isEmpty()) {
160             throw failure(TASK, MESSAGES.getString(
161                     "CIC001034_Invalid_wsdl_no_valid_endpoints_found"), null);
162         }
163 
164         for (Jbi4cicsEndpoint endpoint : endpoints) {
165             endpoint.setServiceUnit(serviceUnit);
166             try {
167                 endpoint.registerService();
168             } catch (Exception e) {
169                 LOG.error("CIC001035_Could_not_register_endpoint", e);
170                 throw failure(TASK, MESSAGES.getString(
171                         "CIC001035_Could_not_register_endpoint"), e);
172             }
173             serviceUnit.addEndpoint(endpoint);
174         }
175         return serviceUnit;
176     }
177 
178     /**
179      * Validates the specified endpoint. This implementation does nothing.
180      *
181      * @param   endpoint             the endpoint to validate.
182      * @throws  DeploymentException  in general, if <code>endpoint</code> can't
183      *                               be validated but this implementation never
184      *                               throw this kind of exception.
185      */
186     @Override
187     protected void validate(final Endpoint endpoint)
188             throws DeploymentException {
189     }
190 
191     /**
192      * Return a <code>WSDLReader</code> and registers the
193      * <code>Jbi4CicsExtension</code>.
194      *
195      * @return  a new <code>WSDLReader</code>.
196      * @throws  WSDLException  No description provided.
197      */
198     private WSDLReader createJbi4CicsWsdlReader() throws WSDLException {
199         WSDLFactory factory = WSDLFactory.newInstance();
200         ExtensionRegistry registry = factory.newPopulatedExtensionRegistry();
201         WSDLReader reader = factory.newWSDLReader();
202 
203         reader.setFeature(Constants.FEATURE_VERBOSE, false);
204         reader.setFeature(Constants.FEATURE_IMPORT_DOCUMENTS, true);
205         Jbi4CicsExtension.register(registry);
206         if (LOG.isDebugEnabled()) {
207             LOG.debug("Extension QName: "
208                       + Jbi4CicsExtension.Q_ELEM_JBI4CICS_BINDING);
209         }
210         reader.setExtensionRegistry(registry);
211         return reader;
212     }
213 
214     /**
215      * Reads the <code>Jbi4cicsEndpoint</code> list from the WSDL.
216      *
217      * @param   wsdl  the WSDL file. Must be not <code>null</code>.
218      * @return  the loaded endpoint list, never empty.
219      * @throws  DeploymentException  in case of errors.
220      */
221     public List<Jbi4cicsEndpoint> getEndpointFromWsdl(final File wsdl)
222             throws DeploymentException {
223         Document document = readXMLFile(wsdl);
224         Definition definition;
225         List<Jbi4cicsEndpoint> endpoints;
226 
227         try {
228             definition = createJbi4CicsWsdlReader().readWSDL(null, document);
229         } catch (WSDLException e) {
230             throw createParseFailure(wsdl, e);
231         }
232 
233         if (definition.getServices().isEmpty()) {
234             throw failure(TASK, MESSAGES.getString(
235                     "CIC001037_Invalid_wsdl_no_defined_services", wsdl), null);
236         }
237 
238         endpoints = getJbi4CicsEndpoints(definition, document);
239         if (endpoints.isEmpty()) {
240             LOG.warn("CIC001038_No_EstensibilityElement_found_from_WSDL_file",
241                      wsdl);
242         }
243         return endpoints;
244     }
245 
246     /**
247      * Reads the XML document from the specified file.
248      *
249      * @param   file  the file to read. Must be not <code>null</code>.
250      * @return  the XML document read from the received file.
251      * @throws  DeploymentException  if it's not possible to parse
252      *                               <code>file</code> as a XML file.
253      */
254     private Document readXMLFile(File file) throws DeploymentException {
255         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
256 
257         factory.setNamespaceAware(true);
258         try {
259             return factory.newDocumentBuilder().parse(file);
260         } catch (IOException e) {
261             throw createParseFailure(file, e);
262         } catch (ParserConfigurationException e) {
263             throw createParseFailure(file, e);
264         } catch (SAXException e) {
265             throw createParseFailure(file, e);
266         }
267     }
268 
269     /**
270      * Creates a new <code>DeploymentException</code> caused by the specified
271      * error obtained while parsing the indicated file. This method also do a
272      * log of the received exception.
273      *
274      * @param   file   the file that caused the error during its parsing. Must
275      *                 be not <code>null</code>.
276      * @param   cause  the original error obtained while parsing the file
277      *                 <code>file</code>. Must be not <code>null</code>.
278      * @return  a newly constructed <code>DeploymentException</code>, obtained
279      *          from parameters passed to this method.
280      */
281     private DeploymentException createParseFailure(File file, Exception cause) {
282         String errorMsgKey = "CIC001036_Could_not_parse";
283         String errorMsg = MESSAGES.getString(errorMsgKey, file);
284 
285         LOG.error(errorMsgKey, new Object[] { file }, cause);
286         return failure(TASK, errorMsg, cause);
287     }
288 
289     /**
290      * Collects all <code>Jbi4cicsEndpoint</code> reading from the specified
291      * WSDL.
292      *
293      * @param   definition  the WSDL to read. Must be not <code>null</code>.
294      * @param   document    the XML representation of <code>definition</code>.
295      *                      Must be not <code>null</code>.
296      * @return  the list of all Cics endpoints read from the specified WSDL
297      *          document. The returned list is never <code>null</code>.
298      * @throws  DeploymentException  if the operation number read from the
299      *                               binding differs from 1.
300      */
301     private List<Jbi4cicsEndpoint> getJbi4CicsEndpoints(Definition definition,
302             Document document) throws DeploymentException {
303         List<Jbi4cicsEndpoint> endpoints = new ArrayList<Jbi4cicsEndpoint>();
304 
305         // For each service, look for a extended bindings
306         for (Object i : definition.getServices().values()) {
307             Service svc = (Service) i;
308 
309             for (Object j : svc.getPorts().values()) {
310                 Port port = (Port) j;
311 
312                 for (Object k : port.getExtensibilityElements()) {
313 
314                     // If the address extension element is found, look for
315                     // binding extension
316                     if (k instanceof Jbi4CicsAddress) {
317                         Jbi4CicsAddress cicsAddress = (Jbi4CicsAddress) k;
318                         Binding binding = port.getBinding();
319 
320                         // Reads the estensibility element of the binding
321                         for (Object l : binding.getExtensibilityElements()) {
322                             if (l instanceof Jbi4CicsBinding) {
323 
324                                 // We found a binding estensibility element
325                                 Jbi4cicsEndpoint endpoint
326                                         = createServiceDescriptor(svc, port,
327                                                 binding, cicsAddress,
328                                                 (Jbi4CicsBinding) l);
329 
330                                 endpoint.setDescription(document);
331                                 endpoint.setDefinition(definition);
332                                 endpoints.add(endpoint);
333                             }
334                         }
335                     }
336                 }
337             }
338         }
339         return endpoints;
340     }
341 
342     /**
343      * Creates the <code>Jbi4cicsEndpoint</code> from the extended
344      * WSDL-element.
345      *
346      * @param   service              the WSDL service.
347      * @param   port                 the WSDL port.
348      * @param   binding              the WSDL binding.
349      * @param   addressExtension     the Jbi4Cics address extension.
350      * @param   bindingExtension     the Jbi4Cics binding extension.
351      * @return  the new <code>Jbi4cicsEndpoint</code> created from the extended
352      *          WSDL-element.
353      * @throws  DeploymentException  if the operation number read from the
354      *                               binding differs from 1.
355      */
356     protected Jbi4cicsEndpoint createServiceDescriptor(final Service service,
357             final Port port, final Binding binding,
358             final Jbi4CicsAddress addressExtension,
359             final Jbi4CicsBinding bindingExtension) throws DeploymentException {
360         ServiceDescriptor serviceDescriptor = new ServiceDescriptor();
361         Jbi4cicsEndpoint endpoint = new Jbi4cicsEndpoint();
362         List bindingOperations = binding.getBindingOperations();
363         CICSInteractionDescription cicsDescription
364                 = new CICSInteractionDescription();
365         J2CAccount account = new J2CAccount();
366         SimpleLocation location = new SimpleLocation();
367         Boolean sameCopyCobol = bindingExtension.getSameCopyCobol();
368 
369         // CopyCobol and Code Page
370         endpoint.setCopyCobol(bindingExtension.getCopyCobol());
371         endpoint.setCodePage(bindingExtension.getCodePage());
372 
373         // ServiceName and ServiceNamespace
374         serviceDescriptor.setServiceName(service.getQName().getLocalPart());
375         serviceDescriptor.setServiceNameSpace(
376                 service.getQName().getNamespaceURI());
377 
378         // Gets the FIRST operation of the binding.
379         // If more than one operation is found an exception is thrown.
380         if (bindingOperations.size() != 1) {
381             throw new DeploymentException(MESSAGES.getString("CIC001039_More_"
382                         + "than_one_operation_find_in_extension_binding"));
383         }
384         serviceDescriptor.setOperationName(
385                 ((BindingOperation) bindingOperations.get(0)).getName());
386         serviceDescriptor.setServiceInterfacePackageName(
387                 bindingExtension.getServicePackageName());
388 
389         serviceDescriptor.setServiceInterfaceName(
390                 binding.getPortType().getQName().getLocalPart());
391 
392         // Back compatibility: default is same copy Cobol for input and output
393         if ((sameCopyCobol == null) || Boolean.FALSE.equals(sameCopyCobol)) {
394             serviceDescriptor.setInputBeanClassName(IO_BEAN_NAME);
395             serviceDescriptor.setOutputBeanClassName(IO_BEAN_NAME);
396         } else {
397             serviceDescriptor.setInputBeanClassName(INPUT_BEAN_NAME);
398             serviceDescriptor.setOutputBeanClassName(OUTPUT_BEAN_NAME);
399         }
400 
401         // Account
402         account.setUsername(addressExtension.getUsername());
403         account.setPassword(addressExtension.getPassword());
404         serviceDescriptor.setAccount(account);
405 
406         // Location
407         try {
408             location.setConnectionTypeName(
409                     addressExtension.getConnectionType());
410         } catch (LocationException e) {
411             String msg = e.getLocalizedMessage();
412 
413             LOG.error("CIC001040_Error_loading_location_type",
414                       new Object[] { msg }, e);
415             throw new DeploymentException(MESSAGES.getString(
416                     "CIC001040_Error_loading_location_type", msg), e);
417         }
418         location.setLocationName(addressExtension.getJNDIConnectionName());
419         serviceDescriptor.setServiceLocation(location);
420 
421         cicsDescription.setProgramName(addressExtension.getProgramName());
422         cicsDescription.setTpn(addressExtension.getTpn());
423         cicsDescription.setTransactionName(
424                 addressExtension.getTransactionName());
425 
426         serviceDescriptor.setInteractionDescription(cicsDescription);
427         endpoint.setServiceDescriptor(serviceDescriptor);
428         return endpoint;
429     }
430 
431     /**
432      * Returns all the deployable WSDL files contained in the specified
433      * directory or in all first-level childs.
434      *
435      * @param   suRootPath  the service unit root path. Must be not
436      *                      <code>null</code>.
437      * @return  all the deployable WSDL files contained in the directory named
438      *          by <code>serviceUnitRootPath</code> and inside its first-level
439      *          subdirectories.
440      */
441     private File[] getWSDL(final String suRootPath) {
442         File suRoot = new File(suRootPath);
443         File[] firstLevelWsdls = suRoot.listFiles(DEPLOYABLE_FILTER);
444         File[] firstLevelDirs = suRoot.listFiles(DirectoryFilter.THE_INSTANCE);
445         List<File> files = new ArrayList<File>();
446         boolean debug = LOG.isDebugEnabled();
447 
448         files.addAll(Arrays.asList(firstLevelWsdls));
449         for (File dir : firstLevelDirs) {
450             File[] secondLevelWsdls = dir.listFiles(DEPLOYABLE_FILTER);
451 
452             if (debug) {
453                 LOG.debug("Found directory: " + dir.getName());
454             }
455             files.addAll(Arrays.asList(secondLevelWsdls));
456         }
457         return files.toArray(new File[files.size()]);
458     }
459 
460 
461     /**
462      * WSDL file filter class.
463      */
464     private static final class WsdlFilter implements FilenameFilter {
465 
466         /**
467          * Prepares a new instance of this class.
468          */
469         private WsdlFilter() {
470         }
471 
472         /**
473          * Tests if a specified file is a WSDL file and contains the Jbi4Cics
474          * extension namespace.
475          *
476          * @param   dir    the directory in which the file was found.
477          * @param   name   the name of the file.
478          * @return  <code>true</code> if and only if the file name ends with
479          *          <i>.WSDL</i> and the file contains the Jbi4Cics extension
480          *          namespace; <code>false</code> otherwise.
481          */
482         public boolean accept(final File dir, final String name) {
483             boolean isWSDL = name.toUpperCase().endsWith(".WSDL");
484 
485             if (isWSDL) {
486                 try {
487                     WSDLFactory factory = WSDLFactory.newInstance();
488                     WSDLReader reader = factory.newWSDLReader();
489                     Definition def;
490 
491                     reader.setFeature(Constants.FEATURE_VERBOSE, false);
492                     reader.setFeature(Constants.FEATURE_IMPORT_DOCUMENTS, true);
493                     def = reader.readWSDL(dir.getAbsolutePath(), name);
494                     if (containsExtensionNamespace(def)) {
495                         if (LOG.isDebugEnabled()) {
496                             LOG.debug("Found namespace "
497                                       + Jbi4CicsExtension.NS_URI_JBI4CICS
498                                       + " in file " + name);
499                         }
500                         return true;
501                     }
502                 } catch (WSDLException e) {
503                     Object[] args = new Object[] {
504                             name, Jbi4CicsExtension.NS_URI_JBI4CICS };
505 
506                     LOG.warn("CIC001041_File_doesnt_contain_the_estension_"
507                              + "jbi4corba", args, e);
508                 }
509             }
510             return false;
511         }
512 
513         /**
514          * Tests if the definition contains the
515          * <code>Jbi4CicsExtension.NS_URI_JBI4CICS</code> namespace.
516          *
517          * @param   definition  the definition to check.
518          * @return  <code>true</code> if the definition contains the
519          *          <code>Jbi4CicsExtension.NS_URI_JBI4CICS</code> namespace.
520          */
521         private static boolean containsExtensionNamespace(
522                 final Definition definition) {
523             for (Object obj : definition.getNamespaces().values()) {
524                 String namespace = (String) obj;
525 
526                 if (namespace.equalsIgnoreCase(
527                         Jbi4CicsExtension.NS_URI_JBI4CICS)) {
528                     return true;
529                 }
530             }
531             return false;
532         }
533     }
534 
535 
536     /**
537      * Accept directories that are NOT "META-INF".
538      *
539      * @author marco
540      */
541     private static final class DirectoryFilter implements FileFilter {
542 
543         /**
544          * An instance of this class. This class may be used as a
545          * <i>singleton</i>, so this field can be accessed by the outer class.
546          */
547         static final DirectoryFilter THE_INSTANCE = new DirectoryFilter();
548 
549         /**
550          * Constructs a new instance of this class.
551          */
552         private DirectoryFilter() {
553         }
554 
555         /**
556          * Accepts only directories not called <i>META-INF</i>, <i>meta-inf</i>
557          * and so on considering case.
558          *
559          * @param   file  the file to test.
560          * @return  <code>true</code> if and only if <code>file</code> is a
561          *          directory and its name is not <i>META-INF</i>, ignoring
562          *          case.
563          */
564         public boolean accept(final File file) {
565             return file.isDirectory()
566                    && !(file.getName().equalsIgnoreCase("meta-inf"));
567         }
568     }
569 }