KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > language > generator > ProgramGeneratorImpl


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.components.language.generator;
17
18 import org.apache.avalon.framework.activity.Disposable;
19 import org.apache.avalon.framework.component.ComponentException;
20 import org.apache.avalon.framework.component.ComponentManager;
21 import org.apache.avalon.framework.component.ComponentSelector;
22 import org.apache.avalon.framework.component.Composable;
23 import org.apache.avalon.framework.component.Recomposable;
24 import org.apache.avalon.framework.context.Context;
25 import org.apache.avalon.framework.context.ContextException;
26 import org.apache.avalon.framework.context.Contextualizable;
27 import org.apache.avalon.framework.logger.AbstractLogEnabled;
28 import org.apache.avalon.framework.parameters.ParameterException;
29 import org.apache.avalon.framework.parameters.Parameterizable;
30 import org.apache.avalon.framework.parameters.Parameters;
31 import org.apache.avalon.framework.thread.ThreadSafe;
32 import org.apache.cocoon.Constants;
33 import org.apache.cocoon.ProcessingException;
34 import org.apache.cocoon.components.classloader.ClassLoaderManager;
35 import org.apache.cocoon.components.language.LanguageException;
36 import org.apache.cocoon.components.language.markup.MarkupLanguage;
37 import org.apache.cocoon.components.language.programming.CodeFormatter;
38 import org.apache.cocoon.components.language.programming.Program;
39 import org.apache.cocoon.components.language.programming.ProgrammingLanguage;
40 import org.apache.cocoon.environment.SourceResolver;
41 import org.apache.cocoon.util.IOUtils;
42 import org.apache.excalibur.source.Source;
43
44 import java.io.File JavaDoc;
45 import java.net.MalformedURLException JavaDoc;
46
47 /**
48  * The default implementation of <code>ProgramGenerator</code>
49  *
50  * @author <a HREF="mailto:ricardo@apache.org">Ricardo Rocha</a>
51  * @author <a HREF="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
52  * @author <a HREF="mailto:tcurdt@apache.org">Torsten Curdt</a>
53  * @version CVS $Id: ProgramGeneratorImpl.java 179149 2005-05-30 21:21:21Z anathaniel $
54  */

