Speeding up start of applications by listing the classes that need post processing and analysing/processing only those
authorJaroslav Tulach <jaroslav.tulach@netbeans.org>
Sun, 12 Jan 2014 19:23:58 +0100
changeset 459fe9813565c54
parent 458 1a8e20574601
child 460 f61d5cd45635
Speeding up start of applications by listing the classes that need post processing and analysing/processing only those
boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java
boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java
boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java
boot/src/test/java/org/netbeans/html/boot/impl/Compile.java
boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java
     1.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java	Sun Jan 12 14:57:18 2014 +0100
     1.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java	Sun Jan 12 19:23:58 2014 +0100
     1.3 @@ -43,6 +43,9 @@
     1.4  package org.netbeans.html.boot.impl;
     1.5  
     1.6  import java.io.IOException;
     1.7 +import java.io.OutputStream;
     1.8 +import java.io.OutputStreamWriter;
     1.9 +import java.io.PrintWriter;
    1.10  import java.io.Writer;
    1.11  import java.util.Collections;
    1.12  import java.util.HashMap;
    1.13 @@ -85,6 +88,8 @@
    1.14  public final class JavaScriptProcesor extends AbstractProcessor {
    1.15      private final Map<String,Map<String,ExecutableElement>> javacalls = 
    1.16          new HashMap<String,Map<String,ExecutableElement>>();
    1.17 +    private final Map<String,Set<TypeElement>> bodies = 
    1.18 +        new HashMap<String, Set<TypeElement>>();
    1.19      
    1.20      @Override
    1.21      public Set<String> getSupportedAnnotationTypes() {
    1.22 @@ -107,6 +112,17 @@
    1.23              JavaScriptBody jsb = e.getAnnotation(JavaScriptBody.class);
    1.24              if (jsb == null) {
    1.25                  continue;
    1.26 +            } else {
    1.27 +                Set<TypeElement> classes = this.bodies.get(findPkg(e));
    1.28 +                if (classes == null) {
    1.29 +                    classes = new HashSet<TypeElement>();
    1.30 +                    bodies.put(findPkg(e), classes);
    1.31 +                }
    1.32 +                Element t = e.getEnclosingElement();
    1.33 +                while (!t.getKind().isClass() && !t.getKind().isInterface()) {
    1.34 +                    t = t.getEnclosingElement();
    1.35 +                }
    1.36 +                classes.add((TypeElement)t);
    1.37              }
    1.38              String[] arr = jsb.args();
    1.39              if (params.size() != arr.length) {
    1.40 @@ -151,6 +167,7 @@
    1.41  
    1.42          if (roundEnv.processingOver()) {
    1.43              generateCallbackClass(javacalls);
    1.44 +            generateJavaScriptBodyList(bodies);
    1.45              javacalls.clear();
    1.46          }
    1.47          return true;
    1.48 @@ -262,6 +279,33 @@
    1.49          }
    1.50      }
    1.51      
    1.52 +    private void generateJavaScriptBodyList(Map<String,Set<TypeElement>> bodies) {
    1.53 +        for (Map.Entry<String, Set<TypeElement>> entry : bodies.entrySet()) {
    1.54 +            String pkg = entry.getKey();
    1.55 +            Set<TypeElement> classes = entry.getValue();
    1.56 +            
    1.57 +            try {
    1.58 +                FileObject out = processingEnv.getFiler().createResource(
    1.59 +                    StandardLocation.CLASS_OUTPUT, pkg, "net.java.html.js.classes",
    1.60 +                    classes.iterator().next()
    1.61 +                );
    1.62 +                OutputStream os = out.openOutputStream();
    1.63 +                try {
    1.64 +                    PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
    1.65 +                    for (TypeElement type : classes) {
    1.66 +                        w.println(processingEnv.getElementUtils().getBinaryName(type));
    1.67 +                    }
    1.68 +                    w.flush();
    1.69 +                    w.close();
    1.70 +                } finally {
    1.71 +                    os.close();
    1.72 +                }
    1.73 +            } catch (IOException x) {
    1.74 +                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
    1.75 +            }
    1.76 +        }
    1.77 +    }
    1.78 +    
    1.79      private void generateCallbackClass(Map<String,Map<String, ExecutableElement>> process) {
    1.80          for (Map.Entry<String, Map<String, ExecutableElement>> pkgEn : process.entrySet()) {
    1.81              String pkgName = pkgEn.getKey();
     2.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java	Sun Jan 12 14:57:18 2014 +0100
     2.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java	Sun Jan 12 19:23:58 2014 +0100
     2.3 @@ -59,7 +59,11 @@
     2.4      @Override
     2.5      public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
     2.6          try {
     2.7 -            return FnUtils.transform(classfileBuffer, loader);
     2.8 +            if (JsPkgCache.process(loader, className)) {
     2.9 +                return FnUtils.transform(classfileBuffer, loader);
    2.10 +            } else {
    2.11 +                return classfileBuffer;
    2.12 +            }
    2.13          } catch (Exception ex) {
    2.14              return classfileBuffer;
    2.15          }
     3.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java	Sun Jan 12 14:57:18 2014 +0100
     3.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java	Sun Jan 12 19:23:58 2014 +0100
     3.3 @@ -117,10 +117,10 @@
     3.4                  }
     3.5                  is.close();
     3.6                  is = null;
     3.7 -                arr = FnUtils.transform(arr, JsClassLoader.this);
     3.8 -                if (arr != null) {
     3.9 -                    return defineClass(name, arr, 0, arr.length);
    3.10 +                if (JsPkgCache.process(this, name)) {
    3.11 +                    arr = FnUtils.transform(arr, JsClassLoader.this);
    3.12                  }
    3.13 +                return defineClass(name, arr, 0, arr.length);
    3.14              } catch (IOException ex) {
    3.15                  throw new ClassNotFoundException("Can't load " + name, ex);
    3.16              } finally {
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java	Sun Jan 12 19:23:58 2014 +0100
     4.3 @@ -0,0 +1,132 @@
     4.4 +/**
     4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.6 + *
     4.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
     4.8 + *
     4.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    4.10 + * Other names may be trademarks of their respective owners.
    4.11 + *
    4.12 + * The contents of this file are subject to the terms of either the GNU
    4.13 + * General Public License Version 2 only ("GPL") or the Common
    4.14 + * Development and Distribution License("CDDL") (collectively, the
    4.15 + * "License"). You may not use this file except in compliance with the
    4.16 + * License. You can obtain a copy of the License at
    4.17 + * http://www.netbeans.org/cddl-gplv2.html
    4.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    4.19 + * specific language governing permissions and limitations under the
    4.20 + * License.  When distributing the software, include this License Header
    4.21 + * Notice in each file and include the License file at
    4.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    4.23 + * particular file as subject to the "Classpath" exception as provided
    4.24 + * by Oracle in the GPL Version 2 section of the License file that
    4.25 + * accompanied this code. If applicable, add the following below the
    4.26 + * License Header, with the fields enclosed by brackets [] replaced by
    4.27 + * your own identifying information:
    4.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    4.29 + *
    4.30 + * Contributor(s):
    4.31 + *
    4.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    4.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
    4.34 + *
    4.35 + * If you wish your version of this file to be governed by only the CDDL
    4.36 + * or only the GPL Version 2, indicate your decision by adding
    4.37 + * "[Contributor] elects to include this software in this distribution
    4.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    4.39 + * single choice of license, a recipient has the option to distribute
    4.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    4.41 + * to extend the choice of license to its licensees as provided above.
    4.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    4.43 + * Version 2 license, then the option applies only if the new code is
    4.44 + * made subject to such option by the copyright holder.
    4.45 + */
    4.46 +package org.netbeans.html.boot.impl;
    4.47 +
    4.48 +import java.io.BufferedReader;
    4.49 +import java.io.IOException;
    4.50 +import java.io.InputStream;
    4.51 +import java.io.InputStreamReader;
    4.52 +import java.net.URL;
    4.53 +import java.util.Collections;
    4.54 +import java.util.Enumeration;
    4.55 +import java.util.Map;
    4.56 +import java.util.Set;
    4.57 +import java.util.TreeSet;
    4.58 +import java.util.WeakHashMap;
    4.59 +import java.util.logging.Level;
    4.60 +import java.util.logging.Logger;
    4.61 +
    4.62 +/**
    4.63 + *
    4.64 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.65 + */
    4.66 +final class JsPkgCache {
    4.67 +    private final Map<String,Set<String>> props = new WeakHashMap<String, Set<String>>();
    4.68 +    private static final Map<ClassLoader, JsPkgCache> CACHE = new WeakHashMap<ClassLoader, JsPkgCache>();
    4.69 +    private static final Set<String> NONE = Collections.emptySet();
    4.70 +
    4.71 +    public static boolean process(ClassLoader l, String className) {
    4.72 +        if (className.equals("org.netbeans.html.boot.impl.Test")) {
    4.73 +            return true;
    4.74 +        }
    4.75 +        Set<String> p;
    4.76 +        JsPkgCache c;
    4.77 +        String pkgName;
    4.78 +        synchronized (CACHE) {
    4.79 +            c = CACHE.get(l);
    4.80 +            if (c == null) {
    4.81 +                c = new JsPkgCache();
    4.82 +                CACHE.put(l, c);
    4.83 +            }
    4.84 +            int lastDot = className.lastIndexOf('.');
    4.85 +            pkgName = className.substring(0, lastDot + 1).replace('.', '/');
    4.86 +            p = c.props.get(pkgName);
    4.87 +            if (p == NONE) {
    4.88 +                return false;
    4.89 +            } else if (p != null) {
    4.90 +                return p.contains(className);
    4.91 +            }
    4.92 +        }
    4.93 +        final String res = pkgName + "net.java.html.js.classes";
    4.94 +        
    4.95 +        Enumeration<URL> en;
    4.96 +        try {
    4.97 +            en = l.getResources(res);
    4.98 +        } catch (IOException ex) {
    4.99 +            en = null;
   4.100 +        }
   4.101 +        if (en == null || !en.hasMoreElements()) synchronized (CACHE) {
   4.102 +            c.props.put(pkgName, NONE);
   4.103 +            return false;
   4.104 +        }
   4.105 +
   4.106 +        try {
   4.107 +            Set<String> arr = new TreeSet<String>();
   4.108 +            while (en.hasMoreElements()) {
   4.109 +                URL u = en.nextElement();
   4.110 +                BufferedReader r = new BufferedReader(
   4.111 +                    new InputStreamReader(u.openStream(), "UTF-8")
   4.112 +                );
   4.113 +                for (;;) {
   4.114 +                    String line = r.readLine();
   4.115 +                    if (line == null) {
   4.116 +                        break;
   4.117 +                    }
   4.118 +                    arr.add(line);
   4.119 +                }
   4.120 +                r.close();
   4.121 +            }
   4.122 +            p = arr;
   4.123 +        } catch (IOException ex) {
   4.124 +            LOG.log(Level.WARNING, "Can't read " + res, ex);
   4.125 +            p = NONE;
   4.126 +        }
   4.127 +        
   4.128 +        synchronized (CACHE) {
   4.129 +            c.props.put(pkgName, p);
   4.130 +            return p.contains(className);
   4.131 +        }
   4.132 +        
   4.133 +    }
   4.134 +    private static final Logger LOG = Logger.getLogger(JsPkgCache.class.getName());
   4.135 +}
     5.1 --- a/boot/src/test/java/org/netbeans/html/boot/impl/Compile.java	Sun Jan 12 14:57:18 2014 +0100
     5.2 +++ b/boot/src/test/java/org/netbeans/html/boot/impl/Compile.java	Sun Jan 12 19:23:58 2014 +0100
     5.3 @@ -55,6 +55,8 @@
     5.4  import java.util.List;
     5.5  import java.util.Locale;
     5.6  import java.util.Map;
     5.7 +import java.util.logging.Level;
     5.8 +import java.util.logging.Logger;
     5.9  import java.util.regex.Matcher;
    5.10  import java.util.regex.Pattern;
    5.11  import javax.tools.Diagnostic;
    5.12 @@ -153,6 +155,16 @@
    5.13          
    5.14          JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
    5.15              @Override
    5.16 +            public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
    5.17 +                try {
    5.18 +                    return new VirtFO(new URI("mem://resource/" + relativeName), Kind.OTHER, relativeName);
    5.19 +                } catch (URISyntaxException ex) {
    5.20 +                    throw new IllegalStateException(ex);
    5.21 +                }
    5.22 +            }
    5.23 +            
    5.24 +            
    5.25 +            @Override
    5.26              public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
    5.27                  if (kind  == Kind.CLASS) {
    5.28                      final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
     6.1 --- a/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java	Sun Jan 12 14:57:18 2014 +0100
     6.2 +++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java	Sun Jan 12 19:23:58 2014 +0100
     6.3 @@ -49,6 +49,7 @@
     6.4  import java.net.URLClassLoader;
     6.5  import java.util.ArrayList;
     6.6  import java.util.Arrays;
     6.7 +import java.util.Collections;
     6.8  import java.util.Enumeration;
     6.9  import java.util.List;
    6.10  import javax.script.Invocable;
    6.11 @@ -123,7 +124,12 @@
    6.12  
    6.13              @Override
    6.14              protected Enumeration<URL> findResources(String name) {
    6.15 -                throw new UnsupportedOperationException();
    6.16 +                URL u = findResource(name);
    6.17 +                List<URL> arr = new ArrayList<URL>();
    6.18 +                if (u != null) {
    6.19 +                    arr.add(u);
    6.20 +                }
    6.21 +                return Collections.enumeration(arr);
    6.22              }
    6.23  
    6.24              @Override