View Javadoc
1   /*
2    * Tentackle - https://tentackle.org
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17   */
18  
19  package org.tentackle.persist.wurblet;
20  
21  import freemarker.template.TemplateException;
22  import org.wurbelizer.misc.Constants;
23  import org.wurbelizer.wurbel.HeapStream;
24  import org.wurbelizer.wurbel.WurbelException;
25  import org.wurbelizer.wurbel.WurbelTerminationException;
26  import org.wurbelizer.wurbel.Wurbler;
27  
28  import org.tentackle.buildsupport.codegen.AbstractGenerator;
29  import org.tentackle.buildsupport.codegen.GeneratedFile;
30  import org.tentackle.buildsupport.codegen.TemplateModel;
31  import org.tentackle.common.NamingRules;
32  import org.tentackle.common.StringHelper;
33  import org.tentackle.model.Entity;
34  import org.tentackle.wurblet.ModelWurblet;
35  
36  import java.io.File;
37  import java.io.IOException;
38  import java.io.PrintStream;
39  import java.util.regex.Pattern;
40  
41  /**
42   * Handles generated remote interface and implementation code.
43   * <p>
44   * Creates the TRIP-java files from freemarker templates, if missing.
45   *
46   * @author harald
47   */
48  public class RemoteIncludes {
49  
50    /** template parameters and variables. */
51    private final class TemplatePars {
52  
53      private final TemplateModel model;              // the template model (for pdo or operation)
54      private final AbstractGenerator generator;      // the template generator
55  
56      private TemplatePars() throws WurbelException {
57  
58        NamingRules namingRules = NamingRules.getInstance();
59        String popPackage = wurblet.determinePackageName(popInterface); // package name of the pdo or operation interface
60  
61        String superPackage;              // package name of the remote super interface
62        String superImplPackage;          // package name of the remote super implementation
63        String superInterface;            // name of the remote super interface
64        String superImplementation;       // name of the remote super implementation
65  
66        switch (superClassName) {
67          case "AbstractPersistentObject":
68            superInterface = "AbstractPersistentObjectRemoteDelegate";
69            superPackage = "org.tentackle.persist.trip";
70            superImplementation = "AbstractPersistentObjectRemoteDelegateImpl";
71            superImplPackage = "org.tentackle.persist.trip";
72            break;
73  
74          case "AbstractPersistentOperation":
75            superInterface = "AbstractPersistentOperationRemoteDelegate";
76            superPackage = "org.tentackle.persist.trip";
77            superImplementation = "AbstractPersistentOperationRemoteDelegateImpl";
78            superImplPackage = "org.tentackle.persist.trip";
79            break;
80  
81          case "AbstractDbObject":
82            // just convenience for TT-builds...
83            superInterface = "AbstractDbObjectRemoteDelegate";
84            superPackage = "org.tentackle.dbms.trip";
85            superImplementation = "AbstractDbObjectRemoteDelegateImpl";
86            superImplPackage = "org.tentackle.dbms.trip";
87            break;
88  
89          default:
90            String superJavaClassName = StringHelper.getPlainClassName(wurblet.getSuperClassName());
91            int dotNdx = superJavaClassName.lastIndexOf('.');
92            String superClassPackageName = dotNdx > 0 ?
93                                           superJavaClassName.substring(0, dotNdx) :   // fully qualified
94                                           wurblet.determinePackageName(superJavaClassName);
95            if (wurblet.isOperation()) {
96              superInterface = namingRules.getOperationRemoteInterface(superClassName);
97              superPackage = namingRules.getOperationRemoteInterfacePackageName(superClassPackageName);
98              superImplementation = namingRules.getOperationRemoteImplementation(superClassName);
99              superImplPackage = namingRules.getOperationRemoteImplementationPackageName(superClassPackageName);
100           }
101           else {
102             superInterface = namingRules.getPdoRemoteInterface(superClassName);
103             superPackage = namingRules.getPdoRemoteInterfacePackageName(superClassPackageName);
104             superImplementation = namingRules.getPdoRemoteImplementation(superClassName);
105             superImplPackage = namingRules.getPdoRemoteImplementationPackageName(superClassPackageName);
106           }
107       }
108 
109       model = new TemplateModel();
110       if (wurblet.isOperation()) {
111         model.putValue("operationInterface", popInterface);
112         model.putValue("operationPackage", popPackage);
113         String sourceText = wurblet.getContainer().getProperty(Wurbler.PROPSPACE_WURBLET, Wurbler.WURBLET_FILECONTENTS);
114         model.putValue("abstractOperation",
115                        sourceText != null &&
116                        Pattern.compile("\\s+extends\\s+" + popInterface + "\\s*<").matcher(sourceText).find());
117       }
118       else {
119         model.putValue("pdoInterface", popInterface);
120         model.putValue("pdoPackage", popPackage);
121         model.putValue("pdoInheritance", entity != null ? entity.getInheritanceType() : "");
122       }
123       model.putValue("persistenceImplementation", className);
124       model.putValue("persistenceImplPackage", packageName);
125       model.putValue("remoteInterface", remoteInterface);
126       model.putValue("remotePackage", remotePackage);
127       model.putValue("remoteImplementation", remoteImplementation);
128       model.putValue("remoteImplPackage", remoteImplPackage);
129       model.putValue("superInterface", superInterface);
130       model.putValue("superPackage", superPackage);
131       model.putValue("superImplementation", superImplementation);
132       model.putValue("superImplPackage", superImplPackage);
133 
134       String templateDir = wurblet.getContainer().getProperty(Wurbler.PROPSPACE_EXTRA, "templateDir");
135       if (templateDir == null) {
136         String projectRoot = wurblet.getContainer().getProperty(Wurbler.PROPSPACE_EXTRA, "projectRoot");
137         if (projectRoot == null) {
138           throw new WurbelTerminationException("neither the WurbletProperty \"templateDir\" nor \"projectRoot\" were set.");
139         }
140         templateDir = projectRoot + File.separator + "templates" +
141                       File.separator + (wurblet.isOperation() ? "operation" : "pdo");
142       }
143       generator = new AbstractGenerator() {};
144       generator.setTemplateDirectory(new File(templateDir));
145     }
146   }
147 
148   private final ModelWurblet wurblet;             // the model wurblet
149   private final Entity entity;                    // the entity associated to the wurblet
150   private final String className;                 // classname of persistence object
151   private final String packageName;               // package of the persistence implementation class
152   private final String superClassName;            // name of the superclass
153   private final HeapStream interfaceStream;       // stream for remote interface wurblet generated code
154   private final HeapStream implementationStream;  // stream for remote implementation wurblet generated code
155 
156   private final String popInterface;              // classname of the pdo or operation interface
157   private final String remotePackage;             // package name of the remote interface
158   private final String remoteInterface;           // classname of the remote interface
159   private final String remoteImplPackage;         // package name of the remote implementation
160   private final String remoteImplementation;      // classname of the remote implementation
161 
162   private TemplatePars templatePars;              // template variables (null if not created yet)
163 
164   /**
165    * Create remote includes.
166    *
167    * @param wurblet the wurblet
168    * @throws WurbelException if failed
169    */
170   public RemoteIncludes(ModelWurblet wurblet) throws WurbelException {
171 
172     this.wurblet = wurblet;
173 
174     className = wurblet.getClassName();
175     entity = wurblet.getEntity();
176     superClassName = entity != null && entity.getSuperEntityName() != null ?
177                        entity.getSuperEntityName() :
178                        StringHelper.getPlainClassName(wurblet.getSuperClassName());
179     packageName = wurblet.getPackageName();
180     popInterface = determinePopInterface(wurblet);
181 
182     NamingRules namingRules = NamingRules.getInstance();
183     Wurbler container = wurblet.getContainer();
184 
185     if (wurblet.isOperation()) {
186       remotePackage = namingRules.getOperationRemoteInterfacePackageName(packageName);
187       remoteInterface = namingRules.getOperationRemoteInterface(popInterface);
188       remoteImplPackage = namingRules.getOperationRemoteImplementationPackageName(packageName);
189       remoteImplementation = namingRules.getOperationRemoteImplementation(popInterface);
190     }
191     else {
192       remotePackage = namingRules.getPdoRemoteInterfacePackageName(packageName);
193       remoteInterface = namingRules.getPdoRemoteInterface(popInterface);
194       remoteImplPackage = namingRules.getPdoRemoteImplementationPackageName(packageName);
195       remoteImplementation = namingRules.getPdoRemoteImplementation(popInterface);
196     }
197 
198     // find directory for remote delegates (must be in the same module as the persistence implementation!)
199     String dirName = container.getProperty(Wurbler.PROPSPACE_WURBLET, Wurbler.WURBLET_DIRNAME);
200     String packagePath = packageName.replace('.', File.separatorChar);
201     int ndx = dirName.indexOf(packagePath);
202     String pathPrefix = ndx > 0 ? dirName.substring(0, ndx) : "";
203 
204     String remoteDirName = pathPrefix + File.separator + remotePackage.replace('.', File.separatorChar);
205     String remoteImplDirName = pathPrefix + File.separator + remoteImplPackage.replace('.', File.separatorChar);
206 
207     // create remote stream and file
208     interfaceStream = new HeapStream(container, remoteInterface + "/methods");
209     checkFile(remoteDirName, remoteInterface, "RemoteInterface.ftl");
210 
211     // create impl stream and file
212     implementationStream = new HeapStream(container, remoteImplementation + "/methods");
213     checkFile(remoteImplDirName, remoteImplementation, "RemoteImplementation.ftl");
214   }
215 
216   /**
217    * Marks the heap files as discarded.<br>
218    * Such files should not be used to generate code because their contents are incomplete
219    * because of former errors. Wurblets reading this file should throw a {@link org.wurbelizer.wurbel.WurbelDiscardException}.
220    */
221   public void discard() {
222     implementationStream.discard();
223     interfaceStream.discard();
224   }
225 
226   /**
227    * Gets the print stream of the remote interface.
228    *
229    * @return get the remote stream
230    */
231   public PrintStream getInterfaceStream()  {
232     return interfaceStream.getStream();
233   }
234 
235   /**
236    * Gets the print stream of the remote implementation.
237    *
238    * @return the impl stream
239    */
240   public PrintStream getImplementationStream()  {
241     return implementationStream.getStream();
242   }
243 
244 
245   /**
246    * Determines the interface name of the PDO or operation.
247    *
248    * @param wurblet the model wurblet from within the persistence implementation java file
249    * @return the proxy interface
250    * @throws WurbelException if fallback classname could not be determined
251    */
252   private String determinePopInterface(ModelWurblet wurblet) throws WurbelException {
253     String popInterface = null;
254     try {
255       popInterface = wurblet.getPdoClassName();
256     }
257     catch (WurbelException ex) {
258       // run into fallback below
259     }
260     if (popInterface == null) {
261       popInterface = wurblet.getClassName();   // fallback if simple object
262     }
263     return popInterface;
264   }
265 
266   /**
267    * Check if remote file exists. If not, generate it.
268    */
269   private void checkFile(String dirName, String remoteName, String templateName) throws WurbelException {
270     File dir = new File(dirName);
271     File file = new File(dir, remoteName + Constants.JAVA_SOURCE_EXTENSION);
272     if (!file.exists()) {
273       // generate it with freemarker
274       dir.mkdirs();
275       if (templatePars == null) {
276         templatePars = new TemplatePars();
277       }
278       try {
279         new GeneratedFile(templatePars.generator.createFreemarkerConfiguration(),
280                           templatePars.model, templateName, file).generate();
281       }
282       catch (IOException | TemplateException e) {
283         throw new WurbelTerminationException("cannot generate " + file + " from " + templateName, e);
284       }
285     }
286   }
287 
288 }