001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.jci.listeners;
019
020 import java.io.File;
021 import java.util.ArrayList;
022 import java.util.Collection;
023
024 import org.apache.commons.jci.compilers.CompilationResult;
025 import org.apache.commons.jci.compilers.JavaCompiler;
026 import org.apache.commons.jci.compilers.JavaCompilerFactory;
027 import org.apache.commons.jci.monitor.FilesystemAlterationObserver;
028 import org.apache.commons.jci.problems.CompilationProblem;
029 import org.apache.commons.jci.readers.FileResourceReader;
030 import org.apache.commons.jci.readers.ResourceReader;
031 import org.apache.commons.jci.stores.MemoryResourceStore;
032 import org.apache.commons.jci.stores.ResourceStore;
033 import org.apache.commons.jci.stores.TransactionalResourceStore;
034 import org.apache.commons.jci.utils.ConversionUtils;
035 import org.apache.commons.logging.Log;
036 import org.apache.commons.logging.LogFactory;
037
038 /**
039 * A CompilingListener is an improved version of the ReloadingListener.
040 * It even compiles the classes from source before doing the reloading.
041 *
042 * @author tcurdt
043 */
044 public class CompilingListener extends ReloadingListener {
045
046 private final Log log = LogFactory.getLog(CompilingListener.class);
047
048 private final JavaCompiler compiler;
049 private final TransactionalResourceStore transactionalStore;
050 private ResourceReader reader;
051 private CompilationResult lastResult;
052
053 public CompilingListener() {
054 this(new JavaCompilerFactory().createCompiler("eclipse"));
055 }
056
057 public CompilingListener( final JavaCompiler pCompiler ) {
058 this(pCompiler, new TransactionalResourceStore(new MemoryResourceStore()));
059 }
060
061 public CompilingListener( final JavaCompiler pCompiler, final TransactionalResourceStore pTransactionalStore ) {
062 super(pTransactionalStore);
063 compiler = pCompiler;
064 transactionalStore = pTransactionalStore;
065 lastResult = null;
066 }
067
068 public JavaCompiler getCompiler() {
069 return compiler;
070 }
071
072 public String getSourceFileExtension() {
073 return ".java";
074 }
075
076 public ResourceReader getReader( final FilesystemAlterationObserver pObserver ) {
077 return new FileResourceReader(pObserver.getRootDirectory());
078 }
079
080 public String getSourceNameFromFile( final FilesystemAlterationObserver pObserver, final File pFile ) {
081 return ConversionUtils.stripExtension(ConversionUtils.getResourceNameFromFileName(ConversionUtils.relative(pObserver.getRootDirectory(), pFile))) + getSourceFileExtension();
082 }
083
084 @Override
085 public ResourceStore getStore() {
086 return transactionalStore;
087 }
088
089 public synchronized CompilationResult getCompilationResult() {
090 return lastResult;
091 }
092
093 @Override
094 public void onStart( final FilesystemAlterationObserver pObserver ) {
095 super.onStart(pObserver);
096
097 reader = getReader(pObserver);
098
099 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 }