View Javadoc

1   /**
2    *
3    */
4   package pt.digitalis.maven.ormgenerator;
5   
6   import java.io.BufferedReader;
7   import java.io.BufferedWriter;
8   import java.io.File;
9   import java.io.FileInputStream;
10  import java.io.FileNotFoundException;
11  import java.io.FileReader;
12  import java.io.FileWriter;
13  import java.io.IOException;
14  import java.io.InputStream;
15  import java.io.Writer;
16  import java.util.Date;
17  import java.util.HashMap;
18  import java.util.Map;
19  import java.util.Properties;
20  
21  import javax.xml.parsers.SAXParserFactory;
22  
23  import org.apache.maven.plugin.logging.Log;
24  
25  import pt.digitalis.maven.ormgenerator.utils.FileUtils;
26  import pt.digitalis.maven.ormgenerator.utils.FreemarkerExecutor;
27  import freemarker.template.TemplateException;
28  
29  /**
30   * Creates the service code, and the IoC contributions for them
31   *
32   * @author Ant�nio Silva <a href="mailto:asilva@digitalis.pt">asilva@digitalis.pt</a>
33   * @author Pedro Viegas <a href="mailto:pviegas@digitalis.pt">pviegas@digitalis.pt</a>
34   * @created 2007/11/14
35   */
36  public class ServiceExecutor extends BaseExecutor {
37  
38      /** The name given to the model class */
39      public static final String MODEL_MODULE_NAME = "DIFModelModule";
40  
41      /** The name given to the model class */
42      public static final String MODULE_PROPERTIES_NAME = "modules";
43  
44      /** The name of the template for the service */
45      public static final String SERVICE_TEMPLATE = "service.ftl";
46  
47      /** The name of the template for the service */
48      public static final String SERVICE_IMPL_TEMPLATE = "serviceimpl.ftl";
49  
50      /** The name of the template for the service */
51      public static final String SERVICE_DIRECTORY_TEMPLATE = "serviceDirectory.ftl";
52  
53      /** The name of the template for the service */
54      public static final String SERVICE_DIRECTORY_IMPL_TEMPLATE = "serviceDirectoryImpl.ftl";
55  
56      /** The name of the template for the database */
57      public static final String DATABASE_DIRECTORY_TEMPLATE = "databaseDirectory.ftl";
58  
59      /** The name of the template for the database */
60      public static final String DATABASE_DIRECTORY_IMPL_TEMPLATE = "databaseDirectoryImpl.ftl";
61  
62      /** The name of the template for the service */
63      public static final String MODULE_TEMPLATE = "module.ftl";
64  
65      /** The name of the template properties for the service */
66      public static final String MODULE_PROPERTIES_TEMPLATE = "moduleProperties.ftl";
67  
68      /** The suffix given to the generated files */
69      public static final String SERVICE_FILE_SUFFIX = "Service";
70  
71      /** The suffix given to the generated files */
72      public static final String SERVICE_DIRECTORY_FILE_SUFFIX = "ServiceDirectory";
73  
74      /** The suffix given to the generated files */
75      public static final String DATABASE_DIRECTORY_FILE = "Directory";
76  
77      /** Service data parsed from the XML. */
78      private ServiceCfg serviceData = null;
79  
80      /** The service configuration file path. */
81      private String serviceConfigurationFileName = null;
82  
83      /** The logger */
84      private Log logger = null;
85  
86      /** The project files path */
87      private String baseDir;
88  
89      /** The database name */
90      private String dataSourceName;
91  
92      /** The project resource files path */
93      private String resourcesPath;
94  
95      /** Multi instance mode */
96      private Boolean multiInstance;
97  
98      /**
99       * Class constructor instantiates the <code>ServiceExecutor</code> with the required initializations
100      *
101      * @param packageName
102      *            The base package for the generated files
103      * @param dataSourceName
104      *            the database name
105      * @param templateDir
106      *            Path for the template directory, could be relative or absolute.
107      * @param destinationDir
108      *            directory where the generated files should be inserted.
109      * @param baseCodeDIR
110      *            the project base path
111      * @param serviceConfigurationFileName
112      *            Service configuration file name, this file should read from the resources
113      * @param multiInstance
114      *            Multi instance mode
115      * @param logger
116      *            the Maven Plugin logger
117      */
118     public ServiceExecutor(String packageName, String dataSourceName, String templateDir, String destinationDir,
119             String baseCodeDIR, String serviceConfigurationFileName, Boolean multiInstance, Log logger) {
120         super(packageName, templateDir, destinationDir);
121         this.setServiceConfigurationFileName(serviceConfigurationFileName);
122         this.setLogger(logger);
123         this.multiInstance = multiInstance;
124         this.baseDir = baseCodeDIR;
125         this.dataSourceName = dataSourceName;
126         this.resourcesPath = baseCodeDIR + "/src/main/resources";
127     }
128 
129     /**
130      * Reads the service configuration file and populates the ServiceCfg
131      *
132      */
133     public void getServiceConfigurationData() {
134         try {
135             // Obtains the file to be read
136             InputStream f = new FileInputStream(this.getServiceConfigurationFileName());
137             ServiceHandler handler =
138                     new ServiceHandler(obtainPath(this.getDestinationDir(), this.getPackageName() + DAO_PACKAGE));
139             SAXParserFactory factory = SAXParserFactory.newInstance();
140             factory.setValidating(false);
141             javax.xml.parsers.SAXParser parser;
142 
143             // parses the XML
144             parser = factory.newSAXParser();
145             parser.parse(f, handler);
146             this.setServiceData(handler.getServiceCfg());
147 
148         } catch (Exception e) {
149             this.setServiceData(null);
150             this.getLogger().error(e.getMessage(), e);
151         }
152     }
153 
154     /**
155      * This method is responsible to generate all the files.
156      *
157      * @return Returns <code>true</code> if the generation process went well and <code>false</code> otherwise
158      */
159     public boolean execute() {
160         boolean result = true;
161 
162         // Obtains the data from XML file
163         this.getServiceConfigurationData();
164 
165         if (this.getServiceData() == null) {
166             this.getLogger()
167                     .error(
168                             "The service executor process was unable to read the Service configuration file with the name "
169                                     + this.getServiceConfigurationFileName()
170                                     + ". Please check if the file is in the classpath");
171             result = false;
172 
173         } else {
174             // Configures the freemarker engine
175             this.prepareExecutor(this.getDestinationDir());
176             if (this.getFreemarkerConfig() == null) {
177                 this
178                         .getLogger()
179                         .error(
180                                 "The service executor process was unable to build the freemarker configuration. Please check if the template directory is in classpath  under the directory "
181                                         + this.getTemplateDir());
182                 result = false;
183             } else {
184                 // generates the files
185                 result = this.generateServiceFiles(this.getDestinationDir());
186             }
187         }
188         return result;
189     }
190 
191     /**
192      * Generates the files from the templates
193      *
194      * @param generatedDest
195      *            generated file destination dir
196      * @return The result is <code>true</code> if the files were correctly generated well and false otherwise
197      */
198     public boolean generateServiceFiles(String generatedDest) {
199         // Get or create a template
200         boolean result = false;
201 
202         try {
203 
204             String pathInterface = obtainPath(generatedDest, this.getPackageName());
205             String pathImpl = obtainPath(generatedDest, this.getPackageName() + IMPL_PACKAGE_SUFFIX);
206             String pathModuleDesc = obtainPath(this.resourcesPath, "");
207 
208             // Generate services
209             for (Services service : this.getServiceData().getServices()) {
210                 // Builds the data to be merged with the template
211                 Map<String, Object> map = new HashMap<String, Object>();
212                 map.put("packageName", this.getPackageName());
213                 map.put("date", new Date());
214                 map.put("service", service);
215 
216                 // Generates the interface
217                 String path = getInterfacePath(pathInterface, service, SERVICE_FILE_SUFFIX);
218                 FreemarkerExecutor.processFreemarker(this.getFreemarkerConfig(), map, SERVICE_TEMPLATE, path);
219 
220                 // Generate the implementation
221                 path = getImplementationPath(pathImpl, service, SERVICE_FILE_SUFFIX);
222                 FreemarkerExecutor.processFreemarker(this.getFreemarkerConfig(), map, SERVICE_IMPL_TEMPLATE, path);
223             }
224 
225             if (multiInstance) {
226                 // Generate service directories
227                 for (Services service : this.getServiceData().getServices()) {
228                     // Builds the data to be merged with the template
229                     Map<String, Object> map = new HashMap<String, Object>();
230                     map.put("packageName", this.getPackageName());
231                     map.put("date", new Date());
232                     map.put("service", service);
233 
234                     // Generates the interface
235                     String path = getInterfacePath(pathInterface, service, SERVICE_DIRECTORY_FILE_SUFFIX);
236                     FreemarkerExecutor.processFreemarker(this.getFreemarkerConfig(), map, SERVICE_DIRECTORY_TEMPLATE, path);
237 
238                     // Generate the implementation
239                     path = getImplementationPath(pathImpl, service, SERVICE_DIRECTORY_FILE_SUFFIX);
240                     FreemarkerExecutor.processFreemarker(this.getFreemarkerConfig(), map, SERVICE_DIRECTORY_IMPL_TEMPLATE, path);
241                 }
242 
243                 // Generate database directory
244                 Map<String, Object> map = new HashMap<String, Object>();
245                 map.put("packageName", this.getPackageName());
246                 map.put("datasource", dataSourceName);
247                 map.put("date", new Date());
248                 map.put("services", getServiceData().getServices());
249 
250                 // Generates the interface
251                 String path = getInterfacePath(pathInterface, dataSourceName + DATABASE_DIRECTORY_FILE);
252                 FreemarkerExecutor.processFreemarker(this.getFreemarkerConfig(), map, DATABASE_DIRECTORY_TEMPLATE, path);
253 
254                 // Generate the implementation
255                 path = getImplementationPath(pathImpl, dataSourceName + DATABASE_DIRECTORY_FILE);
256                 FreemarkerExecutor.processFreemarker(this.getFreemarkerConfig(), map, DATABASE_DIRECTORY_IMPL_TEMPLATE, path);
257             }
258 
259             generateModuleTemplate(pathInterface);
260             generateModulePropertiesTemplate(pathModuleDesc);
261             updateHibernateConfigurationFile();
262 
263             result = true;
264         } catch (IOException e) {
265             this.getLogger().error(e.getMessage(), e);
266         } catch (TemplateException e) {
267             this.getLogger().error(e.getMessage(), e);
268         }
269         return result;
270     }
271 
272     /**
273      * Created the module descriptor file
274      *
275      * @param pathToDescriptor
276      *            the path to where the file should be created
277      * @throws IOException
278      * @throws TemplateException
279      */
280     private void generateModulePropertiesTemplate(String pathToDescriptor) throws IOException, TemplateException {
281 
282         // Reads the existing file and it's modules
283         String path = getModulePropertiesPath(pathToDescriptor);
284         StringBuffer previousModules = new StringBuffer();
285 
286         try {
287             Properties moduleFile = new Properties();
288             moduleFile.load(new FileInputStream(path));
289 
290             String[] previousModulesArray = moduleFile.getProperty("modules").split(",");
291 
292             for (String moduleName : previousModulesArray) {
293                 moduleName = moduleName.trim();
294 
295                 if (!moduleName.equals(this.getPackageName() + "." + MODEL_MODULE_NAME)) {
296                     previousModules.append(moduleName);
297                     previousModules.append(", ");
298                 }
299             }
300         } catch (FileNotFoundException exception) {
301             // Do nothing. If no file exists no previous modules exist for us to add. New clean file will be created
302         }
303 
304         // Builds the data to be merged with the template
305         Map<String, Object> map = new HashMap<String, Object>();
306         map.put("packageName", this.getPackageName());
307         map.put("modelClassName", MODEL_MODULE_NAME);
308         map.put("previousModules", previousModules.toString());
309         // Generate the implementation
310         FreemarkerExecutor.processFreemarker(this.getFreemarkerConfig(), map, MODULE_PROPERTIES_TEMPLATE, path);
311     }
312 
313     /**
314      * @param prefix
315      *            an indentation prefix to add to all generated lines
316      * @return the mapping section of hibernate.cfg.xml
317      */
318     private String getMappings(String prefix) {
319         String relativeDir = FileUtils.dotNotation2PathNotation(getPackageName() + ".data");
320         relativeDir = relativeDir.replace("\\", "/");
321         File hbms = new File(baseDir + "/src/main/resources/" + relativeDir);
322         StringBuffer buffer = new StringBuffer();
323 
324         // If the directory data exists get the *.hbm.xml files
325         if (hbms.exists() && hbms.isDirectory()) {
326             for (File file : hbms.listFiles()) {
327                 if (file.isDirectory()) {
328                     for (File innerFile : file.listFiles()) {
329                         if (innerFile.getName().contains(".hbm.xml")) {
330                             buffer.append(prefix + "<mapping resource=\"" + relativeDir + "/" + file.getName() + "/"
331                                     + innerFile.getName() + "\"/>\n");
332                         }
333                     }
334                 } else if (file.getName().contains(".hbm.xml")) {
335                     buffer.append(prefix + "<mapping resource=\"" + relativeDir + "/" + file.getName() + "\"/>\n");
336                 }
337             }
338         }
339 
340         return buffer.toString();
341     }
342 
343     /**
344      * @param line
345      *            the line to infer the indentation
346      * @return an indentation set of white spaces
347      */
348     private String getPrefixIdentation(String line) {
349         StringBuffer prefix = new StringBuffer("");
350         int i = 0;
351         boolean more = true;
352 
353         while (more) {
354             if (line.charAt(i) == ' ')
355                 prefix.append(' ');
356             else if (line.charAt(i) == '\t')
357                 prefix.append("    ");
358             else
359                 more = false;
360 
361             i++;
362         }
363 
364         return prefix.toString();
365     }
366 
367     /**
368      * Updates the projects existing hibernate configuration file with all mappings
369      */
370     private void updateHibernateConfigurationFile() {
371         String pathToFile = resourcesPath + File.separator + dataSourceName + ".cfg.xml";
372         File cfg = new File(pathToFile);
373 
374         StringBuffer contents = new StringBuffer();
375         BufferedReader input = null;
376         try {
377             input = new BufferedReader(new FileReader(cfg));
378             String line = null;
379             boolean hasWrittenMappings = false;
380             boolean noLineSeparator = false;
381 
382             while ((line = input.readLine()) != null) {
383                 noLineSeparator = false;
384 
385                 if (line.contains("<mapping resource")) {
386                     if (hasWrittenMappings)
387                         // Ignores the current line.
388                         line = null;
389                     else {
390                         line = getMappings(getPrefixIdentation(line));
391                         hasWrittenMappings = true;
392                         noLineSeparator = true;
393                     }
394                 }
395 
396                 if (!hasWrittenMappings && (line != null) && line.contains("</session-factory>")) {
397                     line = getMappings("    " + getPrefixIdentation(line)) + line;
398                     hasWrittenMappings = true;
399                 }
400 
401                 if (line != null) {
402                     contents.append(line);
403                     if (!noLineSeparator)
404                         contents.append(System.getProperty("line.separator"));
405                 }
406             }
407 
408         } catch (IOException ex) {
409             contents = null;
410             ex.printStackTrace();
411 
412         } finally {
413             try {
414                 if (input != null)
415                     input.close();
416             } catch (IOException ex) {
417                 ex.printStackTrace();
418             }
419         }
420 
421         // Not null. file exists and context parsed/updated
422         if (contents != null) {
423             Writer output = null;
424 
425             try {
426                 output = new BufferedWriter(new FileWriter(cfg));
427                 output.write(contents.toString());
428 
429             } catch (IOException ex) {
430                 ex.printStackTrace();
431             } finally {
432                 try {
433                     if (output != null)
434                         output.close();
435 
436                 } catch (IOException ex) {
437                     ex.printStackTrace();
438                 }
439             }
440         }
441     }
442 
443     /**
444      * @param pathInterface
445      * @throws IOException
446      * @throws TemplateException
447      */
448     private void generateModuleTemplate(String pathInterface) throws IOException, TemplateException {
449         // Builds the data to be merged with the template
450         Map<String, Object> map = new HashMap<String, Object>();
451         map.put("packageName", this.getPackageName());
452         map.put("date", new Date());
453         map.put("modelClassName", MODEL_MODULE_NAME);
454         map.put("serviceCfg", this.getServiceData());
455         // Generate the implementation
456         String path = getModulePath(pathInterface);
457         FreemarkerExecutor.processFreemarker(this.getFreemarkerConfig(), map, MODULE_TEMPLATE, path);
458     }
459 
460     /**
461      * Creates the path for the interface files
462      *
463      * @param pathInterface
464      *            The base file path for the interface
465      * @param service
466      * @param sufix
467      * @return The interface file path
468      */
469     private String getInterfacePath(String pathInterface, Services service, String sufix) {
470         String path = pathInterface + File.separator + "I" + service.getName() + sufix + JAVA_EXTENSION;
471         return path;
472     }
473 
474     /**
475      * Creates the path for the implementation files
476      *
477      * @param pathImpl
478      *            The base file path for the interface
479      * @param service
480      *            service name
481      * @param sufix
482      * @return The implementation file path
483      */
484     private String getImplementationPath(String pathImpl, Services service, String sufix) {
485         String path;
486         path = pathImpl + File.separator + service.getName() + sufix + IMPL_FILE_SUFFIX + JAVA_EXTENSION;
487         return path;
488     }
489 
490     /**
491      * Creates the path for the interface files
492      *
493      * @param pathInterface
494      *            The base file path for the interface
495      * @param sufix
496      * @return The interface file path
497      */
498     private String getInterfacePath(String pathInterface, String sufix) {
499         String path = pathInterface + File.separator + "I" + sufix + JAVA_EXTENSION;
500         return path;
501     }
502 
503     /**
504      * Creates the path for the implementation files
505      *
506      * @param pathImpl
507      *            The base file path for the interface
508      * @param sufix
509      * @return The implementation file path
510      */
511     private String getImplementationPath(String pathImpl, String sufix) {
512         String path;
513         path = pathImpl + File.separator + sufix + IMPL_FILE_SUFFIX + JAVA_EXTENSION;
514         return path;
515     }
516 
517     /**
518      * Creates the path for the implementation files
519      *
520      * @param pathImpl
521      *            The base file path for the interface
522      * @return The implementation file path
523      */
524     private String getModulePath(String pathImpl) {
525         String path;
526         path = pathImpl + File.separator + MODEL_MODULE_NAME + JAVA_EXTENSION;
527         return path;
528     }
529 
530     /**
531      * Creates the path for the implementation files
532      *
533      * @param pathImpl
534      *            The base file path for the interface
535      * @return The implementation file path
536      */
537     private String getModulePropertiesPath(String pathImpl) {
538         String path;
539         path = pathImpl + File.separator + MODULE_PROPERTIES_NAME + PROPERTIES_EXTENSION;
540         return path;
541     }
542 
543     /**
544      * @return the serviceData
545      */
546     public ServiceCfg getServiceData() {
547         return serviceData;
548     }
549 
550     /**
551      * @param serviceData
552      *            the serviceData to set
553      */
554     private void setServiceData(ServiceCfg serviceData) {
555         this.serviceData = serviceData;
556     }
557 
558     /**
559      * @return the serviceConfigurationFileName
560      */
561     private String getServiceConfigurationFileName() {
562         return serviceConfigurationFileName;
563     }
564 
565     /**
566      * @param serviceConfigurationFilePath
567      *            the serviceConfigurationFileName to set
568      */
569     private void setServiceConfigurationFileName(String serviceConfigurationFilePath) {
570         this.serviceConfigurationFileName = serviceConfigurationFilePath;
571     }
572 
573     /**
574      * @return the logger
575      */
576     private Log getLogger() {
577         return logger;
578     }
579 
580     /**
581      * @param logger
582      *            the logger to set
583      */
584     private void setLogger(Log logger) {
585         this.logger = logger;
586     }
587 }