View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jci.listeners;
19  
20  import java.io.File;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  
24  import org.apache.commons.jci.compilers.CompilationResult;
25  import org.apache.commons.jci.compilers.JavaCompiler;
26  import org.apache.commons.jci.compilers.JavaCompilerFactory;
27  import org.apache.commons.jci.monitor.FilesystemAlterationObserver;
28  import org.apache.commons.jci.problems.CompilationProblem;
29  import org.apache.commons.jci.readers.FileResourceReader;
30  import org.apache.commons.jci.readers.ResourceReader;
31  import org.apache.commons.jci.stores.MemoryResourceStore;
32  import org.apache.commons.jci.stores.ResourceStore;
33  import org.apache.commons.jci.stores.TransactionalResourceStore;
34  import org.apache.commons.jci.utils.ConversionUtils;
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  
38  /**
39   * A CompilingListener is an improved version of the ReloadingListener.
40   * It even compiles the classes from source before doing the reloading.
41   * 
42   * @author tcurdt
43   */
44  public class CompilingListener extends ReloadingListener {
45  
46      private final Log log = LogFactory.getLog(CompilingListener.class);
47      
48      private final JavaCompiler compiler;
49      private final TransactionalResourceStore transactionalStore;
50      private ResourceReader reader;
51      private CompilationResult lastResult;
52      
53      public CompilingListener() {
54          this(new JavaCompilerFactory().createCompiler("eclipse"));
55      }
56  
57      public CompilingListener( final JavaCompiler pCompiler ) {
58          this(pCompiler, new TransactionalResourceStore(new MemoryResourceStore()));
59      }
60      
61      public CompilingListener( final JavaCompiler pCompiler, final TransactionalResourceStore pTransactionalStore ) {
62          super(pTransactionalStore);
63          compiler = pCompiler;
64          transactionalStore = pTransactionalStore;
65          lastResult = null;
66      }
67      
68      public JavaCompiler getCompiler() {
69          return compiler;
70      }
71      
72      public String getSourceFileExtension() {
73          return ".java";
74      }
75  
76      public ResourceReader getReader( final FilesystemAlterationObserver pObserver ) {
77          return new FileResourceReader(pObserver.getRootDirectory());
78      }
79  
80      public String getSourceNameFromFile( final FilesystemAlterationObserver pObserver, final File pFile ) {
81          return ConversionUtils.stripExtension(ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), pFile))) + getSourceFileExtension();
82      }
83      
84      @Override
85      public ResourceStore getStore() {
86          return transactionalStore;
87      }
88  
89      public synchronized CompilationResult getCompilationResult() {
90          return lastResult;
91      }
92      
93      @Override
94      public void onStart( final FilesystemAlterationObserver pObserver ) {
95          super.onStart(pObserver);
96  
97          reader = getReader(pObserver);
98  
99          transactionalStore.onStart();
100     }
101 
102     public String[] getResourcesToCompile( final FilesystemAlterationObserver pObserver ) {
103         final Collection<File> created = getCreatedFiles();
104         final Collection<File> changed = getChangedFiles();
105 
106         final Collection<String> resourceNames = new ArrayList<String>();
107         
108         for (File createdFile : created) {
109             if (createdFile.getName().endsWith(getSourceFileExtension())) {
110                 resourceNames.add(getSourceNameFromFile(pObserver, createdFile));
111             }
112         }
113         
114         for (File changedFile : changed) {
115             if (changedFile.getName().endsWith(getSourceFileExtension())) {
116                 resourceNames.add(getSourceNameFromFile(pObserver, changedFile));
117             }
118         }
119 
120         final String[] result = new String[resourceNames.size()];
121         resourceNames.toArray(result);
122         return result;
123     }
124     
125     @Override
126     public boolean isReloadRequired( final FilesystemAlterationObserver pObserver ) {
127         boolean reload = false;
128 
129         final Collection<File> created = getCreatedFiles();
130         final Collection<File> changed = getChangedFiles();
131         final Collection<File> deleted = getDeletedFiles();
132         
133         log.debug("created:" + created.size() + " changed:" + changed.size() + " deleted:" + deleted.size() + " resources");
134 
135         if (deleted.size() > 0) {
136             for (File deletedFile : deleted) {
137                 final String resourceName = ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), deletedFile));
138                 
139                 if (resourceName.endsWith(getSourceFileExtension())) {
140                     // if source resource got removed delete the corresponding class 
141                     transactionalStore.remove(ConversionUtils.stripExtension(resourceName) + ".class");
142                 } else {
143                     // ordinary resource to be removed
144                     transactionalStore.remove(resourceName);
145                 }
146                 
147                 // FIXME: does not remove nested classes
148                 
149             }
150             reload = true;
151         }
152                                 
153         final String[] resourcesToCompile = getResourcesToCompile(pObserver);
154 
155         if (resourcesToCompile.length > 0) {
156 
157             log.debug(resourcesToCompile.length + " classes to compile");
158             
159             final CompilationResult result = compiler.compile(resourcesToCompile, reader, transactionalStore);
160             
161             synchronized(this) {
162                 lastResult = result;
163             }
164             
165             final CompilationProblem[] errors = result.getErrors();
166             final CompilationProblem[] warnings = result.getWarnings();
167             
168             log.debug(errors.length + " errors, " + warnings.length + " warnings");
169         
170             if (errors.length > 0) {
171                 // FIXME: they need to be marked for re-compilation
172                 // and then added as compileables again
173                 for (int j = 0; j < resourcesToCompile.length; j++) {
174                     transactionalStore.remove(resourcesToCompile[j]);
175                 }
176             }
177             
178             reload = true;
179         }
180         
181         return reload;
182     }    
183 }