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  import java.util.Iterator;
24  
25  import org.apache.commons.jci.compilers.CompilationResult;
26  import org.apache.commons.jci.compilers.JavaCompiler;
27  import org.apache.commons.jci.compilers.JavaCompilerFactory;
28  import org.apache.commons.jci.monitor.FilesystemAlterationObserver;
29  import org.apache.commons.jci.problems.CompilationProblem;
30  import org.apache.commons.jci.readers.FileResourceReader;
31  import org.apache.commons.jci.readers.ResourceReader;
32  import org.apache.commons.jci.stores.MemoryResourceStore;
33  import org.apache.commons.jci.stores.ResourceStore;
34  import org.apache.commons.jci.stores.TransactionalResourceStore;
35  import org.apache.commons.jci.utils.ConversionUtils;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  /**
40   * A CompilingListener is an improved version of the ReloadingListener.
41   * It even compiles the classes from source before doing the reloading.
42   * 
43   * @author tcurdt
44   */
45  public class CompilingListener extends ReloadingListener {
46  
47      private final Log log = LogFactory.getLog(CompilingListener.class);
48      
49      private final JavaCompiler compiler;
50      private final TransactionalResourceStore transactionalStore;
51      private ResourceReader reader;
52      private CompilationResult lastResult;
53      
54      public CompilingListener() {
55          this(new JavaCompilerFactory().createCompiler("eclipse"));
56      }
57  
58      public CompilingListener( final JavaCompiler pCompiler ) {
59          this(pCompiler, new TransactionalResourceStore(new MemoryResourceStore()));
60      }
61      
62      public CompilingListener( final JavaCompiler pCompiler, final TransactionalResourceStore pTransactionalStore ) {
63          super(pTransactionalStore);
64          compiler = pCompiler;
65          transactionalStore = pTransactionalStore;
66          lastResult = null;
67      }
68      
69      public JavaCompiler getCompiler() {
70          return compiler;
71      }
72      
73      public String getSourceFileExtension() {
74          return ".java";
75      }
76  
77      public ResourceReader getReader( final FilesystemAlterationObserver pObserver ) {
78          return new FileResourceReader(pObserver.getRootDirectory());
79      }
80  
81      public String getSourceNameFromFile( final FilesystemAlterationObserver pObserver, final File pFile ) {
82          return ConversionUtils.stripExtension(ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), pFile))) + getSourceFileExtension();
83      }
84      
85      public ResourceStore getStore() {
86          return transactionalStore;
87      }
88  
89      public synchronized CompilationResult getCompilationResult() {
90          return lastResult;
91      }
92      
93      public void onStart( final FilesystemAlterationObserver pObserver ) {
94          super.onStart(pObserver);
95  
96          reader = getReader(pObserver);
97  
98          transactionalStore.onStart();
99      }
100 
101     public String[] getResourcesToCompile( final FilesystemAlterationObserver pObserver ) {
102         final Collection created = getCreatedFiles();
103         final Collection changed = getChangedFiles();
104 
105         final Collection resourceNames = new ArrayList();
106         
107         for (final Iterator it = created.iterator(); it.hasNext();) {
108             final File createdFile = (File) it.next();
109             if (createdFile.getName().endsWith(getSourceFileExtension())) {
110                 resourceNames.add(getSourceNameFromFile(pObserver, createdFile));
111             }
112         }
113         
114         for (final Iterator it = changed.iterator(); it.hasNext();) {
115             final File changedFile = (File) it.next();
116             if (changedFile.getName().endsWith(getSourceFileExtension())) {
117                 resourceNames.add(getSourceNameFromFile(pObserver, changedFile));
118             }
119         }
120 
121         final String[] result = new String[resourceNames.size()];
122         resourceNames.toArray(result);
123         return result;
124     }
125     
126     public boolean isReloadRequired( final FilesystemAlterationObserver pObserver ) {
127         boolean reload = false;
128 
129         final Collection created = getCreatedFiles();
130         final Collection changed = getChangedFiles();
131         final Collection deleted = getDeletedFiles();
132         
133         log.debug("created:" + created.size() + " changed:" + changed.size() + " deleted:" + deleted.size() + " resources");
134 
135         if (deleted.size() > 0) {
136             for (Iterator it = deleted.iterator(); it.hasNext();) {
137                 final File deletedFile = (File) it.next();
138 
139                 final String resourceName = ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), deletedFile));
140                 
141                 if (resourceName.endsWith(getSourceFileExtension())) {
142                     // if source resource got removed delete the corresponding class 
143                     transactionalStore.remove(ConversionUtils.stripExtension(resourceName) + ".class");
144                 } else {
145                     // ordinary resource to be removed
146                     transactionalStore.remove(resourceName);
147                 }
148                 
149                 // FIXME: does not remove nested classes
150                 
151             }
152             reload = true;
153         }
154                                 
155         final String[] resourcesToCompile = getResourcesToCompile(pObserver);
156 
157         if (resourcesToCompile.length > 0) {
158 
159             log.debug(resourcesToCompile.length + " classes to compile");
160             
161             final CompilationResult result = compiler.compile(resourcesToCompile, reader, transactionalStore);
162             
163             synchronized(this) {
164                 lastResult = result;
165             }
166             
167             final CompilationProblem[] errors = result.getErrors();
168             final CompilationProblem[] warnings = result.getWarnings();
169             
170             log.debug(errors.length + " errors, " + warnings.length + " warnings");
171         
172             if (errors.length > 0) {
173                 // FIXME: they need to be marked for re-compilation
174                 // and then added as compileables again
175                 for (int j = 0; j < resourcesToCompile.length; j++) {
176                     transactionalStore.remove(resourcesToCompile[j]);
177                 }
178             }
179             
180             reload = true;
181         }
182         
183         return reload;
184     }    
185 }