GroovyClassLoading Mechanism - java

I'm really newbie to groovy scripting but following some tutorial I tried to dynamically load some groovy class within my java code using parseClass() method of GroovyClassLoader.
I wrote some snippet and it worked fine for me. The problem is that I don't clearly understand what groovy engine is doing beyond my view and how those scripts are compiled?
Does a new class gets creted and loaded into jvm? Or does my application uses some cached sources?
Here is the class I'm trying to parse:
private static class MyScript {
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("public class SomeClass\n");
builder.append("{\n");
builder.append("Some code...").append("\n");
builder.append("}\n");
return builder.toString();
}
The I load it with build() as below:
private Class MyGroovyBuilder {
private Script script = new Script();
public String build() throws TemplateCompilationException
//
String groovyText = script.toString();
//
CompilerConfiguration config = new CompilerConfiguration();
//
byte[] bytes;
try
{
bytes = groovyText.getBytes(config.getSourceEncoding());
}
catch (UnsupportedEncodingException e)
{
throw new TemplateCompilationException(e, groovyText);
}
//
InputStream in = new ByteArrayInputStream(bytes);
GroovyCodeSource gcs = new GroovyCodeSource(in, "SomeName", "/groovy/shell");
GroovyClassLoader loader = new
GroovyClassLoader(Thread.currentThread().getContextClassLoader(), config);
Class<?> scriptClass;
try
{
scriptClass = loader.parseClass(gcs, false);
}
catch (CompilationFailedException e)
{
throw new GroovyCompilationException(e, "SomeName", groovyText);
}
catch (ClassFormatError e)
{
throw new GroovyCompilationException(e, "SomeName", groovyText);
}
return scriptClass.getName();
}
Any clarification is greatelly appreciated.
BR.

After loading class it appears in your class loader, and can be accessed like any other class.
There is a simple tutorial [here], that show how to load class from string.
In simplest case, you can load class, and hold it's Class object, using it to create objects dynamically. For field access or method invokation you can rely on Groovy dynamic nature.
There is no "cached source" or smth like that behind the scene and you can forget, from where your class is loaded. You can also cache classes, that are already compiled, and save them somewhere, as described [here]. It will drastically improve performance, if you need to load same class often.
But it will be better, to dig down in topic, because dynamic class loading is advanced Java/Groovy technique, it's whole infrastructure of chained classloaders, so it's better to refer documentation about them.
Links below may be helpful.
http://javarevisited.blogspot.ru/2012/12/how-classloader-works-in-java.html
How to use URLClassLoader to load a *.class file?

Related

how to test public method which is calling private method internally

