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 }