Coverage Report - org.webslinger.WebslingerClassLoader
 
Classes in this File Line Coverage Branch Coverage Complexity
WebslingerClassLoader
55%
94/170
63%
27/43
0
WebslingerClassLoader$1
11%
1/9
0%
0/3
0
WebslingerClassLoader$Filter
N/A
N/A
0
WebslingerClassLoader$WebslingerIClassLoader
7%
4/55
0%
0/6
0
 
 1  
 package org.webslinger;
 2  
 
 3  
 import java.io.InputStream;
 4  
 import java.io.IOException;
 5  
 import java.io.OutputStream;
 6  
 import java.net.URL;
 7  
 import java.net.URLConnection;
 8  
 import java.net.URLStreamHandler;
 9  
 import java.security.CodeSource;
 10  
 import java.security.SecureClassLoader;
 11  
 import java.security.cert.Certificate;
 12  
 import java.util.jar.Attributes;
 13  
 import java.util.ArrayList;
 14  
 import java.util.Collection;
 15  
 import java.util.Enumeration;
 16  
 import java.util.HashMap;
 17  
 import java.util.HashSet;
 18  
 import java.util.Iterator;
 19  
 import java.util.List;
 20  
 import java.util.Map;
 21  
 import java.util.Set;
 22  
 
 23  
 import org.apache.commons.collections.CollectionUtils;
 24  
 import org.apache.commons.collections.iterators.IteratorEnumeration;
 25  
 import org.apache.commons.vfs.FileContent;
 26  
 import org.apache.commons.vfs.FileObject;
 27  
 import org.apache.commons.vfs.FileSystemException;
 28  
 import org.apache.commons.vfs.FileSystemManager;
 29  
 import org.apache.commons.vfs.FileUtil;
 30  
 
 31  
 import org.codehaus.janino.ClassFileIClass;
 32  
 import org.codehaus.janino.ClassLoaderIClassLoader;
 33  
 import org.codehaus.janino.CompileException;
 34  
 import org.codehaus.janino.DebuggingInformation;
 35  
 import org.codehaus.janino.Descriptor;
 36  
 import org.codehaus.janino.IClass;
 37  
 import org.codehaus.janino.IClassLoader;
 38  
 import org.codehaus.janino.Java;
 39  
 import org.codehaus.janino.JavaSourceIClassLoader;
 40  
 import org.codehaus.janino.Parser;
 41  
 import org.codehaus.janino.Scanner;
 42  
 import org.codehaus.janino.UnitCompiler;
 43  
 import org.codehaus.janino.util.ClassFile;
 44  
 import org.codehaus.janino.util.enumerator.EnumeratorSet;
 45  
 import org.codehaus.janino.util.resource.Resource;
 46  
 import org.codehaus.janino.util.resource.ResourceFinder;
 47  
 
 48  
 import org.webslinger.commons.vfs.VFSUtil;
 49  
 import org.webslinger.commons.vfs.operations.AllFilesOperation;
 50  
 import org.webslinger.launcher.Instrumenter;
 51  
 import org.webslinger.launcher.Main;
 52  
 
 53  
 public class WebslingerClassLoader extends SecureClassLoader {
 54  26
     protected EnumeratorSet debuggingInformation = DebuggingInformation.ALL;
 55  26
     protected final IClassLoader iClassLoader = new WebslingerIClassLoader();
 56  
     protected final FileObject root;
 57  
     protected final FileObject src;
 58  
     protected final FileObject compiled;
 59  26
     protected final List<FileObject> libs = new ArrayList<FileObject>();
 60  26
     protected final HashSet<UnitCompiler> unitCompilers = new HashSet<UnitCompiler>();
 61  26
     protected final Instrumenter instrumenter = Main.getInstrumenter();
 62  
 
 63  
     public WebslingerClassLoader(ClassLoader parent, FileObject root) throws IOException {
 64  26
         super(parent);
 65  26
         this.root = root;
 66  26
         FileObject lib = root.resolveFile("Lib");
 67  26
         libs.add(root.resolveFile("Classes"));
 68  26
         FileSystemManager fsm = root.getFileSystem().getFileSystemManager();
 69  26
         if (lib.getType().hasChildren()) {
 70  21
             FileObject[] children = lib.getChildren();
 71  21
             if (children != null) {
 72  21
                 for (FileObject file: children) {
 73  0
                     if (!JAR_FILTER.accept(lib, file, 0)) continue;
 74  0
                     String baseName = file.getName().getBaseName();
 75  0
                     if (baseName.startsWith(".") && file.getType().hasChildren()) continue;
 76  0
                     if (fsm.canCreateFileSystem(file)) file = fsm.createFileSystem(file);
 77  0
                     libs.add(file);
 78  
                 }
 79  
             }
 80  
         }
 81  26
         src = root.resolveFile("Src");
 82  26
         compiled = root.resolveFile("Var/Compiled");
 83  
 
 84  26
     }
 85  
 
 86  
     protected URL findResource(String name) {
 87  
         try {
 88  18
             FileObject file = findFile(name);
 89  18
             return file != null ? file.getURL() : null;
 90  0
         } catch (FileSystemException e) {
 91  0
             return null;
 92  
         }
 93  
     }
 94  
 
 95  
     protected Enumeration<URL> findResources(String name) {
 96  
         try {
 97  26
             ArrayList<URL> urls = new ArrayList<URL>();
 98  26
             List<FileObject> files = findFiles(name);
 99  26
             for (FileObject file: files) {
 100  0
                 AllFilesOperation op = VFSUtil.getOperation(file, AllFilesOperation.class);
 101  0
                 if (op == null) {
 102  0
                     urls.add(file.getURL());
 103  
                 } else {
 104  0
                     urls.addAll(op.getAllURLs());
 105  
                 }
 106  0
             }
 107  26
             return new IteratorEnumeration(urls.iterator());
 108  0
         } catch (FileSystemException e) {
 109  0
             return null;
 110  
         }
 111  
     }
 112  
 
 113  
     protected FileObject resolveFile(FileObject base, String name) throws FileSystemException {
 114  12282
         FileObject file = base.resolveFile(name);
 115  12282
         return !file.exists() ? null : file;
 116  
     }
 117  
 
 118  
     protected void addFile(FileObject base, String name, List<FileObject> files) throws FileSystemException {
 119  78
         FileObject file = base.resolveFile(name);
 120  78
         if (file.exists()) files.add(file);
 121  78
     }
 122  
 
 123  
     protected FileObject findCompiledFile(String name) throws FileSystemException {
 124  4114
         return resolveFile(compiled, name);
 125  
     }
 126  
 
 127  
     protected void addCompiledFile(String name, List<FileObject> files) throws FileSystemException {
 128  26
         addFile(compiled, name, files);
 129  26
     }
 130  
 
 131  
     protected FileObject findSrcFile(String name) throws FileSystemException {
 132  4114
         return resolveFile(src, name);
 133  
     }
 134  
 
 135  
     protected void addSrcFile(String name, List<FileObject> files) throws FileSystemException {
 136  26
         addFile(src, name, files);
 137  26
     }
 138  
 
 139  
     protected FileObject findLibFile(String name) throws FileSystemException {
 140  4054
         for (FileObject lib: libs) {
 141  4054
             FileObject file = resolveFile(lib, name);
 142  4054
             if (file != null) return file;
 143  4054
         }
 144  4054
         return null;
 145  
     }
 146  
 
 147  
     protected void addLibFiles(String name, List<FileObject> files) throws FileSystemException {
 148  26
         for (FileObject lib: libs) {
 149  26
             addFile(lib, name, files);
 150  
         }
 151  26
     }
 152  
 
 153  
     protected FileObject findFile(String name) throws FileSystemException {
 154  18
         FileObject file = findCompiledFile(name);
 155  18
         if (file == null) file = findSrcFile(name);
 156  18
         return file == null ? findLibFile(name) : file;
 157  
     }
 158  
 
 159  
     protected List<FileObject> findFiles(String name) throws FileSystemException {
 160  26
         ArrayList<FileObject> files = new ArrayList<FileObject>();
 161  26
         addCompiledFile(name, files);
 162  26
         addSrcFile(name, files);
 163  26
         addLibFiles(name, files);
 164  26
         return files;
 165  
     }
 166  
 
 167  
     //protected abstract void findFiles(String name, List files) throws FileSystemException;
 168  
     protected FileObject getClassFile(String name, List certs) throws ClassNotFoundException, IOException {
 169  4096
         String baseName = name.replace('.', '/');
 170  4096
         String classFileName = baseName + ".class";
 171  4096
         int dollar = baseName.indexOf('$');
 172  4096
         String srcFileName = (dollar != -1 ? baseName.substring(0, dollar) : baseName) + ".java";
 173  
         FileObject certFile;
 174  4096
         FileObject srcFile = certFile = findSrcFile(srcFileName);
 175  4096
         FileObject classFile = findCompiledFile(classFileName);
 176  4096
         if (classFile == null) {
 177  4036
             if (srcFile != null) {
 178  0
                 compile(name);
 179  0
                 classFile = findCompiledFile(classFileName);
 180  0
                 if (classFile == null) throw new ClassNotFoundException(srcFileName + " does not contain " + name);
 181  
             }
 182  
         } else {
 183  60
             if (srcFile == null) {
 184  0
                 classFile.delete();
 185  0
                 classFile = null;
 186  60
             } else if (srcFile.getContent().getLastModifiedTime() > classFile.getContent().getLastModifiedTime()) {
 187  0
                 classFile.delete();
 188  0
                 compile(name);
 189  0
                 classFile = findCompiledFile(classFileName);
 190  0
                 if (classFile == null) throw new ClassNotFoundException(srcFileName + " does not contain " + name);
 191  
             }
 192  
         }
 193  4096
         if (classFile == null) certFile = classFile = findLibFile(classFileName);
 194  4096
         if (classFile == null) return null;
 195  60
         CollectionUtils.addAll(certs, certFile.getContent().getCertificates());
 196  60
         return classFile;
 197  
     }
 198  
 
 199  
     protected Class findClass(String name) throws ClassNotFoundException {
 200  
         try {
 201  4096
             ArrayList<Certificate> certs = new ArrayList<Certificate>();
 202  4096
             FileObject classFile = getClassFile(name, certs);
 203  4096
             if (classFile == null) throw new ClassNotFoundException(name);
 204  60
             int pos = name.lastIndexOf('.');
 205  60
             String packageName = pos != -1 ? name.substring(0, pos) : null;
 206  60
             FileObject parent = classFile.getParent();
 207  60
             Package pkg = getPackage(packageName);
 208  60
             if (pkg != null) {
 209  48
                 if (pkg.isSealed()) {
 210  0
                     if (!pkg.isSealed(parent.getURL())) throw new ClassNotFoundException("redefine class in package that is sealed: " + name);
 211  48
                 } else if (isSealed(parent)) {
 212  0
                     throw new ClassNotFoundException("redefine sealed class in unsealed package: " + name);
 213  
                 }
 214  
             } else {
 215  12
                 definePackage(packageName, parent);
 216  
             }
 217  
             // FIXME: how to walk layered files, to find the real one
 218  60
             FileObject parentLayer = classFile.getFileSystem().getParentLayer();
 219  
             CodeSource cs;
 220  60
             if (parentLayer != null) {
 221  60
                 cs = new CodeSource(parentLayer.getURL(), certs.toArray(new Certificate[certs.size()]));
 222  
             } else {
 223  0
                 cs = null;
 224  
             }
 225  60
             byte[] bytes = instrument(name, FileUtil.getContent(classFile));
 226  60
             return defineClass(name, bytes, 0, bytes.length, cs);
 227  0
         } catch (IOException e) {
 228  0
             throw new ClassNotFoundException(name, e);
 229  
         }
 230  
     }
 231  
 
 232  
     protected byte[] instrument(String className, byte[] bytes) throws IOException {
 233  60
         if (instrumenter == null || !className.startsWith("org.webslinger.")) return bytes;
 234  60
         bytes = instrumenter.instrumentClass(bytes);
 235  60
         return bytes;
 236  
     }
 237  
 
 238  
     protected boolean isSealed(FileObject parent) throws FileSystemException {
 239  60
         return "true".equalsIgnoreCase((String) parent.getContent().getAttribute(Attributes.Name.SEALED.toString()));
 240  
     }
 241  
 
 242  
     protected Package definePackage(String packageName, FileObject parent) throws FileSystemException {
 243  12
         FileContent content = parent.getContent();
 244  
 
 245  12
         String specTitle = (String) content.getAttribute(Attributes.Name.SPECIFICATION_TITLE.toString());
 246  12
         String specVendor = (String) content.getAttribute(Attributes.Name.SPECIFICATION_VENDOR.toString());
 247  12
         String specVersion = (String) content.getAttribute(Attributes.Name.SPECIFICATION_VERSION.toString());
 248  12
         String implTitle = (String) content.getAttribute(Attributes.Name.IMPLEMENTATION_TITLE.toString());
 249  12
         String implVendor = (String) content.getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR.toString());
 250  12
         String implVersion = (String) content.getAttribute(Attributes.Name.IMPLEMENTATION_VERSION.toString());
 251  
 
 252  12
         URL sealBase = isSealed(parent) ? parent.getURL() : null;
 253  
 
 254  12
         return definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
 255  
     }
 256  
 
 257  
     protected void compile(String className) throws ClassNotFoundException, IOException {
 258  0
         Map<String, byte[]> bytecodes = generateBytecodes(className);
 259  0
         if (bytecodes == null) throw new ClassNotFoundException(className);
 260  0
         for (Map.Entry<String, byte[]> entry: bytecodes.entrySet()) {
 261  0
             String generatedClassName = entry.getKey();
 262  0
             byte bytecode[] = entry.getValue();
 263  0
             String generatedClassFileName = ClassFile.getClassFileResourceName(generatedClassName);
 264  0
             String sourceName = ClassFile.getSourceResourceName(generatedClassName);
 265  0
             FileObject tmpFile = compiled.resolveFile(generatedClassFileName);
 266  
             //FileObject tmpFile = compiled.resolveFile(generatedClassFileName + ".tmp");
 267  0
             FileObject srcFile = src.resolveFile(sourceName);
 268  0
             OutputStream os = null;
 269  
             try {
 270  0
                 os = tmpFile.getContent().getOutputStream();
 271  0
                 os.write(bytecode);
 272  0
             } catch (IOException e) {
 273  0
                 throw new ClassNotFoundException("Writing class file to \"" + generatedClassFileName + "\"", e);
 274  
             } finally {
 275  0
                 if (os != null) { try { os.close(); } catch (IOException e) { } }
 276  
             }
 277  0
             tmpFile.getContent().setLastModifiedTime(srcFile.getContent().getLastModifiedTime());
 278  
             //tmpFile.moveTo(compiled.resolveFile(generatedClassFileName));
 279  0
         }
 280  0
     }
 281  
 
 282  
     protected Map<String, byte[]> generateBytecodes(String name) throws ClassNotFoundException {
 283  0
         if (iClassLoader.loadIClass(Descriptor.fromClassName(name)) == null) return null;
 284  0
         Map<String, byte[]> bytecodes = new HashMap<String, byte[]>();
 285  0
         Set<UnitCompiler> compiledUnitCompilers = new HashSet<UnitCompiler>();
 286  
 COMPILE_UNITS:
 287  
         for (;;) {
 288  0
             Iterator<UnitCompiler> it = unitCompilers.iterator();
 289  0
             while (it.hasNext()) {
 290  0
                 UnitCompiler uc = it.next();
 291  0
                 if (compiledUnitCompilers.contains(uc)) continue;
 292  
                 ClassFile cfs[];
 293  
                 try {
 294  0
                     cfs = uc.compileUnit(debuggingInformation);
 295  0
                 } catch (CompileException e) {
 296  0
                     it.remove();
 297  0
                     throw new ClassNotFoundException("Compiling unit \"" + uc + "\"", e);
 298  0
                 }
 299  0
                 for (ClassFile cf: cfs) {
 300  0
                     bytecodes.put(cf.getThisClassName(), cf.toByteArray());
 301  
                 }
 302  0
                 compiledUnitCompilers.add(uc);
 303  0
                 continue COMPILE_UNITS;
 304  
             }
 305  0
             return bytecodes;
 306  
         }
 307  
     }
 308  
 
 309  
     protected Class defineBytecodes(String name, Map<String, byte[]> bytecodes) {
 310  0
         Class clazz = null;
 311  0
         for (Map.Entry<String, byte[]> entry: bytecodes.entrySet()) {
 312  0
             String compiledClassName = entry.getKey();
 313  0
             byte bytes[] = entry.getValue();
 314  0
             Class c = defineBytecode(compiledClassName, bytes);
 315  0
             if (compiledClassName.equals(name)) clazz = c;
 316  0
         };
 317  0
         return clazz;
 318  
     }
 319  
 
 320  
     protected Class defineBytecode(String className, byte bytes[]) {
 321  
         try {
 322  0
             bytes = instrument(className, bytes);
 323  0
         } catch (IOException e) {
 324  0
             e.printStackTrace();
 325  
             // ignore
 326  0
         }
 327  0
         return defineClass(className, bytes, 0, bytes.length);
 328  
     }
 329  
 
 330  
     protected interface Filter {
 331  
         boolean accept(FileObject base, FileObject file, int depth);
 332  
     }
 333  
 
 334  1
     protected static final Filter JAR_FILTER = new Filter() {
 335  
         public boolean accept(FileObject base, FileObject file, int depth) {
 336  
             try {
 337  0
                 if (file.getType().hasChildren()) return true;
 338  0
             } catch (FileSystemException e) {
 339  0
                 return false;
 340  0
             }
 341  0
             String ext = file.getName().getExtension();
 342  0
             if (ext == null) return false;
 343  0
             ext = ext.toLowerCase();
 344  0
             return "jar".equals(ext) || "zip".equals(ext);
 345  
         }
 346  
     };
 347  
 
 348  
     protected class WebslingerIClassLoader extends IClassLoader {
 349  26
         protected WebslingerIClassLoader() {
 350  26
             super(new ClassLoaderIClassLoader(WebslingerClassLoader.this.getParent()));
 351  26
             postConstruct();
 352  26
         }
 353  
 
 354  
         protected IClass findIClass(String descriptor) throws ClassNotFoundException {
 355  0
             String className = Descriptor.toClassName(descriptor);
 356  
 
 357  0
             if (className.startsWith("java.")) return null;
 358  
 
 359  0
             for (UnitCompiler uc: unitCompilers) {
 360  0
                 IClass res = uc.findClass(className);
 361  0
                 if (res == null) continue;
 362  0
                 defineIClass(res);
 363  0
                 return res;
 364  
             }
 365  
 
 366  0
             String srcFileName = ClassFile.getSourceResourceName(className);
 367  
             try {
 368  0
                 FileObject srcFile = findSrcFile(srcFileName);
 369  0
                 if (srcFile != null) return parseSrcFile(srcFile, className);
 370  0
             } catch (IOException e) {
 371  0
             }
 372  0
             String classFileName = ClassFile.getClassFileResourceName(className);
 373  
             try {
 374  0
                 FileObject classFile = findLibFile(classFileName);
 375  0
                 if (classFile != null) return parseClassFile(classFile);
 376  0
             } catch (IOException e) {
 377  0
             }
 378  
 
 379  0
             return null;
 380  
         }
 381  
 
 382  
         protected IClass parseClassFile(FileObject classFile) throws ClassNotFoundException {
 383  
             // Open the class file resource.
 384  
             InputStream in;
 385  
             try {
 386  0
                 in = classFile.getContent().getInputStream();
 387  0
             } catch (IOException e) {
 388  0
                 throw new ClassNotFoundException("Opening resource \"" + classFile + "\"", e);
 389  0
             }
 390  
 
 391  
             // Load the IClass from the class file.
 392  
             ClassFile cf;
 393  
             try {
 394  0
                 cf = new ClassFile(in);
 395  0
             } catch (IOException e) {
 396  0
                 e.printStackTrace();
 397  0
                 throw new ClassNotFoundException("Reading resource \"" + classFile + "\"", e);
 398  
             } finally {
 399  0
                 try { in.close(); } catch (IOException e) { }
 400  0
             }
 401  0
             IClass iClass = new ClassFileIClass(cf, this);
 402  0
             defineIClass(iClass);
 403  0
             return iClass;
 404  
         }
 405  
 
 406  
         protected IClass parseSrcFile(FileObject srcFile, String className) throws ClassNotFoundException {
 407  
             try {
 408  
                 // Scan and parse the source file.
 409  0
                 InputStream inputStream = srcFile.getContent().getInputStream();
 410  
                 Java.CompilationUnit cu;
 411  
                 try {
 412  0
                     Scanner scanner = new Scanner(srcFile.getName().getPath(), inputStream, null); //this.optionalCharacterEncoding);
 413  
                     //scanner.setWarningHandler(this.optionalWarningHandler);
 414  0
                     Parser parser = new Parser(scanner);
 415  
                     //parser.setWarningHandler(this.optionalWarningHandler);
 416  0
                     cu = parser.parseCompilationUnit();
 417  
                 } finally {
 418  0
                     try { inputStream.close(); } catch (IOException e) { }
 419  0
                 }
 420  0
                 UnitCompiler uc = new UnitCompiler(cu, this);
 421  
                 //uc.setCompileErrorHandler(this.optionalCompileErrorHandler);
 422  
                 //uc.setWarningHandler(this.optionalWarningHandler);
 423  
 
 424  
                 // Remember compilation unit for later compilation.
 425  0
                 unitCompilers.add(uc);
 426  
 
 427  
                 // Find the class/interface declaration in the com
 428  0
                 IClass res = uc.findClass(className);
 429  0
                 if (res == null) throw new Parser.ParseException("Source file \"" + srcFile.getName() + "\" does not declare class \"" + className + "\"", null);
 430  0
                 defineIClass(res);
 431  0
                 return res;
 432  0
             } catch (CompileException e) {
 433  0
                 throw new ClassNotFoundException("Parsing compilation unit \"" + srcFile + "\"", e);
 434  0
             } catch (Scanner.ScanException e) {
 435  0
                 throw new ClassNotFoundException("Parsing compilation unit \"" + srcFile + "\"", e);
 436  0
             } catch (Parser.ParseException e) {
 437  0
                 throw new ClassNotFoundException("Parsing compilation unit \"" + srcFile + "\"", e);
 438  0
             } catch (IOException e) {
 439  0
                 throw new ClassNotFoundException("Parsing compilation unit \"" + srcFile + "\"", e);
 440  
             }
 441  
         }
 442  
     }
 443  
 }