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 }