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    }