Commit 88f2db37 authored by Emmanuel Bourg's avatar Emmanuel Bourg

Merge tag 'upstream/2.2.0'

Upstream version 2.2.0
parents 5669d5e6 bd1ff451
*.class
# Package Files #
*.jar
*.war
*.ear
*.iml
compiler/target
This diff is collapsed.
Java-Runtime-Compiler
=====================
This takes a String, compiles it and loads it returning you a class from what you built.
By default it uses the current ClassLoader. It supports nested classes, but otherwise builds one class at a time.
## On maven central
You can include in your project with
<dependency>
<groupId>net.openhft</groupId>
<artifactId>compiler</artifactId>
<version>2.1.2</version>
</dependency>
## Simple example
You needa CachedCompiler and access to you JDK's tools.jar.
private final CachedCompiler cc = new CachedCompiler(null, null);
// dynamically you can call
Class aClass = cc.loadFromJava(className, javaCode);
KnownInterface o = (KnownInterface) aClass.newInstance();
I suggest making you class implement a "KnownInterface" as this will allow you to call/manipulate instances of you generated class.
Another more hacky way is to use this to override a class, provided it hasn't been loaded already.
This means you can redefine an existing class and provided the methods and fields used match,
you have compiler redefine a class and code already compiled to use the class will still work.
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013 Peter Lawrey
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>net.openhft</groupId>
<artifactId>java-parent-pom</artifactId>
<version>1.0.1</version>
<relativePath />
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>compiler</artifactId>
<version>2.2.0</version>
<packaging>bundle</packaging>
<name>OpenHFT/Java-Runtime-Compiler</name>
<description>Java Runtime Compiler library.</description>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>net.openhft</groupId>
<artifactId>third-party-bom</artifactId>
<type>pom</type>
<version>3.4.0</version>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.java</groupId>
<artifactId>tools</artifactId>
</dependency>
<dependency>
<groupId>org.kohsuke.jetbrains</groupId>
<artifactId>annotations</artifactId>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<!-- todo remove once next parent pom is adopted -->
<repositories>
<repository>
<id>sonatype-nexus-staging</id>
<url>https://oss.sonatype.org/content/repositories/staging</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<scm>
<url>scm:git:git@github.com:OpenHFT/Java-Runtime-Compiler.git</url>
<connection>scm:git:git@github.com:OpenHFT/Java-Runtime-Compiler.git</connection>
<developerConnection>scm:git:git@github.com:OpenHFT/Java-Runtime-Compiler.git
</developerConnection>
<tag>compiler-2.2.0</tag>
</scm>
</project>
\ No newline at end of file
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.compiler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.LoggerFactory;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import static net.openhft.compiler.CompilerUtils.writeBytes;
import static net.openhft.compiler.CompilerUtils.writeText;
@SuppressWarnings("StaticNonFinalField")
public class CachedCompiler {
private static final Map<ClassLoader, Map<String, Class>> loadedClassesMap = new WeakHashMap<ClassLoader, Map<String, Class>>();
@Nullable
private final File sourceDir;
@Nullable
private final File classDir;
private final List<JavaFileObject> javaFileObjects = new ArrayList<JavaFileObject>();
public CachedCompiler(@Nullable File sourceDir, @Nullable File classDir) {
this.sourceDir = sourceDir;
this.classDir = classDir;
}
public static void close() {
try {
CompilerUtils.s_fileManager.close();
} catch (IOException e) {
throw new AssertionError(e);
}
}
public Class loadFromJava(@NotNull String className, @NotNull String javaCode) throws ClassNotFoundException {
return loadFromJava(getClass().getClassLoader(), className, javaCode);
}
@NotNull
Map<String, byte[]> compileFromJava(@NotNull String className, @NotNull String javaCode) {
Iterable<? extends JavaFileObject> compilationUnits;
if (sourceDir != null) {
String filename = className.replaceAll("\\.", '\\' + File.separator) + ".java";
File file = new File(sourceDir, filename);
writeText(file, javaCode);
compilationUnits = CompilerUtils.s_standardJavaFileManager.getJavaFileObjects(file);
} else {
javaFileObjects.add(new JavaSourceFromString(className, javaCode));
compilationUnits = javaFileObjects;
}
// reuse the same file manager to allow caching of jar files
CompilerUtils.s_compiler.getTask(null, CompilerUtils.s_fileManager, null, null, null, compilationUnits).call();
return CompilerUtils.s_fileManager.getAllBuffers();
}
public Class loadFromJava(@NotNull ClassLoader classLoader, @NotNull String className, @NotNull String javaCode) throws ClassNotFoundException {
Class clazz = null;
Map<String, Class> loadedClasses;
synchronized (loadedClassesMap) {
loadedClasses = loadedClassesMap.get(classLoader);
if (loadedClasses == null)
loadedClassesMap.put(classLoader, loadedClasses = new LinkedHashMap<String, Class>());
else
clazz = loadedClasses.get(className);
}
if (clazz != null)
return clazz;
for (Map.Entry<String, byte[]> entry : compileFromJava(className, javaCode).entrySet()) {
String className2 = entry.getKey();
synchronized (loadedClassesMap) {
if (loadedClasses.containsKey(className2))
continue;
}
byte[] bytes = entry.getValue();
if (classDir != null) {
String filename = className2.replaceAll("\\.", '\\' + File.separator) + ".class";
boolean changed = writeBytes(new File(classDir, filename), bytes);
if (changed) {
LoggerFactory.getLogger(CachedCompiler.class).info("Updated {} in {}", className2, classDir);
}
}
Class clazz2 = CompilerUtils.defineClass(classLoader, className2, bytes);
synchronized (loadedClassesMap) {
loadedClasses.put(className2, clazz2);
}
}
// CompilerUtils.s_fileManager.clearBuffers();
synchronized (loadedClassesMap) {
loadedClasses.put(className, clazz = classLoader.loadClass(className));
}
return clazz;
}
}
This diff is collapsed.
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.compiler;
import org.jetbrains.annotations.NotNull;
import javax.tools.SimpleJavaFileObject;
import java.net.URI;
/* A file object used to represent source coming from a string.
*/
class JavaSourceFromString extends SimpleJavaFileObject {
/**
* The source code of this "file".
*/
private final String code;
/**
* Constructs a new JavaSourceFromString.
*
* @param name the name of the compilation unit represented by this file object
* @param code the source code for the compilation unit represented by this file object
*/
JavaSourceFromString(@NotNull String name, String code) {
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension),
Kind.SOURCE);
this.code = code;
}
@SuppressWarnings("RefusedBequest")
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.compiler;
import org.jetbrains.annotations.NotNull;
import javax.tools.*;
import javax.tools.JavaFileObject.Kind;
import java.io.*;
import java.net.URI;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@SuppressWarnings("RefusedBequest")
class MyJavaFileManager implements JavaFileManager {
private final StandardJavaFileManager fileManager;
private final Map<String, ByteArrayOutputStream> buffers = new LinkedHashMap<String, ByteArrayOutputStream>();
MyJavaFileManager(StandardJavaFileManager fileManager) {
this.fileManager = fileManager;
}
public ClassLoader getClassLoader(Location location) {
// System.out.println("getClassLoader(" + location + ')');
return fileManager.getClassLoader(location);
}
public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
// System.out.println("list(" + location + ',' + packageName + ',' + kinds + ',' + recurse + ')');
return fileManager.list(location, packageName, kinds, recurse);
}
public String inferBinaryName(Location location, JavaFileObject file) {
// System.out.println("inferBinaryName(location=" + location + ", file=" + file + ')');
return fileManager.inferBinaryName(location, file);
}
public boolean isSameFile(FileObject a, FileObject b) {
return fileManager.isSameFile(a, b);
}
public boolean handleOption(String current, Iterator<String> remaining) {
return fileManager.handleOption(current, remaining);
}
public boolean hasLocation(Location location) {
// System.out.println("hasLocation(" + location + ')');
return fileManager.hasLocation(location);
}
public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
// System.out.println("getJavaFileForInput(location=" + location + ", className=" + className + ", kind=" + kind + ')');
if (location == StandardLocation.CLASS_OUTPUT && buffers.containsKey(className) && kind == Kind.CLASS) {
final byte[] bytes = buffers.get(className).toByteArray();
return new SimpleJavaFileObject(URI.create(className), kind) {
@NotNull
public InputStream openInputStream() {
return new ByteArrayInputStream(bytes);
}
};
}
return fileManager.getJavaFileForInput(location, className, kind);
}
@NotNull
public JavaFileObject getJavaFileForOutput(Location location, final String className, Kind kind, FileObject sibling) throws IOException {
return new SimpleJavaFileObject(URI.create(className), kind) {
@NotNull
public OutputStream openOutputStream() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
buffers.put(className, baos);
return baos;
}
};
}
public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
return fileManager.getFileForInput(location, packageName, relativeName);
}
public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
return fileManager.getFileForOutput(location, packageName, relativeName, sibling);
}
public void flush() throws IOException {
// for (Map.Entry<String, ByteArrayOutputStream> entry : buffers.entrySet())
// System.out.println("Compiled " + entry.getKey() + ", size=" + entry.getValue().toByteArray().length);
}
public void close() throws IOException {
// System.out.println("close()");
fileManager.close();
}
public int isSupportedOption(String option) {
return fileManager.isSupportedOption(option);
}
public void clearBuffers() {
buffers.clear();
}
@NotNull
public Map<String, byte[]> getAllBuffers() {
Map<String, byte[]> ret = new LinkedHashMap<String, byte[]>(buffers.size() * 2);
for (Map.Entry<String, ByteArrayOutputStream> entry : buffers.entrySet()) {
ret.put(entry.getKey(), entry.getValue().toByteArray());
}
return ret;
}
}
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eg;
import eg.components.BarImpl;
import eg.components.Foo;
import eg.components.TeeImpl;
public class FooBarTee {
public final String name;
public final TeeImpl tee;
public final BarImpl bar;
public final BarImpl copy;
public final Foo foo;
public FooBarTee(String name) {
// when viewing this file, ensure it is synchronised with the copy on disk.
System.out.println("generated test Tue Nov 06 20:54:52 GMT 2007");
this.name = name;
tee = new TeeImpl("test");
bar = new BarImpl(tee, 55);
copy = new BarImpl(tee, 555);
// you should see the current date here after synchronisation.
foo = new Foo(bar, copy, "generated test Tue Nov 06 20:54:52 GMT 2007", 5);
}
public void start() {
}
public void stop() {
}
public void close() {
stop();
}
}
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eg.components;
interface Bar {
Tee getTee();
int getI();
}
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eg.components;
public class BarImpl implements Bar {
final int i;
final Tee tee;
public BarImpl(Tee tee, int i) {
this.tee = tee;
this.i = i;
}
public Tee getTee() {
return tee;
}
public int getI() {
return i;
}
}
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eg.components;
@SuppressWarnings({"QuestionableName"})
public class Foo {
public final Bar bar;
public final Bar copy;
public final String s;
public final int i;
public Foo(Bar bar, Bar copy, String s, int i) {
this.bar = bar;
this.copy = copy;
this.s = s;
this.i = i;
}
}
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eg.components;
interface Tee {
String getS();
}
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eg.components;
public class TeeImpl implements Tee {
final String s;
public TeeImpl(String s) {
this.s = s;
}
public String getS() {
return s;
}
}
/*
* Copyright 2013 Peter Lawrey
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.compiler;
import com.sun.tools.internal.xjc.util.NullStream;
import eg.FooBarTee;
import eg.components.Foo;
import junit.framework.TestCase;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
public class CompilerTest extends TestCase {
private static final String EG_FOO_BAR_TEE = "eg.FooBarTee";
static final File parent;
static {
File parent2 = new File("essence-file");
if (parent2.exists()) {
parent = parent2;
} else {
parent = new File(".");
}
}
public static void test_compiler() throws ClassNotFoundException {
// CompilerUtils.setDebug(true);
// added so the test passes in Maven.
CompilerUtils.addClassPath("target/test-classes");
// ClassLoader loader = CompilerTest.class.getClassLoader();