55 public class ProgramGeneratorImpl extends AbstractLogEnabled
56     implements ProgramGenerator, Contextualizable, Composable, Parameterizable,
57                Disposable, ThreadSafe {
58
59     /** The auto-reloading option */
60     protected boolean autoReload = true;
61
62     /** The pre-loading option */
63     protected boolean preload = false;
64
65     /** The check for manual source changes in the repository*/
66     protected boolean watchSource = false;
67
68     /**
69      * The ComponentSelector for programs. Caches Program by program
70      * source file.
71      */

72     protected GeneratorSelector cache;
73
74     /** The component manager */
75     protected ComponentManager manager;
76
77     /** The markup language component selector */
78     protected ComponentSelector markupSelector;
79
80     /** The programming language component selector */
81     protected ComponentSelector languageSelector;
82
83     /** The working directory */
84     protected File JavaDoc workDir;
85
86     /** The ClassLoaderManager */
87     protected ClassLoaderManager classManager;
88
89     /** The root package */
90     protected String JavaDoc rootPackage;
91
92     /** Servlet Context Directory */
93     protected String JavaDoc contextDir;
94
95
96     /** Contextualize this class */
97     public void contextualize(Context context) throws ContextException {
98         if (this.workDir == null) {
99             this.workDir = (File JavaDoc) context.get(Constants.CONTEXT_WORK_DIR);
100         }
101
102         if (this.contextDir == null) {
103             org.apache.cocoon.environment.Context ctx =
104                 (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
105
106             // Determine the context directory, preferably as a file
107
// FIXME (SW) - this is purposely redundant with some code in CocoonServlet
108
// to have the same rootPath. How to avoid this ?
109
try {
110                 String JavaDoc rootPath = ctx.getRealPath("/");
111                 if (rootPath != null) {
112                     this.contextDir = new File JavaDoc(rootPath).toURL().toExternalForm();
113                 } else {
114                     String JavaDoc webInf = ctx.getResource("/WEB-INF").toExternalForm();
115                     this.contextDir = webInf.substring(0, webInf.length() - "WEB-INF".length());
116                 }
117                 if (getLogger().isDebugEnabled()) {
118                     getLogger().debug("Context directory is " + this.contextDir);
119                 }
120             } catch (MalformedURLException JavaDoc e) {
121                 getLogger().warn("Could not get context directory", e);
122                 this.contextDir = "";
123             }
124         }
125     }
126
127     /**
128      * Set the global component manager. This method also sets the
129      * <code>ComponentSelector</code> used as language factory for both markup
130      * and programming languages.
131      * @param manager The global component manager
132      */

133     public void compose(ComponentManager manager) throws ComponentException {
134         if (this.manager == null && manager != null) {
135             this.manager = manager;
136             this.cache = (GeneratorSelector) this.manager.lookup(GeneratorSelector.ROLE + "Selector");
137             this.markupSelector = (ComponentSelector)this.manager.lookup(MarkupLanguage.ROLE + "Selector");
138             this.languageSelector = (ComponentSelector)this.manager.lookup(ProgrammingLanguage.ROLE + "Selector");
139             this.classManager = (ClassLoaderManager)this.manager.lookup(ClassLoaderManager.ROLE);
140         }
141     }
142
143     /**
144      * Set the sitemap-provided configuration. This method sets the persistent code repository and the auto-reload option
145      * @param params The configuration information
146      * @exception ParameterException Not thrown here
147      */

148     public void parameterize(Parameters params) throws ParameterException {
149         this.autoReload = params.getParameterAsBoolean("auto-reload", autoReload);
150         this.rootPackage = params.getParameter("root-package", "org.apache.cocoon.www");
151         this.preload = params.getParameterAsBoolean("preload", preload);
152         this.watchSource = params.getParameterAsBoolean("watch-source", watchSource);
153     }
154
155     /**
156      * Generates program source file name in the working directory
157      * from the source SystemID
158      */

159     private String JavaDoc getNormalizedName(final String JavaDoc systemId) {
160         StringBuffer JavaDoc contextFilename = new StringBuffer JavaDoc(this.rootPackage.replace('.', File.separatorChar));
161         contextFilename.append(File.separator);
162         if(systemId.startsWith(this.contextDir)) {
163             // VG: File is located under contextDir; using relative file name ...
164
contextFilename.append(systemId.substring(this.contextDir.length()));
165         } else {
166             // VG: File is located outside of contextDir; using systemId ...
167
contextFilename.append(systemId);
168         }
169         return IOUtils.normalizedFilename(contextFilename.toString());
170     }
171
172     /**
173      * Load a program built from an XML document written in a <code>MarkupLanguage</code>
174      *
175      * @param fileName The input document's <code>File</code>
176      * @param markupLanguageName The <code>MarkupLanguage</code> in which the input document is written
177      * @param programmingLanguageName The <code>ProgrammingLanguage</code> in which the program must be written
178      * @return The loaded program instance
179      * @exception Exception If an error occurs during generation or loading
180      * @deprecated Pass Source object instead of file name.
181      */

182     public CompiledComponent load(ComponentManager newManager,
183                                   String JavaDoc fileName,
184                                   String JavaDoc markupLanguageName,
185                                   String JavaDoc programmingLanguageName,
186                                   SourceResolver resolver)
187     throws Exception JavaDoc {
188
189         final Source source = resolver.resolveURI(fileName);
190         try {
191             return load(newManager, source, markupLanguageName, programmingLanguageName, resolver);
192         } finally {
193             resolver.release(source);
194         }
195     }
196
197     /**
198      * Load a program built from an XML document written in a <code>MarkupLanguage</code>.
199      *
200      * This method does not releases passed source object. Caller of the method must release
201      * source when needed.
202      *
203      * @param source The input document's <code>File</code>
204      * @param markupLanguageName The <code>MarkupLanguage</code> in which the input document is written
205      * @param programmingLanguageName The <code>ProgrammingLanguage</code> in which the program must be written
206      * @return The loaded program instance
207      * @exception Exception If an error occurs during generation or loading
208      */

209     public CompiledComponent load(ComponentManager newManager,
210                                   Source source,
211                                   String JavaDoc markupLanguageName,
212                                   String JavaDoc programmingLanguageName,
213                                   SourceResolver resolver)
214     throws Exception JavaDoc {
215
216         final String JavaDoc id = source.getURI();
217
218         ProgrammingLanguage programmingLanguage = null;
219         MarkupLanguage markupLanguage = null;
220         try {
221             // Create file name for the program generated from the provided source.
222
final String JavaDoc normalizedName = getNormalizedName(id);
223
224             if (getLogger().isDebugEnabled()) {
225                 getLogger().debug("Loading serverpage systemId=[" + id + "]" +
226                     " markupLanguageName=[" + markupLanguageName + "]" +
227                     " programmingLanguageName=[" + programmingLanguageName + "]" +
228                     " -> normalizedName=[" + normalizedName + "]");
229             }
230
231             markupLanguage = (MarkupLanguage) this.markupSelector.select(markupLanguageName);
232             programmingLanguage = (ProgrammingLanguage) this.languageSelector.select(programmingLanguageName);
233             programmingLanguage.setLanguageName(programmingLanguageName);
234
235             Program program = null;
236             CompiledComponent programInstance = null;
237
238             // Attempt to load program object from cache
239
try {
240                 programInstance = (CompiledComponent) this.cache.select(normalizedName);
241             } catch (Exception JavaDoc e) {
242                 if (getLogger().isDebugEnabled()) {
243                     getLogger().debug("The serverpage [" + id + "] is not in the cache yet");
244                 }
245             }
246
247             if (programInstance == null && this.preload) {
248                 // Preloading: Load program if its source/[object file] is available
249
try {
250                     program = programmingLanguage.preload(normalizedName,
251                                                           this.workDir,
252                                                           markupLanguage.getEncoding());
253
254                     this.cache.addGenerator(newManager, normalizedName, program);
255                     programInstance = (CompiledComponent) this.cache.select(normalizedName);
256
257                     if (getLogger().isDebugEnabled()) {
258                         getLogger().debug("Successfully preloaded serverpage [" + id + "]");
259                     }
260                 } catch (Exception JavaDoc e) {
261                     if (getLogger().isInfoEnabled()) {
262                         getLogger().info("The serverpage [" + id
263                                          + "] could not be preloaded, will be re-created ("
264                                          + e + ")");
265                     }
266                 }
267             }
268
269             if (programInstance == null) {
270                 synchronized (this) {
271                     // Attempt again to load program object from cache.
272
// This avoids that simultaneous requests recompile
273
// the same XSP over and over again.
274
try {
275                         programInstance = (CompiledComponent) this.cache.select(normalizedName);
276                         if (getLogger().isDebugEnabled()) {
277                             getLogger().debug("The serverpage [" + id + "] was now in the cache");
278                         }
279                     } catch (Exception JavaDoc e) {
280                         // no instance found
281
if (getLogger().isDebugEnabled()) {
282                             getLogger().debug("Creating new serverpage for [" + id + "]");
283                         }
284                         generateSourcecode(source,
285                                            normalizedName,
286                                            markupLanguage,
287                                            programmingLanguage);
288
289                         programInstance = loadProgram(newManager,
290                                                       normalizedName,
291                                                       markupLanguage,
292                                                       programmingLanguage);
293                     }
294                 }
295             } else {
296                 // found an instance
297
if (this.autoReload) {
298                     long sourceLastModified = source.getLastModified();
299                     // Has XSP changed?
300
// Note : lastModified can be 0 if source is dynamically generated.
301
// In that case, let the program instance decide if it is modified or not.
302
if (programInstance.modifiedSince(sourceLastModified)) {
303                         if (getLogger().isDebugEnabled()) {
304                             getLogger().debug("ReCreating serverpage for [" + id + "]");
305                         }
306                         synchronized (this) {
307                             if (getLogger().isDebugEnabled()) {
308                                 getLogger().debug("Releasing old serverpage program [" + id + "]");
309                             }
310                             release(programInstance);
311                             programmingLanguage.unload(program, normalizedName, this.workDir);
312                             this.cache.removeGenerator(normalizedName);
313                             programInstance = null;
314                             program = null;
315
316                             generateSourcecode(source,
317                                                normalizedName,
318                                                markupLanguage,
319                                                programmingLanguage);
320
321                             programInstance = loadProgram(newManager,
322                                                           normalizedName,
323                                                           markupLanguage,
324                                                           programmingLanguage);
325                         }
326                     } else {
327                         // check the repository for changes at all?
328
if (this.watchSource) {
329                             if (getLogger().isDebugEnabled()) {
330                                 getLogger().debug("Checking sourcecode of [" + id + "] for a change");
331                             }
332                             File JavaDoc sourcecodeFile = new File JavaDoc(this.workDir,
333                                                            normalizedName + "." + programmingLanguage.getSourceExtension());
334                             // has sourcecode in repository changed ?
335
if (sourcecodeFile != null && sourcecodeFile.exists()) {
336                                 long sourcecodeLastModified = sourcecodeFile.lastModified();
337                                 if (sourcecodeLastModified > sourceLastModified
338                                         || sourceLastModified == 0
339                                         || sourcecodeLastModified == 0) {
340                                     if (getLogger().isDebugEnabled()) {
341                                         getLogger().debug("Create new serverpage program for [" + id + "] - repository has changed");
342                                     }
343                                     synchronized (this) {
344                                         if (getLogger().isDebugEnabled()) {
345                                             getLogger().debug("Releasing old serverpage program [" + id + "]");
346                                         }
347                                         release(programInstance);
348                                         //programmingLanguage.unload(program, normalizedName, this.workDir);
349
this.cache.removeGenerator(normalizedName);
350                                         programInstance = null;
351                                         program = null;
352
353                                         programInstance = loadProgram(newManager,
354                                                                       normalizedName,
355                                                                       markupLanguage,
356                                                                       programmingLanguage);
357                                     }
358                                 } else {
359                                     if (getLogger().isDebugEnabled()) {
360                                         getLogger().debug("Sourcecode of [" + id + "] has not changed - returning program from cache");
361                                     }
362                                 }
363                             } else {
364                                 if (getLogger().isErrorEnabled()) {
365                                     getLogger().error("Could not find sourcecode for [" + id + "]");
366                                 }
367                             }
368                         }
369                     }
370                 } else {
371                     if (getLogger().isDebugEnabled()) {
372                         getLogger().debug("Not checking for modifications [autoReload=false] - using current version");
373                     }
374                 }
375             }
376
377             // Recompose with the new manager if program needs it.
378
// This is required to provide XSP with manager from the correct
379
// sitemap so it will be able to find all components declared in
380
// the sitemap.
381
if (programInstance instanceof Recomposable) {
382                 ((Recomposable) programInstance).recompose(newManager);
383             }
384
385             return (programInstance);
386         } finally {
387             this.markupSelector.release(markupLanguage);
388             this.languageSelector.release(programmingLanguage);
389         }
390     }
391
392     private CompiledComponent loadProgram(ComponentManager newManager,
393                                           String JavaDoc normalizedName,
394                                           MarkupLanguage markupLanguage,
395                                           ProgrammingLanguage programmingLanguage)
396             throws Exception JavaDoc {
397
398         CompiledComponent programInstance = null;
399
400         try {
401             return (CompiledComponent) this.cache.select(normalizedName);
402         } catch (Exception JavaDoc e) {
403         }
404
405         try {
406             if (getLogger().isDebugEnabled()) {
407                 getLogger().debug("Loading program [" + normalizedName + "]");
408             }
409             Program program = programmingLanguage.load(normalizedName, this.workDir, markupLanguage.getEncoding());
410
411             this.cache.addGenerator(newManager, normalizedName, program);
412             if (getLogger().isDebugEnabled()) {
413                 getLogger().debug("Successfully loaded program [" + normalizedName + "]");
414             }
415         } catch (LanguageException le) {
416             if (getLogger().isDebugEnabled()) {
417                 getLogger().debug("Got Language Exception", le);
418             }
419             throw new ProcessingException("Language Exception", le);
420         }
421
422         try {
423             programInstance = (CompiledComponent) this.cache.select(normalizedName);
424         } catch (Exception JavaDoc cme) {
425             if (getLogger().isDebugEnabled()) {
426                 getLogger().debug("Can't load ServerPage: got exception", cme);
427             }
428             throw new ProcessingException("Can't load ServerPage", cme);
429         }
430
431         return (programInstance);
432     }
433
434
435     private void generateSourcecode(Source source,
436                                     String JavaDoc normalizedName,
437                                     MarkupLanguage markupLanguage,
438                                     ProgrammingLanguage programmingLanguage)
439             throws Exception JavaDoc {
440
441         if (getLogger().isDebugEnabled()) {
442             getLogger().debug("Creating sourcecode for [" + source.getURI() + "]");
443         }
444
445         // Generate code
446
String JavaDoc code = markupLanguage.generateCode(source, normalizedName, programmingLanguage);
447         if (code == null || code.length() == 0) {
448             // FIXME(VG): Xalan with incremental-processing=true does not propagate exceptions
449
// from working thread to main thread. See
450
// http://nagoya.apache.org/bugzilla/show_bug.cgi?id=8033
451
throw new ProcessingException("Failed to generate program code (this may happen " +
452                     "if you use Xalan in incremental processing mode). " +
453                     "Please check log file and/or console for errors.");
454         }
455
456         String JavaDoc encoding = markupLanguage.getEncoding();
457
458         // Format source code if applicable
459
CodeFormatter codeFormatter = programmingLanguage.getCodeFormatter();
460         if (codeFormatter != null) {
461             code = codeFormatter.format(code, encoding);
462         }
463
464         // Store generated code
465
final File JavaDoc sourceFile = new File JavaDoc(this.workDir, normalizedName + "." + programmingLanguage.getSourceExtension());
466         final File JavaDoc sourceDir = sourceFile.getParentFile();
467         if (sourceDir != null) {
468             sourceDir.mkdirs();
469         }
470         IOUtils.serializeString(sourceFile, code);
471         if (getLogger().isDebugEnabled()) {
472             getLogger().debug("Successfully created sourcecode for [" + source.getURI() + "]");
473         }
474     }
475
476     /**
477      * Releases the program instance.
478      * @param component program instance to be released
479      */

480     public void release(CompiledComponent component) {
481         this.cache.release(component);
482     }
483
484     /**
485      * Removes named program from the program generator's cache.
486      * Disposes all created instances of the program.
487      * @param source of the program to be removed
488      */

489     public void remove(Source source) {
490         final String JavaDoc normalizedName = getNormalizedName(source.getURI());
491         this.cache.removeGenerator(normalizedName);
492     }
493
494     /**
495      * dispose
496      */

497     public void dispose() {
498         this.manager.release(this.cache);
499         this.cache = null;
500         this.manager.release(this.markupSelector);
501         this.markupSelector = null;
502         this.manager.release(this.languageSelector);
503         this.languageSelector = null;
504         this.manager.release(this.classManager);
505         this.classManager = null;
506
507         this.manager = null;
508
509         this.workDir = null;
510         this.contextDir = null;
511     }
512 }
513
Popular Tags