/*
this method will create the required manifest file in compatible format such that
quicksight can import data from specified s3 bucket
*/
private JSONObject CreateManifestFileJSONObject(JSONObject ManifestFile){
JSONArray URIPrefixArray= new JSONArray();
URIPrefixArray.put(PrefixLocation);
JSONObject URIPrefixJSONObject= new JSONObject();
URIPrefixJSONObject.put("URIPrefixes",URIPrefixArray);
JSONArray FileLocationsArray= new JSONArray();
FileLocationsArray.put(URIPrefixJSONObject);
JSONObject globalUploadSettings= new JSONObject();
globalUploadSettings.put("format","JSON");
ManifestFile.put("globalUploadSettings",globalUploadSettings);
ManifestFile.put("fileLocations",FileLocationsArray);
return(ManifestFile);
}
/*
this method will upload the ManifestFile to same S3 Bucket in which data files is stored
*/
private void UploadManifestFileJSONObjectToS3(JSONObject ManifestFile){
try {
AmazonS3 S3Client = new Utility().SetUpS3Client();
byte[] fileContentBytes = (ManifestFile.toString()).getBytes();
InputStream fileInputStream = new ByteArrayInputStream(fileContentBytes);
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(fileContentBytes.length);
S3Client.putObject(new PutObjectRequest(Bucket_Name, ManifestFileName, fileInputStream, objectMetadata).withCannedAcl(CannedAccessControlList.PublicRead));
}
catch(Exception exception){
exception.printStackTrace();
}
}
public void handler() {
System.out.println("inside the manifest file");
try {
JSONObject ManifestFile = new JSONObject();
ManifestFile = CreateManifestFileJSONObject(ManifestFile);
UploadManifestFileJSONObjectToS3(ManifestFile);
}
catch(Exception exception){
exception.printStackTrace();
}
I want to test handler method but handlor method is calling private methods so I do not know how to write the test class for this code.
i want to write unit test for this class please help
this is the test class I am able to create up to this point but it will not surely mock s3 behaviour
#Test
public void handler() {
ManifestFileHandler manifestFileHandler=new ManifestFileHandler();
manifestFileHandler.handler();
}
You can test private methods with the help of PowerMock provides utilities which uses reflection to do certain things.
check the below example,
https://examples.javacodegeeks.com/core-java/mockito/mockito-mock-private-method-example-with-powermock/
There are two schools of thought on unit testing private functions. The first is that you make them public (or protected or package accessible) and test them as you would a public function. The second is that if they are private they are part of the encapsulated implementation detail and you only need to test them through the public functions.
My personal view is that complicated private functions are often a sign that you are breaking the single responsibility principle and it's likely you should have logic in private functions that should be split into a separate class that can then be tested through its public methods.
With respect to the code you've posted you have a larger problem than how to test the private functions: your class depends on other classes that you don't have control over. You have no way of mocking the behaviour of those classes to test various scenarios or to verify that they have been called correctly. I suspect it is this problem that is really behind your question.
As an example, I would suggest you inject a S3Client into your class rather than create it internally through new Utility().SetUpS3Client(). That way you can mock its behaviour and verify it is called correctly by your code. Attempting to do that with the real version of this class will be challenging.
So using this model, your code might look something like:
public class ManifestFileHandler {
private final S3Client client;
public ManifestFileHandler(S3Client client) {
this.client = client;
}
private void upload(JSONObject manifestFile) {
...
client.putObject(...);
}
public void handleManifest() {
...
upload(manifestFile);
...
}
}
And your test code (using mockito):
#Test
void testManifestUpload() {
S3Client client = mock(S3Client.class);
ManifestFileHandler handler = new ManifestFileHandler(client);
handler.handleManifest();
verify(client).putObject(expectedObject);
}
If you need to capture the argument passed to putObject and assert various aspects of it then that is possible with most mocking tools (including mockito) but is beyond the scope of your question.

Java dynamic loading a class issue

I am trying to write a code that compiles and runs another java class, after it creates it from a String.
My problem is when I run
Class classToLoad = null;
ClassLoader classLoader = Server.class.getClassLoader();
try {
classToLoad = classLoader.loadClass(className);
} catch (Exception e) {
e.printStackTrace();
}
It throws a ClassNotFoundException. My problem isn't about the package, because if I debug the code and place a breakpoint before the "getClassLoader" and I reload the classes, then my code works fine and it sees the class that was recently created earlier in the app.
How can I reload the classes during runtime so the loadClass will work?
Take a look at this tutorial:
ClassLoader Load / Reload Example
... Let's look at a simple
example. Below is an example of a simple ClassLoader subclass. Notice
how it delegates class loading to its parent except for the one class
it is intended to be able to reload. If the loading of this class is
delegated to the parent class loader, it cannot be reloaded later.
Remember, a class can only be loaded once by the same ClassLoader
instance.
As said earlier, this is just an example that serves to show you the
basics of a ClassLoader's behaviour. It is not a production ready
template for your own class loaders. Your own class loaders should
probably not be limited to a single class, but a collection of classes
that you know you will need to reload. In addition, you should
probably not hardcode the class paths either.
public class MyClassLoader extends ClassLoader{
public MyClassLoader(ClassLoader parent) {
super(parent);
}
public Class loadClass(String name) throws ClassNotFoundException {
if(!"reflection.MyObject".equals(name))
return super.loadClass(name);
try {
String url = "file:C:/data/projects/tutorials/web/WEB-INF/" +
"classes/reflection/MyObject.class";
URL myUrl = new URL(url);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass("reflection.MyObject",
classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
Below is an example use of the MyClassLoader.
public static void main(String[] args) throws
ClassNotFoundException,
IllegalAccessException,
InstantiationException {
ClassLoader parentClassLoader = MyClassLoader.class.getClassLoader();
MyClassLoader classLoader = new MyClassLoader(parentClassLoader);
Class myObjectClass = classLoader.loadClass("reflection.MyObject");
AnInterface2 object1 =
(AnInterface2) myObjectClass.newInstance();
MyObjectSuperClass object2 =
(MyObjectSuperClass) myObjectClass.newInstance();
//create new class loader so classes can be reloaded.
classLoader = new MyClassLoader(parentClassLoader);
myObjectClass = classLoader.loadClass("reflection.MyObject");
object1 = (AnInterface2) myObjectClass.newInstance();
object2 = (MyObjectSuperClass) myObjectClass.newInstance();
}
Probably asking: "What is the context in which you are loading the class?" will help answer your question better.
Most standard frameworks like Spring handle loading classes internally and exposing only the methods that those classes provide.
Try Class.forName(String name) to attempt to load the class and return the handle to the class object.
If you want to specifically use your own classloader to load the class, use the overloaded: Class.forName(String name, boolean initialize, ClassLoader loader)
But you will need to ensure that your classloader is able to locate the class to load correctly.
For the classloader you are using, try:
Thread.currentThread().getContextClassLoader()

JarClassLoader - loading jars dynamically - how to?

I have seen many class loader questions, but still was not able to figure why, the error here.
I am writing a program which uses 2 versions of jars. One is needed to get content from older storage, and another to store content in new storage.
Since, I need either of the jar to be loaded at a time, I used JarClassLoader to create a proxy for adding one jar and loading its classes. But I face ClassNotFoundException.
public class HbaseMigrator implements Runnable {
public void run() {
JarClassLoader jcl = new JarClassLoader();
jcl.add("hadoop-0.13.0-core-modified-1.jar");
Object obj1 = JclObjectFactory.getInstance().create(jcl, "UserMigThreadImpl", toProcessQueue,threadName, latch,DBUtil,lock);
MigThread mig = JclUtils.cast(obj1, MigThread.class, jcl);
Thread.currentThread().setContextClassLoader(jcl);
try {
Method method = MigThread.class.getMethod("callthis", new Class[]{});
method.invoke(mig, new Object[]{});
// mig.callthis();
} catch( Exception e) {
e.printStackTrace();
} catch(Error er) {
er.printStackTrace();
}
}
}
Method called is:
public void callthis() {
DFSUtil = new DFSAccessAPIImpl();
.........
}
This class instantiation internally uses hadoop modified jar, which is not picked up from my classloader and it throws ClassNotFoundException.
What is that I am doing wrong ?
JarClassLoader used here is jcloader :
org.xeustechnologies.jcl.JarClassLoader
I experienced this problem with loading plugins to my application, so I decided to try to load all .class files from all jars in path. Maybe this code snipped from my app will help you.
https://bitbucket.org/rsohlich/plagdetector/src/432b52f252ff7647221b7e91b08731bd9cbe2a70/PlagDetectorSpring/src/main/java/cz/sohlich/app/service/impl/PluginHolderImpl.java

How to reload previously loaded classes that changed during runtime?

I'm writing an application that needs to reload a previously loaded class during runtime. The reason is that the class is auto-generated during runtime, and the new implementation changes the way the app works. I generate only one object of the said class, and I stripped it from all dependencies but an interface that defines constant values. There's no problem reseting the values of any or all of the members when reloading. I know exactly when it changes and I can control it. The only problem I have is the reload itself.
From what I read, I should use a ClassLoader. I tried to do so, but I can't make it work.
I tried the following:
Getting the current ClassLoader (myClassObject.getClass().getClassLoader()) and using it to reload the class - Doesn't work. It probably keeps loading the old implementation.
Generating my own (AKA copy-paste from SO with modifications) - Doesn't work because the ClassLoader I generate is different than the one that generated the class (Exception: myClass cannot be casted to myClass).
Creating a constructor that sets the ClassLoader of the superclass doesn't seem to have any effect.
Using my new ClassLoader to generate the class that has myClassObject as a member solved the ClassLoader mismatch for myClassObject, but created a new mismatch one level up. I used getClassLoader() everytime and I see they don't match.
I tried adding -Djava.system.class.loader=com.test.Reoader com.test.myMainClass to make it my default reloader, but I get an error from the compiler.
Google keeps pointing me back to the same stuff I read already.
EDIT: I tried creating an interface and reload the class implementing it. That didn't solve it either.
I know I should override the default ClassLoader, but nothing I do seems to succeed at that.
My ClassLoader:
public class Reloader extends ClassLoader {
public Reloader(){
super(Reloader.class.getClassLoader());
}
#Override
public Class<?> loadClass(String s) {
return findClass(s);
}
#Override
public Class<?> findClass(String s) {
try {
byte[] bytes = loadClassData(s);
return defineClass(s, bytes, 0, bytes.length);
} catch (IOException ioe) {
try {
return super.loadClass(s);
} catch (ClassNotFoundException ignore) {}
ioe.printStackTrace(java.lang.System.out);
return null;
}
}
private byte[] loadClassData(String className) throws IOException {
File f = new File("out\\production\\ManoCPU\\" + className.replaceAll("\\.", "/") + ".class");
int size = (int) f.length();
byte buff[] = new byte[size];
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
dis.readFully(buff);
dis.close();
return buff;
}
}
Thanks very much to anyone that can help.
You can only load a class once (per instance of a classloader). That means you have to throw away the classloader you have loaded your class with and instantiate a new one for your updated version of the class.
When dealing with multiple class loaders also have in mind that if you load the same class with several classloaders they are NOT recognized as being the same class.

java constructor to assign the entire class not just fields

my system is both jibx and a legacy xml app and i want to build a constructor that can take a string of xml and unmarshal it into its own class. like this:
public ActiveBankTO(String xmlIn)
{
try
{
ByteArrayInputStream bin = new ByteArrayInputStream(xmlIn.getBytes());
IBindingFactory bfact;
bfact = BindingDirectory.getFactory(ActiveBankTO.class);
IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
this = (ActiveBankTO) uctx.unmarshalDocument(bin, null);
} catch (JiBXException e)
{
e.printStackTrace();
}
}
but obviously i cant assign "this" as a variable. is there a way to make this work? i realize i can put this into a static method that can be used, or a few other tricks to make it work, but this is something that has come up on several projects in various forms and i was wondering if this particular method is possible.
No, it's not possible. The static method solution is the best idea.
public static ActiveBankTO parseActiveBankTO(String xmlIn) {
ActiveBankTO newTO = null;
try {
ByteArrayInputStream bin = new ByteArrayInputStream(xmlIn.getBytes());
IBindingFactory bfact;
bfact = BindingDirectory.getFactory(ActiveBankTO.class);
IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
newTO = (ActiveBankTO) uctx.unmarshalDocument(bin, null);
} catch (JiBXException e) {
e.printStackTrace();
}
return newTO;
}
No. ti's not possible in the constructor. A static factory method is the only real way (you can't even cheat like this in bytecode).

Categories

Resources