View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3;
18  
19  import java.io.File;
20  import java.io.FileWriter;
21  import java.lang.reflect.Method;
22  import java.net.URL;
23  import java.net.URLClassLoader;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  import javax.tools.Diagnostic;
28  import javax.tools.DiagnosticCollector;
29  import javax.tools.JavaCompiler;
30  import javax.tools.JavaFileObject;
31  import javax.tools.StandardJavaFileManager;
32  import javax.tools.ToolProvider;
33  
34  /**
35   * Helper class to test GC / reference interactions. Dynamically creates a class
36   * by compiling generated source Java code and load it through a dedicated class
37   * loader.
38   */
39  public class ClassCreator {
40  
41      //private final JexlEngine jexl;
42      private final File base;
43      private File packageDir = null;
44      private int seed = 0;
45      private String ctorBody = "";
46      private String className = null;
47      private String sourceName = null;
48      private ClassLoader loader = null;
49      public static final boolean canRun = true;//comSunToolsJavacMain();
50  
51      static final String JEXL_PACKAGE = "org.apache.commons.jexl3";
52      static final String GEN_PACKAGE = "org.apache.commons.jexl3.generated";
53      static final String GEN_PATH = "/" + GEN_PACKAGE.replace(".", "/");///org/apache/commons/jexl3/generated";
54      static final String GEN_CLASS = GEN_PACKAGE + ".";
55  
56      /**
57       * Check if we can invoke Sun's java compiler.
58       *
59       * @return true if it is possible, false otherwise
60       */
61      private static boolean comSunToolsJavacMain() {
62          try {
63              final Class<?> javac = ClassCreatorTest.class.getClassLoader().loadClass("com.sun.tools.javac.Main");
64              return javac != null;
65          } catch (final Exception xany) {
66              return false;
67          }
68      }
69  
70      public ClassCreator(final JexlEngine theJexl, final File theBase) throws Exception {
71          //jexl = theJexl;
72          base = theBase;
73      }
74  
75      public void clear() {
76          seed = 0;
77          ctorBody = "";
78          packageDir = null;
79          className = null;
80          sourceName = null;
81          packageDir = null;
82          loader = null;
83      }
84  
85      public void setSeed(final int s) {
86          seed = s;
87          className = "foo" + s;
88          sourceName = className + ".java";
89          packageDir = new File(base, seed + GEN_PATH);
90          packageDir.mkdirs();
91          loader = null;
92      }
93  
94      public void setCtorBody(final String arg) {
95          ctorBody = arg;
96      }
97  
98      public String getClassName() {
99          return GEN_CLASS + className;
100     }
101 
102     public Class<?> getClassInstance() throws Exception {
103         return getClassLoader().loadClass(getClassName());
104     }
105 
106     public ClassLoader getClassLoader() throws Exception {
107         if (loader == null) {
108             final URL classpath = (new File(base, Integer.toString(seed))).toURI().toURL();
109             loader = new URLClassLoader(new URL[]{classpath}, getClass().getClassLoader());
110         }
111         return loader;
112     }
113 
114     public Class<?> createClass() throws Exception {
115         return createClass(false);
116     }
117 
118     public Class<?> createClass(final boolean ftor) throws Exception {
119         // generate, compile & validate
120         generate(ftor);
121         final Class<?> clazz = compile();
122         if (clazz == null) {
123             throw new Exception("failed to compile foo" + seed);
124         }
125         if (ftor) {
126             return clazz;
127         }
128         final Object v = validate(clazz);
129         if (v instanceof Integer && (Integer) v == seed) {
130             return clazz;
131         }
132         throw new Exception("failed to validate foo" + seed);
133     }
134 
135     Object newInstance(final Class<?> clazz, final JexlContext ctxt) throws Exception {
136         return clazz.getConstructor(JexlContext.class).newInstance(ctxt);
137     }
138 
139     void generate(final boolean ftor) throws Exception {
140         try (final FileWriter writer = new FileWriter(new File(packageDir, sourceName), false)) {
141             writer.write("package ");
142             writer.write(GEN_PACKAGE);
143             writer.write(";\n");
144             if (ftor) {
145                 writer.write("import " + JEXL_PACKAGE + ".JexlContext;");
146                 writer.write(";\n");
147             }
148             writer.write("public class " + className);
149             writer.write(" {\n");
150             if (ftor) {
151                 writer.write("public " + className + "(JexlContext ctxt) {\n");
152                 writer.write(ctorBody);
153                 writer.write(" }\n");
154             }
155             writer.write("private int value =");
156             writer.write(Integer.toString(seed));
157             writer.write(";\n");
158             writer.write(" public void setValue(int v) {");
159             writer.write(" value = v;");
160             writer.write(" }\n");
161             writer.write(" public int getValue() {");
162             writer.write(" return value;");
163             writer.write(" }\n");
164             writer.write(" }\n");
165             writer.flush();
166         }
167     }
168 
169 //    Class<?> compile0() throws Exception {
170 //        String source = packageDir.getPath() + "/" + sourceName;
171 //        Class<?> javac = getClassLoader().loadClass("com.sun.tools.javac.Main");
172 //        if (javac == null) {
173 //            return null;
174 //        }
175 //        Integer r;
176 //        try {
177 //            r = (Integer) jexl.invokeMethod(javac, "compile", source);
178 //            if (r.intValue() >= 0) {
179 //                return getClassLoader().loadClass(GEN_CLASS + className);
180 //            }
181 //        } catch (JexlException xignore) {
182 //            // ignore
183 //        }
184 //        r = (Integer) jexl.invokeMethod(javac, "compile", (Object) new String[]{source});
185 //        if (r.intValue() >= 0) {
186 //            return getClassLoader().loadClass(GEN_CLASS + className);
187 //        }
188 //        return null;
189 //    }
190 
191     Class<?> compile() throws Exception {
192         final String source = packageDir.getPath() + "/" + sourceName;
193         final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
194         final DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<>();
195         final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null);
196         final Iterable<? extends JavaFileObject> compilationUnits = fileManager
197                 .getJavaFileObjectsFromStrings(Collections.singletonList(source));
198 
199         final List<String> options = new ArrayList<>();
200         options.add("-classpath");
201         // only add hbase classes to classpath. This is a little bit tricky: assume
202         // the classpath is {hbaseSrc}/target/classes.
203         final String currentDir = new File(".").getAbsolutePath();
204         final String classpath = currentDir + File.separator + "target" + File.separator + "classes"
205                 //+ System.getProperty("path.separator") + System.getProperty("java.class.path")
206                 + System.getProperty("path.separator") + System.getProperty("surefire.test.class.path");
207 
208         options.add(classpath);
209         //LOG.debug("Setting classpath to: " + classpath);
210 
211         final JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnosticsCollector, options,
212                 null, compilationUnits);
213         final boolean success = task.call();
214         fileManager.close();
215         if (success) {
216             return getClassLoader().loadClass(GEN_CLASS + className);
217         }
218         final List<Diagnostic<? extends JavaFileObject>> diagnostics = diagnosticsCollector.getDiagnostics();
219         for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnostics) {
220             // read error dertails from the diagnostic object
221             System.out.println(diagnostic.getMessage(null));
222 
223         }
224         return null;
225     }
226 
227     Object validate(final Class<?> clazz) throws Exception {
228         final Class<?>[] params = {};
229         final Object[] paramsObj = {};
230         final Object iClass = clazz.newInstance();
231         final Method thisMethod = clazz.getDeclaredMethod("getValue", params);
232         return thisMethod.invoke(iClass, paramsObj);
233     }
234 
235 }