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;
019
020 import java.io.File;
021
022 import org.apache.commons.io.FileUtils;
023 import org.apache.commons.jci.classes.ExtendedDump;
024 import org.apache.commons.jci.classes.SimpleDump;
025 import org.apache.commons.jci.compilers.CompilationResult;
026 import org.apache.commons.jci.compilers.JavaCompiler;
027 import org.apache.commons.jci.compilers.JavaCompilerSettings;
028 import org.apache.commons.jci.listeners.CompilingListener;
029 import org.apache.commons.jci.monitor.FilesystemAlterationMonitor;
030 import org.apache.commons.jci.problems.CompilationProblem;
031 import org.apache.commons.jci.problems.CompilationProblemHandler;
032 import org.apache.commons.jci.readers.ResourceReader;
033 import org.apache.commons.jci.stores.ResourceStore;
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 *
040 * @author tcurdt
041 */
042 public final class CompilingClassLoaderTestCase extends AbstractTestCase {
043
044 private final Log log = LogFactory.getLog(CompilingClassLoaderTestCase.class);
045
046 private ReloadingClassLoader classloader;
047 private CompilingListener listener;
048 private FilesystemAlterationMonitor fam;
049
050 private final static class MockJavaCompiler implements JavaCompiler {
051
052 private final Log log = LogFactory.getLog(MockJavaCompiler.class);
053
054 public CompilationResult compile(String[] pResourcePaths, ResourceReader pReader, ResourceStore pStore, ClassLoader pClassLoader, JavaCompilerSettings pSettings ) {
055
056 for (int i = 0; i < pResourcePaths.length; i++) {
057 final String resourcePath = pResourcePaths[i];
058 final byte[] resourceContent = pReader.getBytes(resourcePath);
059
060 log.debug("resource " + resourcePath + " = " + ((resourceContent!=null)?new String(resourceContent):null) );
061
062 final byte[] data;
063
064 if ("jci/Simple.java".equals(resourcePath)) {
065
066 try {
067 data = SimpleDump.dump(new String(resourceContent));
068 } catch (Exception e) {
069 throw new RuntimeException("cannot handle resource " + resourcePath, e);
070 }
071
072 } else if ("jci/Extended.java".equals(resourcePath)) {
073
074 try {
075 data = ExtendedDump.dump();
076 } catch (Exception e) {
077 throw new RuntimeException("cannot handle resource " + resourcePath, e);
078 }
079
080 } else {
081 throw new RuntimeException("cannot handle resource " + resourcePath);
082 }
083
084 log.debug("compiling " + resourcePath + " (" + data.length + ")");
085
086 pStore.write(ConversionUtils.stripExtension(resourcePath) + ".class", data);
087
088 }
089
090 return new CompilationResult(new CompilationProblem[0]);
091 }
092
093 public CompilationResult compile(String[] pResourcePaths, ResourceReader pReader, ResourceStore pStore, ClassLoader pClassLoader) {
094 return compile(pResourcePaths, pReader, pStore, pClassLoader, null);
095 }
096
097 public CompilationResult compile(String[] pResourcePaths, ResourceReader pReader, ResourceStore pStore) {
098 return compile(pResourcePaths, pReader, pStore, null);
099 }
100
101 public void setCompilationProblemHandler(CompilationProblemHandler pHandler) {
102 }
103
104 public JavaCompilerSettings createDefaultSettings() {
105 return null;
106 }
107
108 }
109
110 @Override
111 protected void setUp() throws Exception {
112 super.setUp();
113
114 classloader = new ReloadingClassLoader(this.getClass().getClassLoader());
115 listener = new CompilingListener(new MockJavaCompiler());
116
117 listener.addReloadNotificationListener(classloader);
118
119 fam = new FilesystemAlterationMonitor();
120 fam.addListener(directory, listener);
121 fam.start();
122 }
123
124 private void initialCompile() throws Exception {
125 log.debug("initial compile");
126
127 listener.waitForFirstCheck();
128
129 writeFile("jci/Simple.java", "Simple1");
130 writeFile("jci/Extended.java", "Extended");
131
132 log.debug("waiting for compile changes to get applied");
133 listener.waitForCheck();
134
135 log.debug("*** ready to test");
136 }
137
138 public void testCreate() throws Exception {
139 initialCompile();
140
141 log.debug("loading Simple");
142 final Object simple = classloader.loadClass("jci.Simple").newInstance();
143 assertEquals("Simple1", simple.toString());
144
145 log.debug("loading Extended");
146 final Object extended = classloader.loadClass("jci.Extended").newInstance();
147 assertEquals("Extended:Simple1", extended.toString());
148 }
149
150 public void testChange() throws Exception {
151 initialCompile();
152
153 final Object simple = classloader.loadClass("jci.Simple").newInstance();
154 assertEquals("Simple1", simple.toString());
155
156 final Object extended = classloader.loadClass("jci.Extended").newInstance();
157 assertEquals("Extended:Simple1", extended.toString());
158
159 delay();
160 writeFile("jci/Simple.java", "Simple2");
161 listener.waitForCheck();
162
163 final Object simple2 = classloader.loadClass("jci.Simple").newInstance();
164 assertEquals("Simple2", simple2.toString());
165
166 final Object newExtended = classloader.loadClass("jci.Extended").newInstance();
167 assertEquals("Extended:Simple2", newExtended.toString());
168 }
169
170 public void testDelete() throws Exception {
171 initialCompile();
172
173 final Object simple = classloader.loadClass("jci.Simple").newInstance();
174 assertEquals("Simple1", simple.toString());
175
176 final Object extended = classloader.loadClass("jci.Extended").newInstance();
177 assertEquals("Extended:Simple1", extended.toString());
178
179 listener.waitForCheck();
180
181 log.debug("deleting source file");
182 assertTrue(new File(directory, "jci/Extended.java").delete());
183
184 listener.waitForCheck();
185
186 log.debug("loading Simple");
187 final Object oldSimple = classloader.loadClass("jci.Simple").newInstance();
188 assertEquals("Simple1", oldSimple.toString());
189
190 log.debug("trying to loading Extended");
191 try {
192 classloader.loadClass("jci.Extended").newInstance();
193 fail();
194 } catch(final ClassNotFoundException e) {
195 assertEquals("jci.Extended", e.getMessage());
196 }
197
198 log.debug("deleting whole directory");
199 FileUtils.deleteDirectory(new File(directory, "jci"));
200
201 listener.waitForCheck();
202
203 log.debug("trying to loading Simple");
204 try {
205 classloader.loadClass("jci.Simple").newInstance();
206 fail();
207 } catch(final ClassNotFoundException e) {
208 assertEquals("jci.Simple", e.getMessage());
209 }
210
211 }
212
213 public void testDeleteDependency() throws Exception {
214 initialCompile();
215
216 final Object simple = classloader.loadClass("jci.Simple").newInstance();
217 assertEquals("Simple1", simple.toString());
218
219 final Object extended = classloader.loadClass("jci.Extended").newInstance();
220 assertEquals("Extended:Simple1", extended.toString());
221
222 log.debug("deleting source file");
223 assertTrue(new File(directory, "jci/Simple.java").delete());
224 listener.waitForCheck();
225
226 log.debug("trying to load dependend class");
227 try {
228 classloader.loadClass("jci.Extended").newInstance();
229 fail();
230 } catch(final NoClassDefFoundError e) {
231 assertEquals("jci/Simple", e.getMessage());
232 }
233
234 }
235
236 @Override
237 protected void tearDown() throws Exception {
238 fam.removeListener(listener);
239 fam.stop();
240 super.tearDown();
241 }
242 }