Skip to content

hypercube-software/dynamic-compilation-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dynamic compilation in Java 11

Table of content

1 Dynamic compilation in Java 11
    1.1 Introduction
    1.2 Security
    1.3 Few notes about JDK 9
2 This project
3 Example

1.1 Introduction

Compiling a Java file on the fly is not so complicated. First, you have to instantiate a new compiler:

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

Then you need to create a compilation task providing a file manager:

CompilationTask task = compiler.getTask(...,fileManager,...);

The file manager is responsible to provide the source code of the script and other pre-compiled classes. All those files implements the interface JavaFileObject

Then you run the compilation task:

boolean success = task.call();

And voila! The compiled class will be available in the file manager.

Class<?> compiledClass = fileManager.getClassLoader(null).loadClass(className);

1.2 Security

Creating a plugin system could cause serious security holes if you run a malicious script. Fortunately the dynamic compilation API in Java allow you to control which class can be used by the script at compile time. This is handled by ForwardingJavaFileManager::list, so you have to inherit from this class to make your own file manager.

It's also a good idea to limit the scope of your script to a method class instead of an entire Java file. After all, your plugin will have to implement a known method to be usable. In this project all plugin will inherit from the abstract class Plugin and the abstract method will be execute

public abstract class Plugin { /**  * This method is implemented in the script  * @throws Exception  */ abstract public void execute() throws Exception; }

1.3 Few notes about JDK 9

Since JDK 9, you have now modules. So pay attention to the class javax.tools.StandardLocation, because there are more possible locations now and you must handle them in the file manager:

JDK 8:

Enum Constant Description
CLASS_OUTPUT Location of new class files.
CLASS_PATH Location to search for user class files.
NATIVE_HEADER_OUTPUT Location of new native header files.
PLATFORM_CLASS_PATH Location to search for platform classes.
SOURCE_OUTPUT Location of new source files.
SOURCE_PATH Location to search for existing source files.

JDK 9:

Enum Constant Description
CLASS_OUTPUT Location of new class files.
CLASS_PATH Location to search for user class files.
MODULE_PATH Location to search for precompiled user modules.
MODULE_SOURCE_PATH Location to search for the source code of modules.
NATIVE_HEADER_OUTPUT Location of new native header files.
PATCH_MODULE_PATH Location to search for module patches.
PLATFORM_CLASS_PATH Location to search for platform classes.
SOURCE_OUTPUT Location of new source files.
SOURCE_PATH Location to search for existing source files.
SYSTEM_MODULES Location to search for system modules.
UPGRADE_MODULE_PATH Location to search for upgradeable system modules.

2 This project

I made the things as simple as possible:

  • com.hypercube.scripts.FileManager: the most important class used by the compiler
  • com.hypercube.scripts.PluginBinary: a class used by the compiler to store the compiled .class, we store in memory.
  • com.hypercube.scripts.PluginScript: a class used by the compiler to get the source code
  • com.hypercube.scripts.PrecompiledJavaFile: a kind of wrapper used by the compiler to get some .class. We use it for Plugin.class
  • com.hypercube.scripts.model.Plugin: the base class of any plugin, provides a logger to the plugin.

3 Example

Script located in the file scripts/MyScript.txt:

package com.hypercube.scripts.model; public class MyScript extends Plugin { @Override public void execute() throws Exception { logger.info("Hello world !");	} }

I made some logs to make you understand what's going on, especially what the compiler ask to the file manager:

[2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: Requires ClassLoader for location CLASS_PATH [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.base] package: java.lang kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION FILTERED: CLASS_PATH package: com.hypercube.scripts kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION FILTERED: CLASS_PATH package: com.hypercube kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION FILTERED: CLASS_PATH package: com kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.base] package: java.lang.annotation kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.base] package: java.io kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.logging] package: java.util.logging kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.base] package: java.util.function kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.base] package: java.lang.invoke kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:22:56 PM] com.hypercube.scripts.FileManager INFO: Requires ClassLoader for location null [2020-09-08 03:22:56 PM] com.hypercube.DynamicCompilerExample INFO: Script successfully compiled [2020-09-08 03:22:56 PM] com.hypercube.scripts.model.MyScript INFO: Hello world ! 

If you remove "java.util.logging" or "java.util.function" from FileManager::acceptedPackages you will have:

[2020-09-08 03:25:12 PM] com.hypercube.scripts.FileManager INFO: Requires ClassLoader for location CLASS_PATH [2020-09-08 03:25:12 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.base] package: java.lang kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:25:12 PM] com.hypercube.scripts.FileManager INFO: LOCATION FILTERED: CLASS_PATH package: com.hypercube.scripts kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:25:12 PM] com.hypercube.scripts.FileManager INFO: LOCATION FILTERED: CLASS_PATH package: com.hypercube kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:25:12 PM] com.hypercube.scripts.FileManager INFO: LOCATION FILTERED: CLASS_PATH package: com kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:25:12 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.base] package: java.lang.annotation kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:25:12 PM] com.hypercube.scripts.FileManager INFO: LOCATION INCLUDED: SYSTEM_MODULES[java.base] package: java.io kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:25:12 PM] com.hypercube.scripts.FileManager INFO: LOCATION FILTERED: SYSTEM_MODULES[java.logging] package: java.util.logging kinds: SOURCE,CLASS,HTML,OTHER [2020-09-08 03:25:12 PM] com.hypercube.DynamicCompilerExample SEVERE: Compilation error: [2020-09-08 03:25:12 PM] com.hypercube.DynamicCompilerExample SEVERE: MyScript:7: error: cannot access java.util.logging.Logger	logger.info("Hello world !"); ^ class file for java.util.logging.Logger not found 

About

Make your own plugin system in JDK 11

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages