How to cast an object in class loaded in classloader - groovy

I am new to Stack Overflow. I have created a Groovy class loader's object in which I have loaded all the classes required by my script. I have task of serializing and deserializing an object created of one of the class that is loaded in class loader. Problem is while deserializing I am not able to cast the object in the class as class in loaded in class loader. I don't know how to cast an object in a class that is loaded in a class loader. Can some one help me in this. The ????? in below snippet is a class that is loaded in class loader but how shall I achieve this.
Object o = null
new ByteArrayInputStream(bytes).withObjectInputStream(getClass().classLoader){ gin ->
o = (?????)gin.readObject() }
Thanks in advance!!!

I managed to solve your problem. Let me show you a working example
Some Employee class, that I use
public class Employee implements java.io.Serializable
{
public String name;
public String address;
}
Then the Main class
class Main {
public static void main(String[] args) {
Employee e1 = new Employee()
e1.name = 'John'
e1.address = 'Main Street'
byte[] bytes = []
ByteArrayOutputStream stream = new ByteArrayOutputStream()
ObjectOutputStream out = new ObjectOutputStream(stream)
out.writeObject(e1)
bytes = stream.toByteArray()
out.close()
stream.close()
Object o = null
new ByteArrayInputStream(bytes).withObjectInputStream(Main.getClassLoader()){ gin ->
o = gin.readObject()
}
print o instanceof Employee
println 'Deserialized Employee...'
println 'Name: ' + o.name
println 'Address: ' + o.address
}
}
Instead of doing getClass().classLoader, which was throwing a java.lang.ClassNotFoundException, I am doing Main.getClassLoader(). This classloader is able to find my Employee class.
Moreover, you don't really need to cast the object, it is groovy, its dynamic, so you get the name and address fields at runtime.
But, you can always check the type of the object and then cast it:
print o instanceof Employee
this will return true

Related

Passing HashMap as parameter into a subclass constructor

I've been struggling with trying to figure out the problem and fixing the error when I tried to pass HashMap into a constructor. My scenario is:
I've a Student class:
public class Student {
String name;
String major;
String level;
public Student (String name, String major, String level) {
this.name = name;
this.major = major;
this.level = level;
}
}
I've another class, called TA_Manager that is a subclass of Student. This TA_Manager class uses HashMap to collect the students (who are TA) from the Student class:
import java.util.HashMap;
public class TA_Manager extends Student {
HashMap<String, Student> TA;
public TA_Manager(HashMap<String, Student> TA) {
this.TA = TA;
}
}
In the main class, I've created three student objects and I put two of the students into the HashMap (they are TAs). Then I create a TA_Manager object and pass the HashMap into the TA_Manager class:
import java.util.HashMap;
public class Test {
public static void main(String[] args) {
Student s1 = new Student("A", "CS", "Junior");
Student s2 = new Student("B", "IS", "Senior");
Student s3 = new Student("C", "CE", "Senior");
HashMap<String, Student> TA = new HashMap<String, Student>();
TA.put("TA1", s1);
TA.put("TA2", s2);
TA_Manager tamgr = new TA_Manager (TA);
}
}
When I run the main class, it returns error:
TA_Manager.java:6: error: constructor Student in class Student cannot be applied to given types;
public TA_Manager(HashMap<String, Student> TA) {
^
required: String,String,String
found: no arguments
I actually have searched this HashMap problem and I followed the solution given on how to pass the HashMap into the constructor:
Pass a HashMap as parameter in Java
and also from this link on how to pass a class as hashmap value:
Can HashMap contain custom class for key/value?
But I still get the error message. I don't know how to fix this error. Can anyone bring some light into this. Really appreciated.
The error is caused because java is trying to call the no-arg constructor of your Student class, but you only have a three argument public constructor.
The simplest solution is to create an empty public constructor for your student.
public Student(){
//do nothing and leave values as null.
}
This is not a very practical solution. The problem is a bit conceptual. Your TA class is a Student, but you don't give it a name major or level.
The next way to manage this would be to call the current constructor with some values.
public TA_Manager(HashMap<String, Student> TA) {
super( null, null, null);
this.TA = TA;
}
Now java knows to use the public constructor instead of the no-arg one. I left the values as null because I don't know what default values you would have. This is practical when there are useful default values that you wouldn't need to include during construction.
Personally, I would expect the TA to be a full student AND have a hashmap.
public TA_Manager (String name, String major, String level) {
super(name, major, level);
this.TA = new HashMap<>();
}
In this case you would create the manager, then add all of the students afterwards. It has the advantage that your TA_Manager is a fully formed student though.

Groovy class factory pattern gives weird error

I am new to groovy and I am trying with some groovy code. I have this scenario. I have the following modules
package com.utils
abstract class Base {
static String data = ''
}
package com.utils
class A extends Base {
static String data = 'dummy'
}
package com.utils
class B extends Base {
static String data = 'dummy'
}
package com.utils
class ShapeFactory {
static Map <String,Object> shapes = [
"a": A,
"b": B
]
static Object get_shapes(String shape) {
return shapes.get(shape);
}
}
And in the main file I am using
and in the main file and here is where it fails with weird error. I couldn't identify the reason, I would appreciate any help.
import com.utils.ShapeFactory
def shapeA = ShapeFactory.get_shapes('a')
shapeA.data // here it fails with the below error
hudson.remoting.ProxyException: org.codehaus.groovy.runtime.typehandling.GroovyCastException:
Cannot cast object '[]' with class 'java.util.ArrayList' to class 'java.util.Map'
due to: groovy.lang.GroovyRuntimeException:
Could not find matching constructor for: java.util.Map()
Any help will be appreciated, Thanks
Apologies, I found the issue, In the Base class I had a Map variable as well which was initialized as an array
abstract class Base {
static String data = ''
static Map mapper = [] // This has to be [:]
}

Does Groovy "as" operator create a subclass at run time for User defined classes?

In Groovy When I write the below code in a groovy script.
class Emp {
public String getId() {
return "12345";
}
}
def coercedInstance = [
getId: {
"99999"
}
] as Emp
println new Emp().getId()
println coercedInstance .getId()
Using the as Operator here, am I creating a sub class of the actual Emp class at runtime and providing the method body at run time?
I have seen other stack overflow articles and i have learnt that Groovy uses DefaultGroovyMethods.java & DefaultTypeTransformation.java to do the coercion. But could not figure out if it was subclassing or not.
Yes, an as operator creates an object which type is a subclass of the target class. Using DefaultGroovyMethods.asType(Map map, Class clazz) generates (in a memory) a proxy class that extends given base class.
class Emp {
public String getId() {
return "12345";
}
}
def coercedInstance = [
getId: {
"99999"
}
] as Emp
assert (coercedInstance instanceof Emp)
assert (coercedInstance.class != Emp)
assert (Emp.isAssignableFrom(coercedInstance.class))
println coercedInstance.dump() // <Emp1_groovyProxy#229c6181 $closures$delegate$map=[getId:coercion$_run_closure1#7bd4937b]>
What happens in your case specifically is the following:
The asType method goes to line 11816 to execute ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(map, clazz);
In the next step, ProxyGeneratorAdapter object gets created.
In the last step, adapter.proxy(map,constructorArgs) gets called to return a newly generated class that is a proxy of the base class.

Adding a field with Groovy AST doesn't generate getter and setter

So I'm writing an AST Transformation that adds a field with the #Delegate annotation to a class
#SimpleAST
class PersonBuilder{
}
Should produce
class PersonBuilder{
#Delegate
Person target = new Person
}
My Interface:
#Retention(RetentionPolicy.RUNTIME)
#Target([ElementType.TYPE])
#GroovyASTTransformationClass("poc.SimpleASTTransformation")
public #interface SimpleAST {
/**
* A class for which builder methods should be created. It will be an error to leave
* this attribute with its default value for some strategies.
*/
Class value()
}
My transformation:
#CompileStatic
#GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class SimpleASTTransformation implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit source) {
ClassNode classNode = (ClassNode) astNodes[1]
if (!astNodes) return
if (!astNodes[0]) return
if (!astNodes[1]) return
if (!(astNodes[0] instanceof AnnotationNode)) return
if (!(astNodes[1] instanceof ClassNode)) return
println "Running AST Transformation for ${classNode.getNameWithoutPackage()}..."
AnnotationNode annotationNode = (AnnotationNode) astNodes[0]
ClassExpression classExpression = (ClassExpression) annotationNode.getMember("value")
String packageName = classNode.getPackageName()
String builderClassNameWithoutPackage = classNode.getNameWithoutPackage()
String originalClassNameWithPackage = classExpression.getText()
originalClassNameWithPackage = Validate.checkOriginalClassName(originalClassNameWithPackage)
Class<?> originalClass = Class.forName(originalClassNameWithPackage)
ClassNode originalClassNode = new ClassNode(originalClass)
String originalClassNameWithoutPackage = originalClassNode.getNameWithoutPackage()
println "Generating methods for $originalClassNameWithoutPackage..."
generateTargetField(classNode,originalClass)
println "Transformation applied!"
}
static void generateTargetField(ClassNode classNode, Class originalClass){
ClassNode originalClassNode = new ClassNode(originalClass)
ConstructorCallExpression constructorCallExpression = new ConstructorCallExpression(originalClassNode,new ArgumentListExpression())
FieldNode fieldNode = new FieldNode("target",
2,
originalClassNode,
classNode,
constructorCallExpression)
ArrayList<AnnotationNode> annotationNodes = new ArrayList<>()
annotationNodes.add(new AnnotationNode(new ClassNode(Delegate)))
fieldNode.addAnnotations(annotationNodes)
classNode.addField(fieldNode)
}
}
When I check the byte code there is no getter/setter methods for the fields inside the Class Person (I used #Delegate on the target field)
However, if I Just add the field manually and compile the code I get getter and setters for the fields in class Person.
Person has 2 fields: firstName, lastName both Strings.

Getting name of the variable for class in groovy

I am using Groovy Expando class to hold some properties and methods. I want to prevent accidental setting of the same property multiple times such as below
e = new Expando()
e.var1 = 'abc'
e.var1 = 'cde'
In order to do that I created a new class that extends Expando like below
class MyExpando extends Expando {
void setProperty(String property, Object newValue) {
if(this.getProperty(property)){
throw new RuntimeException("duplicate declaration of $property in ${this.class.name}")
} else {
super.setProperty(property, newValue)
}
}
}
This works so it throws RuntimeException but I want the error message to be like Duplicate property var1 in e. Now the question is how to get e

Resources