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;
19  
20  import java.io.File;
21  
22  import org.apache.commons.io.FileUtils;
23  import org.apache.commons.jci.classes.ExtendedDump;
24  import org.apache.commons.jci.classes.SimpleDump;
25  import org.apache.commons.jci.compilers.CompilationResult;
26  import org.apache.commons.jci.compilers.JavaCompiler;
27  import org.apache.commons.jci.compilers.JavaCompilerSettings;
28  import org.apache.commons.jci.listeners.CompilingListener;
29  import org.apache.commons.jci.monitor.FilesystemAlterationMonitor;
30  import org.apache.commons.jci.problems.CompilationProblem;
31  import org.apache.commons.jci.problems.CompilationProblemHandler;
32  import org.apache.commons.jci.readers.ResourceReader;
33  import org.apache.commons.jci.stores.ResourceStore;
34  import org.apache.commons.jci.utils.ConversionUtils;
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  
38  /**
39   * 
40   * @author tcurdt
41   */
42  public final class CompilingClassLoaderTestCase extends AbstractTestCase {
43  
44      private final Log log = LogFactory.getLog(CompilingClassLoaderTestCase.class);
45  
46      private ReloadingClassLoader classloader;
47      private CompilingListener listener;
48      private FilesystemAlterationMonitor fam;
49          
50      private final static class MockJavaCompiler implements JavaCompiler {
51  
52          private final Log log = LogFactory.getLog(MockJavaCompiler.class);
53  
54          public CompilationResult compile(String[] pResourcePaths, ResourceReader pReader, ResourceStore pStore, ClassLoader pClassLoader, JavaCompilerSettings pSettings ) {
55  
56              for (int i = 0; i < pResourcePaths.length; i++) {
57                  final String resourcePath = pResourcePaths[i];
58                  final byte[] resourceContent = pReader.getBytes(resourcePath);
59  
60                  log.debug("resource " + resourcePath + " = " + ((resourceContent!=null)?new String(resourceContent):null) );
61  
62                  final byte[] data;
63  
64                  if ("jci/Simple.java".equals(resourcePath)) {
65  
66                      try {
67                          data = SimpleDump.dump(new String(resourceContent));
68                      } catch (Exception e) {
69                          throw new RuntimeException("cannot handle resource " + resourcePath, e);
70                      }
71  
72                  } else if ("jci/Extended.java".equals(resourcePath)) {
73  
74                      try {
75                          data = ExtendedDump.dump();
76                      } catch (Exception e) {
77                          throw new RuntimeException("cannot handle resource " + resourcePath, e);
78                      }
79  
80                  } else {
81                      throw new RuntimeException("cannot handle resource " + resourcePath);
82                  }
83  
84                  log.debug("compiling " + resourcePath + " (" + data.length + ")");
85  
86                  pStore.write(ConversionUtils.stripExtension(resourcePath) + ".class", data);
87  
88              }
89  
90              return new CompilationResult(new CompilationProblem[0]);
91          }
92  
93          public CompilationResult compile(String[] pResourcePaths, ResourceReader pReader, ResourceStore pStore, ClassLoader pClassLoader) {
94              return compile(pResourcePaths, pReader, pStore, pClassLoader, null);
95          }
96  
97          public CompilationResult compile(String[] pResourcePaths, ResourceReader pReader, ResourceStore pStore) {
98              return compile(pResourcePaths, pReader, pStore, null);
99          }
100 
101         public void setCompilationProblemHandler(CompilationProblemHandler pHandler) {
102         }
103 
104         public JavaCompilerSettings createDefaultSettings() {
105             return null;
106         }
107 
108     }
109     
110     protected void setUp() throws Exception {
111         super.setUp();
112         
113         classloader = new ReloadingClassLoader(this.getClass().getClassLoader());
114         listener = new CompilingListener(new MockJavaCompiler());   
115 
116         listener.addReloadNotificationListener(classloader);
117         
118         fam = new FilesystemAlterationMonitor();
119         fam.addListener(directory, listener);
120         fam.start();
121     }
122 
123     private void initialCompile() throws Exception {
124         log.debug("initial compile");        
125 
126         listener.waitForFirstCheck();
127                 
128         writeFile("jci/Simple.java", "Simple1");        
129         writeFile("jci/Extended.java", "Extended");        
130         
131         log.debug("waiting for compile changes to get applied");        
132         listener.waitForCheck();
133         
134         log.debug("*** ready to test");        
135     }
136     
137     public void testCreate() throws Exception {
138         initialCompile();
139         
140         log.debug("loading Simple");        
141         final Object simple = classloader.loadClass("jci.Simple").newInstance();        
142         assertEquals("Simple1", simple.toString());
143         
144         log.debug("loading Extended");        
145         final Object extended = classloader.loadClass("jci.Extended").newInstance();        
146         assertEquals("Extended:Simple1", extended.toString());
147     }
148 
149     public void testChange() throws Exception {        
150         initialCompile();
151 
152         final Object simple = classloader.loadClass("jci.Simple").newInstance();        
153         assertEquals("Simple1", simple.toString());
154         
155         final Object extended = classloader.loadClass("jci.Extended").newInstance();        
156         assertEquals("Extended:Simple1", extended.toString());
157 
158         delay();
159         writeFile("jci/Simple.java", "Simple2");
160         listener.waitForCheck();
161     
162         final Object simple2 = classloader.loadClass("jci.Simple").newInstance();        
163         assertEquals("Simple2", simple2.toString());
164         
165         final Object newExtended = classloader.loadClass("jci.Extended").newInstance();        
166         assertEquals("Extended:Simple2", newExtended.toString());
167     }
168 
169     public void testDelete() throws Exception {
170         initialCompile();
171 
172         final Object simple = classloader.loadClass("jci.Simple").newInstance();        
173         assertEquals("Simple1", simple.toString());
174         
175         final Object extended = classloader.loadClass("jci.Extended").newInstance();        
176         assertEquals("Extended:Simple1", extended.toString());
177                 
178         listener.waitForCheck();
179         
180         log.debug("deleting source file");
181         assertTrue(new File(directory, "jci/Extended.java").delete());
182         
183         listener.waitForCheck();
184        
185         log.debug("loading Simple");
186         final Object oldSimple = classloader.loadClass("jci.Simple").newInstance();        
187         assertEquals("Simple1", oldSimple.toString());
188 
189         log.debug("trying to loading Extended");
190         try {
191             classloader.loadClass("jci.Extended").newInstance();
192             fail();
193         } catch(final ClassNotFoundException e) {
194             assertEquals("jci.Extended", e.getMessage());
195         }        
196         
197         log.debug("deleting whole directory");
198         FileUtils.deleteDirectory(new File(directory, "jci"));
199 
200         listener.waitForCheck();
201 
202         log.debug("trying to loading Simple");
203         try {
204             classloader.loadClass("jci.Simple").newInstance();
205             fail();
206         } catch(final ClassNotFoundException e) {
207             assertEquals("jci.Simple", e.getMessage());
208         }
209 
210     }
211 
212     public void testDeleteDependency() throws Exception {        
213         initialCompile();
214 
215         final Object simple = classloader.loadClass("jci.Simple").newInstance();        
216         assertEquals("Simple1", simple.toString());
217         
218         final Object extended = classloader.loadClass("jci.Extended").newInstance();        
219         assertEquals("Extended:Simple1", extended.toString());
220         
221         log.debug("deleting source file");
222         assertTrue(new File(directory, "jci/Simple.java").delete());
223         listener.waitForCheck();
224 
225         log.debug("trying to load dependend class");
226         try {
227             classloader.loadClass("jci.Extended").newInstance();
228             fail();
229         } catch(final NoClassDefFoundError e) {
230             assertEquals("jci/Simple", e.getMessage());
231         }
232         
233     }
234 
235     protected void tearDown() throws Exception {
236         fam.removeListener(listener);
237         fam.stop();
238         super.tearDown();
239     }    
240 }