Source Code Cross Referenced for GroovyClassLoader.java in  » Scripting » groovy-1.0 » groovy » lang » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Scripting » groovy 1.0 » groovy.lang 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * $Id: GroovyClassLoader.java 4445 2006-12-17 22:35:15Z blackdrag $
003:         *
004:         * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005:         *
006:         * Redistribution and use of this software and associated documentation
007:         * ("Software"), with or without modification, are permitted provided that the
008:         * following conditions are met:
009:         *  1. Redistributions of source code must retain copyright statements and
010:         * notices. Redistributions must also contain a copy of this document.
011:         *  2. Redistributions in binary form must reproduce the above copyright
012:         * notice, this list of conditions and the following disclaimer in the
013:         * documentation and/or other materials provided with the distribution.
014:         *  3. The name "groovy" must not be used to endorse or promote products
015:         * derived from this Software without prior written permission of The Codehaus.
016:         * For written permission, please contact info@codehaus.org.
017:         *  4. Products derived from this Software may not be called "groovy" nor may
018:         * "groovy" appear in their names without prior written permission of The
019:         * Codehaus. "groovy" is a registered trademark of The Codehaus.
020:         *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
021:         *
022:         * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
023:         * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
024:         * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
025:         * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
026:         * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
027:         * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
028:         * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
029:         * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
030:         * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
031:         * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
032:         * DAMAGE.
033:         *
034:         */
035:
036:        /**
037:         * @TODO: multi threaded compiling of the same class but with different roots
038:         * for compilation... T1 compiles A, which uses B, T2 compiles B... mark A and B
039:         * as parsed and then synchronize compilation. Problems: How to synchronize? 
040:         * How to get error messages?   
041:         * 
042:         */package groovy.lang;
043:
044:        import java.io.ByteArrayInputStream;
045:        import java.io.File;
046:        import java.io.IOException;
047:        import java.io.InputStream;
048:        import java.lang.reflect.Field;
049:        import java.net.MalformedURLException;
050:        import java.net.URL;
051:        import java.net.URLClassLoader;
052:        import java.security.AccessController;
053:        import java.security.CodeSource;
054:        import java.security.PrivilegedAction;
055:        import java.security.ProtectionDomain;
056:        import java.util.ArrayList;
057:        import java.util.Collection;
058:        import java.util.Enumeration;
059:        import java.util.HashMap;
060:        import java.util.Iterator;
061:        import java.util.List;
062:        import java.util.Map;
063:
064:        import org.codehaus.groovy.ast.ClassNode;
065:        import org.codehaus.groovy.ast.ModuleNode;
066:        import org.codehaus.groovy.classgen.Verifier;
067:        import org.codehaus.groovy.control.CompilationFailedException;
068:        import org.codehaus.groovy.control.CompilationUnit;
069:        import org.codehaus.groovy.control.CompilerConfiguration;
070:        import org.codehaus.groovy.control.Phases;
071:        import org.codehaus.groovy.control.SourceUnit;
072:        import org.objectweb.asm.ClassVisitor;
073:        import org.objectweb.asm.ClassWriter;
074:
075:        /**
076:         * A ClassLoader which can load Groovy classes. The loaded classes are cached, 
077:         * classes from other classlaoders should not be cached. To be able to load a 
078:         * script that was asked for earlier but was created later it is essential not
079:         * to keep anything like a "class not found" information for that class name. 
080:         * This includes possible parent loaders. Classes that are not chached are always 
081:         * reloaded.
082:         *
083:         * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
084:         * @author Guillaume Laforge
085:         * @author Steve Goetze
086:         * @author Bing Ran
087:         * @author <a href="mailto:scottstirling@rcn.com">Scott Stirling</a>
088:         * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
089:         * @version $Revision: 4445 $
090:         */
091:        public class GroovyClassLoader extends URLClassLoader {
092:
093:            /**
094:             * this cache contains the loaded classes or PARSING, if the class is currently parsed 
095:             */
096:            protected Map classCache = new HashMap();
097:            protected Map sourceCache = new HashMap();
098:            private CompilerConfiguration config;
099:            private Boolean recompile = null;
100:            // use 1000000 as offset to avoid conflicts with names form the GroovyShell 
101:            private static int scriptNameCounter = 1000000;
102:
103:            private GroovyResourceLoader resourceLoader = new GroovyResourceLoader() {
104:                public URL loadGroovySource(final String filename)
105:                        throws MalformedURLException {
106:                    URL file = (URL) AccessController
107:                            .doPrivileged(new PrivilegedAction() {
108:                                public Object run() {
109:                                    return getSourceFile(filename);
110:                                }
111:                            });
112:                    return file;
113:                }
114:            };
115:
116:            /**
117:             * creates a GroovyClassLoader using the current Thread's context
118:             * Class loader as parent.
119:             */
120:            public GroovyClassLoader() {
121:                this (Thread.currentThread().getContextClassLoader());
122:            }
123:
124:            /**
125:             * creates a GroovyClassLoader using the given ClassLoader as parent
126:             */
127:            public GroovyClassLoader(ClassLoader loader) {
128:                this (loader, null);
129:            }
130:
131:            /**
132:             * creates a GroovyClassLoader using the given GroovyClassLoader as parent.
133:             * This loader will get the parent's CompilerConfiguration
134:             */
135:            public GroovyClassLoader(GroovyClassLoader parent) {
136:                this (parent, parent.config, false);
137:            }
138:
139:            /**
140:             * creates a GroovyClassLaoder.
141:             * @param parent the parten class loader
142:             * @param config the compiler configuration
143:             * @param useConfigurationClasspath determines if the configurations classpath should be added 
144:             */
145:            public GroovyClassLoader(ClassLoader parent,
146:                    CompilerConfiguration config,
147:                    boolean useConfigurationClasspath) {
148:                super (new URL[0], parent);
149:                if (config == null)
150:                    config = CompilerConfiguration.DEFAULT;
151:                this .config = config;
152:                if (useConfigurationClasspath) {
153:                    for (Iterator it = config.getClasspath().iterator(); it
154:                            .hasNext();) {
155:                        String path = (String) it.next();
156:                        this .addClasspath(path);
157:                    }
158:                }
159:            }
160:
161:            /**
162:             * creates a GroovyClassLoader using the given ClassLoader as parent.
163:             */
164:            public GroovyClassLoader(ClassLoader loader,
165:                    CompilerConfiguration config) {
166:                this (loader, config, true);
167:            }
168:
169:            public void setResourceLoader(GroovyResourceLoader resourceLoader) {
170:                if (resourceLoader == null) {
171:                    throw new IllegalArgumentException(
172:                            "Resource loader must not be null!");
173:                }
174:                this .resourceLoader = resourceLoader;
175:            }
176:
177:            public GroovyResourceLoader getResourceLoader() {
178:                return resourceLoader;
179:            }
180:
181:            /**
182:             * Loads the given class node returning the implementation Class
183:             *
184:             * @param classNode
185:             * @return a class
186:             */
187:            public Class defineClass(ClassNode classNode, String file) {
188:                //return defineClass(classNode, file, "/groovy/defineClass");
189:                throw new DeprecationException(
190:                        "the method GroovyClassLoader#defineClass(ClassNode, String) is no longer used and removed");
191:            }
192:
193:            /**
194:             * Loads the given class node returning the implementation Class. 
195:             * 
196:             * WARNING: this compilation is not synchronized
197:             *
198:             * @param classNode
199:             * @return a class
200:             */
201:            public Class defineClass(ClassNode classNode, String file,
202:                    String newCodeBase) {
203:                CodeSource codeSource = null;
204:                try {
205:                    codeSource = new CodeSource(
206:                            new URL("file", "", newCodeBase),
207:                            (java.security.cert.Certificate[]) null);
208:                } catch (MalformedURLException e) {
209:                    //swallow
210:                }
211:
212:                CompilationUnit unit = createCompilationUnit(config, codeSource);
213:                ClassCollector collector = createCollector(unit, classNode
214:                        .getModule().getContext());
215:                try {
216:                    unit.addClassNode(classNode);
217:                    unit.setClassgenCallback(collector);
218:                    unit.compile(Phases.CLASS_GENERATION);
219:
220:                    return collector.generatedClass;
221:                } catch (CompilationFailedException e) {
222:                    throw new RuntimeException(e);
223:                }
224:            }
225:
226:            /**
227:             * Parses the given file into a Java class capable of being run
228:             *
229:             * @param file the file name to parse
230:             * @return the main class defined in the given script
231:             */
232:            public Class parseClass(File file)
233:                    throws CompilationFailedException, IOException {
234:                return parseClass(new GroovyCodeSource(file));
235:            }
236:
237:            /**
238:             * Parses the given text into a Java class capable of being run
239:             *
240:             * @param text     the text of the script/class to parse
241:             * @param fileName the file name to use as the name of the class
242:             * @return the main class defined in the given script
243:             */
244:            public Class parseClass(String text, String fileName)
245:                    throws CompilationFailedException {
246:                return parseClass(new ByteArrayInputStream(text.getBytes()),
247:                        fileName);
248:            }
249:
250:            /**
251:             * Parses the given text into a Java class capable of being run
252:             *
253:             * @param text the text of the script/class to parse
254:             * @return the main class defined in the given script
255:             */
256:            public Class parseClass(String text)
257:                    throws CompilationFailedException {
258:                return parseClass(new ByteArrayInputStream(text.getBytes()),
259:                        "script" + System.currentTimeMillis() + ".groovy");
260:            }
261:
262:            /**
263:             * Parses the given character stream into a Java class capable of being run
264:             *
265:             * @param in an InputStream
266:             * @return the main class defined in the given script
267:             */
268:            public Class parseClass(InputStream in)
269:                    throws CompilationFailedException {
270:                return parseClass(in, generateScriptName());
271:            }
272:
273:            public synchronized String generateScriptName() {
274:                scriptNameCounter++;
275:                return "script" + scriptNameCounter + ".groovy";
276:            }
277:
278:            public Class parseClass(final InputStream in, final String fileName)
279:                    throws CompilationFailedException {
280:                // For generic input streams, provide a catch-all codebase of
281:                // GroovyScript
282:                // Security for these classes can be administered via policy grants with
283:                // a codebase of file:groovy.script
284:                GroovyCodeSource gcs = (GroovyCodeSource) AccessController
285:                        .doPrivileged(new PrivilegedAction() {
286:                            public Object run() {
287:                                return new GroovyCodeSource(in, fileName,
288:                                        "/groovy/script");
289:                            }
290:                        });
291:                return parseClass(gcs);
292:            }
293:
294:            public Class parseClass(GroovyCodeSource codeSource)
295:                    throws CompilationFailedException {
296:                return parseClass(codeSource, codeSource.isCachable());
297:            }
298:
299:            /**
300:             * Parses the given code source into a Java class. If there is a class file
301:             * for the given code source, then no parsing is done, instead the cached class is returned.
302:             * 
303:             * @param shouldCacheSource if true then the generated class will be stored in the source cache 
304:             *
305:             * @return the main class defined in the given script
306:             */
307:            public Class parseClass(GroovyCodeSource codeSource,
308:                    boolean shouldCacheSource)
309:                    throws CompilationFailedException {
310:                synchronized (classCache) {
311:                    Class answer = (Class) sourceCache
312:                            .get(codeSource.getName());
313:                    if (answer != null)
314:                        return answer;
315:
316:                    // Was neither already loaded nor compiling, so compile and add to
317:                    // cache.
318:                    try {
319:                        CompilationUnit unit = createCompilationUnit(config,
320:                                codeSource.getCodeSource());
321:                        SourceUnit su = null;
322:                        if (codeSource.getFile() == null) {
323:                            su = unit.addSource(codeSource.getName(),
324:                                    codeSource.getInputStream());
325:                        } else {
326:                            su = unit.addSource(codeSource.getFile());
327:                        }
328:
329:                        ClassCollector collector = createCollector(unit, su);
330:                        unit.setClassgenCallback(collector);
331:                        int goalPhase = Phases.CLASS_GENERATION;
332:                        if (config != null
333:                                && config.getTargetDirectory() != null)
334:                            goalPhase = Phases.OUTPUT;
335:                        unit.compile(goalPhase);
336:
337:                        answer = collector.generatedClass;
338:                        for (Iterator iter = collector.getLoadedClasses()
339:                                .iterator(); iter.hasNext();) {
340:                            Class clazz = (Class) iter.next();
341:                            setClassCacheEntry(clazz);
342:                        }
343:                        if (shouldCacheSource)
344:                            sourceCache.put(codeSource.getName(), answer);
345:                    } finally {
346:                        try {
347:                            InputStream is = codeSource.getInputStream();
348:                            if (is != null)
349:                                is.close();
350:                        } catch (IOException e) {
351:                            throw new GroovyRuntimeException(
352:                                    "unable to close stream", e);
353:                        }
354:                    }
355:                    return answer;
356:                }
357:            }
358:
359:            /**
360:             * gets the currently used classpath. 
361:             * @return a String[] containing the file information of the urls 
362:             * @see #getURLs()
363:             */
364:            protected String[] getClassPath() {
365:                //workaround for Groovy-835
366:                URL[] urls = getURLs();
367:                String[] ret = new String[urls.length];
368:                for (int i = 0; i < ret.length; i++) {
369:                    ret[i] = urls[i].getFile();
370:                }
371:                return ret;
372:            }
373:
374:            /**
375:             * expands the classpath
376:             * @param pathList an empty list that will contain the elements of the classpath
377:             * @param classpath the classpath specified as a single string
378:             * @deprecated
379:             */
380:            protected void expandClassPath(List pathList, String base,
381:                    String classpath, boolean isManifestClasspath) {
382:                throw new DeprecationException(
383:                        "the method groovy.lang.GroovyClassLoader#expandClassPath(List,String,String,boolean) is no longer used internally and removed");
384:            }
385:
386:            /**
387:             * A helper method to allow bytecode to be loaded. spg changed name to
388:             * defineClass to make it more consistent with other ClassLoader methods
389:             * @deprecated
390:             */
391:            protected Class defineClass(String name, byte[] bytecode,
392:                    ProtectionDomain domain) {
393:                throw new DeprecationException(
394:                        "the method groovy.lang.GroovyClassLoader#defineClass(String,byte[],ProtectionDomain) is no longer used internally and removed");
395:            }
396:
397:            public static class InnerLoader extends GroovyClassLoader {
398:                private GroovyClassLoader delegate;
399:
400:                public InnerLoader(GroovyClassLoader delegate) {
401:                    super (delegate);
402:                    this .delegate = delegate;
403:                }
404:
405:                public void addClasspath(String path) {
406:                    delegate.addClasspath(path);
407:                }
408:
409:                public void clearCache() {
410:                    delegate.clearCache();
411:                }
412:
413:                public URL findResource(String name) {
414:                    return delegate.findResource(name);
415:                }
416:
417:                public Enumeration findResources(String name)
418:                        throws IOException {
419:                    return delegate.findResources(name);
420:                }
421:
422:                public Class[] getLoadedClasses() {
423:                    return delegate.getLoadedClasses();
424:                }
425:
426:                public URL getResource(String name) {
427:                    return delegate.getResource(name);
428:                }
429:
430:                public InputStream getResourceAsStream(String name) {
431:                    return delegate.getResourceAsStream(name);
432:                }
433:
434:                public GroovyResourceLoader getResourceLoader() {
435:                    return delegate.getResourceLoader();
436:                }
437:
438:                public URL[] getURLs() {
439:                    return delegate.getURLs();
440:                }
441:
442:                public Class loadClass(String name, boolean lookupScriptFiles,
443:                        boolean preferClassOverScript, boolean resolve)
444:                        throws ClassNotFoundException,
445:                        CompilationFailedException {
446:                    Class c = findLoadedClass(name);
447:                    if (c != null)
448:                        return c;
449:                    return delegate.loadClass(name, lookupScriptFiles,
450:                            preferClassOverScript, resolve);
451:                }
452:
453:                public Class parseClass(GroovyCodeSource codeSource,
454:                        boolean shouldCache) throws CompilationFailedException {
455:                    return delegate.parseClass(codeSource, shouldCache);
456:                }
457:
458:                public void setResourceLoader(
459:                        GroovyResourceLoader resourceLoader) {
460:                    delegate.setResourceLoader(resourceLoader);
461:                }
462:
463:                public void addURL(URL url) {
464:                    delegate.addURL(url);
465:                }
466:            }
467:
468:            /**
469:             * creates a new CompilationUnit. If you want to add additional
470:             * phase operations to the CompilationUnit (for example to inject
471:             * additional methods, variables, fields), then you should overwrite
472:             * this method.
473:             * 
474:             * @param config the compiler configuration, usually the same as for this class loader
475:             * @param source the source containing the initial file to compile, more files may follow during compilation
476:             * 
477:             * @return the CompilationUnit
478:             */
479:            protected CompilationUnit createCompilationUnit(
480:                    CompilerConfiguration config, CodeSource source) {
481:                return new CompilationUnit(config, source, this );
482:            }
483:
484:            /**
485:             * creates a ClassCollector for a new compilation.
486:             * @param unit the compilationUnit
487:             * @param su  the SoruceUnit
488:             * @return the ClassCollector
489:             */
490:            protected ClassCollector createCollector(CompilationUnit unit,
491:                    SourceUnit su) {
492:                InnerLoader loader = (InnerLoader) AccessController
493:                        .doPrivileged(new PrivilegedAction() {
494:                            public Object run() {
495:                                return new InnerLoader(GroovyClassLoader.this );
496:                            }
497:                        });
498:                return new ClassCollector(loader, unit, su);
499:            }
500:
501:            public static class ClassCollector extends
502:                    CompilationUnit.ClassgenCallback {
503:                private Class generatedClass;
504:                private GroovyClassLoader cl;
505:                private SourceUnit su;
506:                private CompilationUnit unit;
507:                private Collection loadedClasses = null;
508:
509:                protected ClassCollector(InnerLoader cl, CompilationUnit unit,
510:                        SourceUnit su) {
511:                    this .cl = cl;
512:                    this .unit = unit;
513:                    this .loadedClasses = new ArrayList();
514:                    this .su = su;
515:                }
516:
517:                protected GroovyClassLoader getDefiningClassLoader() {
518:                    return cl;
519:                }
520:
521:                protected Class createClass(byte[] code, ClassNode classNode) {
522:                    GroovyClassLoader cl = getDefiningClassLoader();
523:                    Class theClass = cl.defineClass(classNode.getName(), code,
524:                            0, code.length, unit.getAST().getCodeSource());
525:                    cl.resolveClass(theClass);
526:                    this .loadedClasses.add(theClass);
527:
528:                    if (generatedClass == null) {
529:                        ModuleNode mn = classNode.getModule();
530:                        SourceUnit msu = null;
531:                        if (mn != null)
532:                            msu = mn.getContext();
533:                        ClassNode main = null;
534:                        if (mn != null)
535:                            main = (ClassNode) mn.getClasses().get(0);
536:                        if (msu == su && main == classNode)
537:                            generatedClass = theClass;
538:                    }
539:
540:                    return theClass;
541:                }
542:
543:                protected Class onClassNode(ClassWriter classWriter,
544:                        ClassNode classNode) {
545:                    byte[] code = classWriter.toByteArray();
546:                    return createClass(code, classNode);
547:                }
548:
549:                public void call(ClassVisitor classWriter, ClassNode classNode) {
550:                    onClassNode((ClassWriter) classWriter, classNode);
551:                }
552:
553:                public Collection getLoadedClasses() {
554:                    return this .loadedClasses;
555:                }
556:            }
557:
558:            /**
559:             * open up the super class define that takes raw bytes
560:             *
561:             */
562:            public Class defineClass(String name, byte[] b) {
563:                return super .defineClass(name, b, 0, b.length);
564:            }
565:
566:            /**
567:             * loads a class from a file or a parent classloader.
568:             * This method does call loadClass(String, boolean, boolean, boolean)
569:             * with the last parameter set to false.
570:             * @throws CompilationFailedException 
571:             */
572:            public Class loadClass(final String name,
573:                    boolean lookupScriptFiles, boolean preferClassOverScript)
574:                    throws ClassNotFoundException, CompilationFailedException {
575:                return loadClass(name, lookupScriptFiles,
576:                        preferClassOverScript, false);
577:            }
578:
579:            /**
580:             * gets a class from the class cache. This cache contains only classes loaded through
581:             * this class loader or an InnerLoader instance. If no class is stored for a
582:             * specific name, then the method should return null. 
583:             *  
584:             * @param name of the class
585:             * @return the class stored for the given name 
586:             * @see #removeClassCacheEntry(String)
587:             * @see #setClassCacheEntry(Class)
588:             * @see #clearCache()
589:             */
590:            protected Class getClassCacheEntry(String name) {
591:                if (name == null)
592:                    return null;
593:                synchronized (classCache) {
594:                    Class cls = (Class) classCache.get(name);
595:                    return cls;
596:                }
597:            }
598:
599:            /**
600:             * sets an entry in the class cache. 
601:             * @param cls the class
602:             * @see #removeClassCacheEntry(String)
603:             * @see #getClassCacheEntry(String)
604:             * @see #clearCache()
605:             */
606:            protected void setClassCacheEntry(Class cls) {
607:                synchronized (classCache) {
608:                    classCache.put(cls.getName(), cls);
609:                }
610:            }
611:
612:            /**
613:             * removes a class from the class cache.
614:             * @param name of the class
615:             * @see #getClassCacheEntry(String)
616:             * @see #setClassCacheEntry(Class)
617:             * @see #clearCache()
618:             */
619:            protected void removeClassCacheEntry(String name) {
620:                synchronized (classCache) {
621:                    classCache.remove(name);
622:                }
623:            }
624:
625:            /**
626:             * adds a URL to the classloader.
627:             * @param url the new classpath element 
628:             */
629:            public void addURL(URL url) {
630:                super .addURL(url);
631:            }
632:
633:            /**
634:             * Indicates if a class is recompilable. Recompileable means, that the classloader
635:             * will try to locate a groovy source file for this class and then compile it again,
636:             * adding the resulting class as entry to the cache. Giving null as class is like a
637:             * recompilation, so the method should always return true here. Only classes that are
638:             * implementing GroovyObject are compileable and only if the timestamp in the class
639:             * is lower than Long.MAX_VALUE.  
640:             * 
641:             *  NOTE: First the parent loaders will be asked and only if they don't return a
642:             *  class the recompilation will happen. Recompilation also only happen if the source
643:             *  file is newer.
644:             * 
645:             * @see #isSourceNewer(URL, Class)
646:             * @param cls the class to be tested. If null the method should return true
647:             * @return true if the class should be compiled again
648:             */
649:            protected boolean isRecompilable(Class cls) {
650:                if (cls == null)
651:                    return true;
652:                if (recompile == null && !config.getRecompileGroovySource())
653:                    return false;
654:                if (recompile != null && !recompile.booleanValue())
655:                    return false;
656:                if (!GroovyObject.class.isAssignableFrom(cls))
657:                    return false;
658:                long timestamp = getTimeStamp(cls);
659:                if (timestamp == Long.MAX_VALUE)
660:                    return false;
661:
662:                return true;
663:            }
664:
665:            /**
666:             * sets if the recompilation should be enable. There are 3 possible
667:             * values for this. Any value different than null overrides the
668:             * value from the compiler configuration. true means to recompile if needed
669:             * false means to never recompile.  
670:             * @param mode the recompilation mode
671:             * @see CompilerConfiguration
672:             */
673:            public void setShouldRecompile(Boolean mode) {
674:                recompile = mode;
675:            }
676:
677:            /**
678:             * gets the currently set recompilation mode. null means, the 
679:             * compiler configuration is used. False means no recompilation and 
680:             * true means that recompilation will be done if needed. 
681:             * @return the recompilation mode
682:             */
683:            public Boolean isShouldRecompile() {
684:                return recompile;
685:            }
686:
687:            /**
688:             * loads a class from a file or a parent classloader.
689:             *
690:             * @param name                      of the class to be loaded
691:             * @param lookupScriptFiles         if false no lookup at files is done at all
692:             * @param preferClassOverScript     if true the file lookup is only done if there is no class
693:             * @param resolve                   @see ClassLoader#loadClass(java.lang.String, boolean)
694:             * @return                          the class found or the class created from a file lookup
695:             * @throws ClassNotFoundException
696:             */
697:            public Class loadClass(final String name,
698:                    boolean lookupScriptFiles, boolean preferClassOverScript,
699:                    boolean resolve) throws ClassNotFoundException,
700:                    CompilationFailedException {
701:                // look into cache
702:                Class cls = getClassCacheEntry(name);
703:
704:                // enable recompilation?
705:                boolean recompile = isRecompilable(cls);
706:                if (!recompile)
707:                    return cls;
708:
709:                // check security manager
710:                SecurityManager sm = System.getSecurityManager();
711:                if (sm != null) {
712:                    String className = name.replace('/', '.');
713:                    int i = className.lastIndexOf('.');
714:                    if (i != -1) {
715:                        sm.checkPackageAccess(className.substring(0, i));
716:                    }
717:                }
718:
719:                // try parent loader
720:                ClassNotFoundException last = null;
721:                try {
722:                    Class parentClassLoaderClass = super .loadClass(name,
723:                            resolve);
724:                    // always return if the parent loader was successfull 
725:                    if (cls != parentClassLoaderClass)
726:                        return parentClassLoaderClass;
727:                } catch (ClassNotFoundException cnfe) {
728:                    last = cnfe;
729:                } catch (NoClassDefFoundError ncdfe) {
730:                    if (ncdfe.getMessage().indexOf("wrong name") > 0) {
731:                        last = new ClassNotFoundException(name);
732:                    } else {
733:                        throw ncdfe;
734:                    }
735:                }
736:
737:                if (cls != null) {
738:                    // prefer class if no recompilation
739:                    preferClassOverScript |= !recompile;
740:                    if (preferClassOverScript)
741:                        return cls;
742:                }
743:
744:                // at this point the loading from a parent loader failed
745:                // and we want to recompile if needed.
746:                if (lookupScriptFiles) {
747:                    // synchronize on cache, as we want only one compilation
748:                    // at the same time
749:                    synchronized (classCache) {
750:                        // try groovy file
751:                        try {
752:                            // check if recompilation already happend.
753:                            if (getClassCacheEntry(name) != cls)
754:                                return getClassCacheEntry(name);
755:                            URL source = resourceLoader.loadGroovySource(name);
756:                            cls = recompile(source, name, cls);
757:                        } catch (IOException ioe) {
758:                            last = new ClassNotFoundException(
759:                                    "IOException while openening groovy source: "
760:                                            + name, ioe);
761:                        } finally {
762:                            if (cls == null) {
763:                                removeClassCacheEntry(name);
764:                            } else {
765:                                setClassCacheEntry(cls);
766:                            }
767:                        }
768:                    }
769:                }
770:
771:                if (cls == null) {
772:                    // no class found, there has to be an exception before then
773:                    if (last == null)
774:                        throw new AssertionError(true);
775:                    throw last;
776:                }
777:                return cls;
778:            }
779:
780:            /**
781:             * (Re)Comipiles the given source. 
782:             * This method starts the compilation of a given source, if
783:             * the source has changed since the class was created. For
784:             * this isSourceNewer is called.
785:             * 
786:             * @see #isSourceNewer(URL, Class)
787:             * @param source the source pointer for the compilation
788:             * @param className the name of the class to be generated
789:             * @param oldClass a possible former class
790:             * @return the old class if the source wasn't new enough, the new class else
791:             * @throws CompilationFailedException if the compilation failed
792:             * @throws IOException if the source is not readable
793:             * 
794:             */
795:            protected Class recompile(URL source, String className,
796:                    Class oldClass) throws CompilationFailedException,
797:                    IOException {
798:                if (source != null) {
799:                    // found a source, compile it if newer
800:                    if ((oldClass != null && isSourceNewer(source, oldClass))
801:                            || (oldClass == null)) {
802:                        sourceCache.remove(className);
803:                        return parseClass(source.openStream(), className);
804:                    }
805:                }
806:                return oldClass;
807:            }
808:
809:            /**
810:             * Implemented here to check package access prior to returning an
811:             * already loaded class.
812:             * @throws CompilationFailedException if the compilation failed
813:             * @throws ClassNotFoundException if the class was not found
814:             * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
815:             */
816:            protected Class loadClass(final String name, boolean resolve)
817:                    throws ClassNotFoundException {
818:                return loadClass(name, true, false, resolve);
819:            }
820:
821:            /**
822:             * gets the time stamp of a given class. For groovy
823:             * generated classes this usually means to return the value
824:             * of the static field __timeStamp. If the parameter doesn't
825:             * have such a field, then Long.MAX_VALUE is returned
826:             * 
827:             * @param cls the class 
828:             * @return the time stamp
829:             */
830:            protected long getTimeStamp(Class cls) {
831:                Long o;
832:                try {
833:                    Field field = cls.getField(Verifier.__TIMESTAMP);
834:                    o = (Long) field.get(null);
835:                } catch (Exception e) {
836:                    return Long.MAX_VALUE;
837:                }
838:                return o.longValue();
839:            }
840:
841:            private URL getSourceFile(String name) {
842:                String filename = name.replace('.', '/')
843:                        + config.getDefaultScriptExtension();
844:                URL ret = getResource(filename);
845:                if (ret != null && ret.getProtocol().equals("file")) {
846:                    String fileWithoutPackage = filename;
847:                    if (fileWithoutPackage.indexOf('/') != -1) {
848:                        int index = fileWithoutPackage.lastIndexOf('/');
849:                        fileWithoutPackage = fileWithoutPackage
850:                                .substring(index + 1);
851:                    }
852:                    File path = new File(ret.getFile()).getParentFile();
853:                    if (path.exists() && path.isDirectory()) {
854:                        File file = new File(path, fileWithoutPackage);
855:                        if (file.exists()) {
856:                            // file.exists() might be case insensitive. Let's do
857:                            // case sensitive match for the filename
858:                            File parent = file.getParentFile();
859:                            String[] files = parent.list();
860:                            for (int j = 0; j < files.length; j++) {
861:                                if (files[j].equals(fileWithoutPackage))
862:                                    return ret;
863:                            }
864:                        }
865:                    }
866:                    //file does not exist!
867:                    return null;
868:                }
869:                return ret;
870:            }
871:
872:            /**
873:             * Decides if the given source is newer than a class.
874:             * 
875:             * @see #getTimeStamp(Class)
876:             * @param source the source we may want to compile
877:             * @param cls the former class
878:             * @return true if the source is newer, false else
879:             * @throws IOException if it is not possible to open an
880:             * connection for the given source
881:             */
882:            protected boolean isSourceNewer(URL source, Class cls)
883:                    throws IOException {
884:                long lastMod;
885:
886:                // Special handling for file:// protocol, as getLastModified() often reports
887:                // incorrect results (-1)
888:                if (source.getProtocol().equals("file")) {
889:                    // Coerce the file URL to a File
890:                    String path = source.getPath().replace('/',
891:                            File.separatorChar).replace('|', ':');
892:                    File file = new File(path);
893:                    lastMod = file.lastModified();
894:                } else {
895:                    lastMod = source.openConnection().getLastModified();
896:                }
897:                long classTime = getTimeStamp(cls);
898:                return classTime + config.getMinimumRecompilationInterval() < lastMod;
899:            }
900:
901:            /**
902:             * adds a classpath to this classloader.  
903:             * @param path is a jar file or a directory.
904:             * @see #addURL(URL)
905:             */
906:            public void addClasspath(final String path) {
907:                AccessController.doPrivileged(new PrivilegedAction() {
908:                    public Object run() {
909:                        try {
910:                            File f = new File(path);
911:                            URL newURL = f.toURI().toURL();
912:                            URL[] urls = getURLs();
913:                            for (int i = 0; i < urls.length; i++) {
914:                                if (urls[i].equals(newURL))
915:                                    return null;
916:                            }
917:                            addURL(newURL);
918:                        } catch (MalformedURLException e) {
919:                            //TODO: fail through ?
920:                        }
921:                        return null;
922:                    }
923:                });
924:            }
925:
926:            /**
927:             * <p>Returns all Groovy classes loaded by this class loader.
928:             *
929:             * @return all classes loaded by this class loader
930:             */
931:            public Class[] getLoadedClasses() {
932:                synchronized (classCache) {
933:                    return (Class[]) classCache.values().toArray(new Class[0]);
934:                }
935:            }
936:
937:            /**
938:             * removes all classes from the class cache.
939:             * @see #getClassCacheEntry(String)
940:             * @see #setClassCacheEntry(Class)
941:             * @see #removeClassCacheEntry(String)
942:             */
943:            public void clearCache() {
944:                synchronized (classCache) {
945:                    classCache.clear();
946:                }
947:            }
948:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.