Over a million developers have joined DZone.

RelProxy: A Hot Class Reloader and Scripting for Java and Groovy

DZone's Guide to

RelProxy: A Hot Class Reloader and Scripting for Java and Groovy

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

What is RelProxy?

RelProxy has three principal features:

1) A class reloader in runtime from source code in Groovy or Java. Similar to JRebel but not so sophisticated... and cheaper.

2) A scripting environment for Java as a scripting language, including "shell scripts" coded in pure Java.

3) JSR-223 Java Scripting API  support for "the scripting language named Java."

RelProxy was born to provide automatic class reload to ItsNat, a Java based web framework, for code in Groovy, it became a general purpose tool including Java support.

1) A class reloader in runtime from source code in Groovy or Java.

Let's go first with Groovy.
You know Groovy is a dynamic language that supports built-in hot class reload. Maybe you don't know Groovy is a bytecode based language. In this case, Groovy engine compiles Groovy classes on demand.
Think we have several Groovy classes with object instances going to be registered into some Java library as listeners implementing some interface. The Groovy object implementing the Java interface is in memory a "normal" Java object (in spite it was created by Groovy), then this object is received by the Java library when registering. In spite of Groovy can reload classes, there is no way to reload the Groovy object registered.

This is when RelProxy makes its job through a utility named GProxy. With GProxy, we create a  java.lang.reflect.Proxy and pass this proxy instead of the original Groovy object. When a method is called to java.lang.reflect.Proxy, the method call is intercepted by GProxy an through Groovy engine RelProxy checks whether some class has changed, when changed the source code of the class of the original object or other dependent class, the original object is re-created based on the new class, the same dependent classes.

Only the bridge object and related/dependent code is reloaded, maybe not everything, but it may avoid many re-deploys for instance in web applications.
The same is applied to Java code with the utility JProxy, in this case is RelProxy which timely checks source code changes, compiling changed classes on the fly and reloading all classes with a new ClassLoader.
The following is a Java example based on ItsNat:
FalseDB db = new FalseDB();
ItsNatServletRequestListener listener = 
   JProxy.create(new example.javaex.JProxyExampleLoadListener(db), 
The object JProxyExampleLoadListener is reloadable, the same its dependencies but FalseDB because is created "outside of the proxy environment".
In case of an ItsNat web project, the class  JProxyExampleLoadListener (implementing  ItsNatServletRequestListener) is where we code view logic, whether this class and related/dependent classes can be hot reloaded and also pure HTML templates are reloaded by ItsNat when changed, we have a development environment (also in production?) where re-deployments are drastically reduced because view design and view logic are hot reloadable.
The Groovy example is exactly the same but in Groovy (Groovy is an alternative language for ItsNat projects).

2) Scripting environment for Java as "scripting language", "shell scripts" based on pure Java
Thanks to jproxysh utility you can create an archive, for instance with name example_java_shell, with this content:
#!/usr/bin/env jproxysh

String msg = args[0] + args[1];
System.out.println("example_java_shell 1 ");
and be executed with no previous compilation.
It can be a complete Java class:
#!/usr/bin/env jproxysh

import example.javashellex.JProxyShellExample;
public class example_java_shell_complete_class
    public static void main(String[] args)
        String msg = args[0] + args[1];
        System.out.println("example_java_shell_complete_class 1 ");
JProxyShellExample can also be a Java class with just the source code.
Also it could be a normal Java class, again with no need of compilation (JavaSE projects with no compilation):
jproxysh example_normal_class.java "HELLO " "WORLD!"
We can execute a code snippet:
jproxysh -c 'System.out.print("This code snippet says: ");' \
                   'System.out.println("Hello World!!");'
Or a complete class:
jproxysh -c 'public class _jproxyMainClass_ { '  \
            ' public static void main(String[] args) { ' \
            '    System.out.print("This code snippet says: ");' \
            '    System.out.println("Hello World!!");' \
            '  }' \
Finally invoking jproxysh with no param:
Starts a simple interactive shell, where we can write and execute Java code progressively.
Must be noted RelProxy does NOT create a new language similar to Java, such as BeanShell does, RelProxy just compiles on the fly in memory (optionally can save .class files).

3) JSR-223 Java Scripting API  support for the scripting language "Java"
Java is just another "script" language supported:
JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(jpConfig);

ScriptEngineManager manager = new ScriptEngineManager();
manager.registerEngineName("Java", factory);
manager.getBindings().put("msg","HELLO GLOBAL WORLD!");

ScriptEngine engine = manager.getEngineByName("Java");
Bindings bindings = engine.createBindings();
bindings.put("msg","HELLO SCOPE WORLD!");

StringBuilder code = new StringBuilder();
code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n");
code.append( " String msg = (String)bindings.get(\"msg\"); \n");
code.append( " System.out.println(msg); \n");
code.append( " bindings = context.getBindings(javax.script.ScriptContext.GLOBAL_SCOPE); \n");
code.append( " msg = (String)bindings.get(\"msg\"); \n");
code.append( " System.out.println(msg); \n");
code.append( " example.javashellex.JProxyShellExample.exec(engine); \n");
code.append( " return \"SUCCESS\";");

String result = (String)engine.eval( code.toString() , bindings);
System.out.println("RETURNED: " + result);

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat


Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}