Adding IntegerMetaClass to GroovyShell dynamically - java

I would like to use a custom IntegerMetaClass only in a given GroovyShell context.
The reason why is to not pollute the whole runtime with my potentially 'disturbing' IntegerMetaClass.
It works like a charm when I put my IntegerMetaClass.java implementation in the magic package groovy.runtime.metaclass.java.lang. But, when I try to add it manually to an intermediate GroovyClassLoader, it stops working.
// Pseudo-code without try catch, etc
// Within my eval factory
GroovyClassLoader gcl = new GroovyClassLoader(getClass().getClassLoader());
URL url = getClass().getClassLoader().getResource("groovy/runtime/metaclass/java/lang/IntegerMetaClass.groovy"); // I rename it to .groovy file
GroovyCodeSource gcs = new GroovyCodeSource(url);
Class<?> clazz = gcl.parseClass(gcs);
// clazz is not null here and equals to what I expect:
// Class<groovy.runtime.metaclass.java.lang.IntegerMetaClass>
// Now trying to use it in a groovy shell
GroovyShell gs = new GroovyShell(gcl);
gs.evaluate("10.minutes"); // Where .minutes is part of my IntegerMetaClass
// Fail with an NoSuchProperty exception
Do I miss something to do on GroovyClassLoader more than just 'parsing' the MetaClass ? Anywhere else ?
Update1:
As mentioned above, IntegerMetaClass.minutes lookup is working when I put it directly in my java sources classpath.
package groovy.runtime.metaclass.java.lang;
import groovy.lang.DelegatingMetaClass;
import groovy.lang.MetaClass;
public class IntegerMetaClass extends DelegatingMetaClass {
public IntegerMetaClass(Class<Integer> delegate) {
super(delegate);
}
public IntegerMetaClass(MetaClass delegate) {
super(delegate);
}
#Override
public Object getProperty(Object object, String property) {
if ("minutes".equals(property)) {
Integer q = (Integer) object;
return new Minutes(q);
}
return super.getProperty(object, property);
}
}
Update2:
A possible but not satisfying solution :
Adding the following just after the gcl.parseClass call
Constructor<?> constructor = clazz.getConstructor(Class.class);
DelegatingMetaClass dmc = (DelegatingMetaClass) constructor.newInstance(Integer.class);
dmc.initialize();
InvokerHelper.getMetaRegistry().setMetaClass(Integer.class, dmc);
But this solution has to maintain a sort of 'mapping' between MetaClass sources and original targeted class to support more than Integer ...

You can load the class with the GroovyShell classloader:
GroovyShell gs = new GroovyShell()
gs.loader.loadClass('groovy.runtime.metaclass.java.lang.IntegerMetaClass')
gs.evaluate("10.minutes")
Note: I got a java.lang.IncompatibleClassChangeError when IntegerMetaClass was a groovy file but no issues when it was java. That could just be related to my environment setup

Related

How to configure multiple bindings for the same child type

I'm new to Java and I'm jumping right in the deep end and using Guice in a project but I can't work out how to bind different values to a constructor based on what parent object the current object will be injected into.
In my code I want to be able to pass in the path to some XSLT files in the constructor of the MessageTransformer but these should be different depending on what it's being injected into.
public class MessageTransformer {
public MessageTransformer(String XsltRequestPath, String XsltReplyPath {
....
}
}
public class SomeClassThatTransformsMessages {
public SomeClassThatTransformsMessages(Transformer transformer) {
....
}
public class SomeOtherClassThatTransformsMessages {
public SomeOtherClassThatTransformsMessages(Transforms transformer) {
....
}
}
So if I was doing it manually I'm trying to recreate the following:
variable1 = new SomeClassThatTransformsMessages (new Transformer("fileA", "fileB"));
variable2 = new SomeOtherClassThatTransformsMessages (new Transformer("fileC", "fileD"));
I don't think I can use the #Named attribute on the transformer but i can possibly use that on the parent classes but not sure how to wire up the files.
I've looked at all the bindings help but none appear to match.
Thanks

How do I LoadClass() from outside project build?

I'm trying to create an exploit, I want to load a class from outside the netbeans project, of a subclassed cash register, which should have been made final.
I can load the LegitClass fine from within the original package badclassloader, with:
claz = "badclassloader.LegitClass"
loadClass = this.getClass().getClassLoader().loadClass(claz);
(LegitClass)loadClass.newInstance();
Now I want to load the MalClass which lives in another project and package 'Package Mal'
How do I get the Mal.MalClass into the path for the LoadClass() method to find?
I tried the following:
private void loadLibrary() {
if (library != null) {
AccessController.doPrivileged(new PrivilegedAction() {
#Override
public Object run() {
System.loadLibrary("Path/to/Project/mal.jar");
return null;
}
});
}
}
but firstly I got Directory separator should not appear in library name So that clearly isn't it, I'm pretty sure I'm missing something considerable here.
Create a new, custom class loader...
URLClassLoader cl = new URLClassLoader(
new URL[]{new File("Path/to/Project/mal.jar").toURI().toURL()}
);
Load the class from the class loader...
Class clazz = cl.loadClass("Mal.MalClass");
Create a new instance of the class...
Object obj = clazz.newInstance();
Now. You have a problem. Unless the Class you've just loaded implements a interface which is common to both projects, you won't be able to resolve the instance of the Class to an actual type, as the type would be unknown at compile time. Instead, you would be forced to use reflection, which is never pretty...

adding #XmlTransient annotations on runtime (combined with own annotations)

since one day I'm stuck at this Problem. But first I would like to describe, why I'm going the way which is shown:
We`re building a RESTful API in Java using EE7 and Glassfish4. Authentication and authorisation has to be build by ourselves (student project). So the idea was to add our own annotation for #AccesRight and #Roles. After interpreting the metadata on each set and get method of our models (if declared), the #XmlTransient annotation should be set on runtime, when the user has not the right to see this. In short: granting different access at the model attributes.
I tried to modify the method annotations from _model-class-methods (see method signature) but when I run ".toClass()" it fails, because the WebAppClassLoader already has a class loaded (duplicate entry). So I decided to create a copy with another name of the given model (_model.getClass().getName() + transactionToken). The big problem: I can not cast this copy anymore to the original model (I get ClassCastException). The class and the copyclass is stored in the same class loader.
So i considered to invoke a method like e.g. "loadModelByEntity(UserModel _model)" which is stored in all models. The problem is: after running .toClass() of my copy class the method signature now looks like the following: loadModelByEntity(UserModel020a8e6bb07c65da3e9095368db34e843c0b0d1e _model)
Javassist is changing ALL datatypes in the class.
Is there any way to prevent this or to fill my copy model with data? Is there any way to cast my copy models?
Many thanks!
Phil
//my annotation interface
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target( { ElementType.TYPE, ElementType.METHOD} )
public #interface AccessRight
{
String name() default "";
Role[] roles() default {};
boolean self() default true;
boolean friends() default true;
}
//my method where i analyse the annotation (and perhaps set a new one)
public Object filter(Object _model, String _transactionToken) throws Exception
{
String className = _model.getClass().getName() + transactionToken;
ClassPool pool = ClassPool.getDefault();
CtClass copyClass = pool.getOrNull(className);
if(copyClass != null)
{
Class filterModel = copyClass.getClass().getClassLoader().loadClass(className);
Object filterInstance = filterModel.newInstance();
filterInstance.getClass().getDeclaredMethod("loadByEntity", _model.getClass()).invoke(filterInstance, _model);
return filterInstance;
}
pool.insertClassPath(new ClassClassPath(_model.getClass()));
pool.makeClass(className);
copyClass = pool.getAndRename(_model.getClass().getName(), className);
ClassFile copyClassFile = copyClass.getClassFile();
ConstPool constPool = copyClassFile.getConstPool();
AnnotationsAttribute attribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation annotation = attribute.getAnnotation("javax.xml.bind.annotation.XmlTransient");
if(annotation == null)
{
attribute.addAnnotation(new Annotation("javax.xml.bind.annotation.XmlTransient", constPool));
}
for(CtMethod method : copyClass.getDeclaredMethods())
{
if(method.hasAnnotation(AccessRight.class))
{
AccessRight arAnnotation = (AccessRight)method.getAnnotation(AccessRight.class);
if(!checkAccess(arAnnotation.name(), arAnnotation.roles(), arAnnotation.friends(), arAnnotation.self()))
{
method.getMethodInfo().addAttribute(attribute);
}
}
}
return copyClass.toClass().newInstance();
}
//my consideration to fill the copy model (but it doesn`t work, like i described)
public void loadByEntity(UserModel _model)
{
this.m_id = _model.getId();
this.m_firstname = _model.getFirstname();
this.m_lastname = _model.getLastname();
this.m_username = _model.getUsername();
this.m_birthday = _model.getBirthday();
this.m_email = _model.getEmail();
this.m_password = _model.getPassword();
this.m_roleId = _model.getRoleId();
this.m_timestampCreated = _model.getTimestampCreated();
this.m_accessRightList = _model.getAccesRightList();
}
I solved the problem by deleting the method "loadByEntity" (this is Settings.ENTITY_LOAD_METHODNAME) on runtime in the copy class. Then I readded the method to the copy class with a custom Signature and the javassist codeAttribute from the original class. Also I added the original class as the superclass for casting issues. So my signature looks nice and I`m able to cast to the original model. The methods are ALL overwritten now, because the signature is the same.
String className = _model.getClass().getName() + _transactionToken + Helper.getUnixTimestamp() / Math.random();
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(_model.getClass()));
CtClass copyClass = pool.getAndRename(_model.getClass().getName(),className);
CtClass originalClass = pool.get(_model.getClass().getName());
ClassFile copyClassFile = copyClass.getClassFile();
ConstPool constPool = copyClassFile.getConstPool();
copyClass.setSuperclass(pool.get(_model.getClass().getName()));
copyClass.removeMethod(copyClass.getDeclaredMethod(Settings.ENTITY_LOAD_METHODNAME));
//creates a new method without codeattribute BUT(!) it is abstract
CtMethod newLoadMethod = new CtMethod(CtClass.voidType, Settings.ENTITY_LOAD_METHODNAME, new CtClass[] {originalClass}, copyClass);
CtMethod oldLoadMethod = originalClass.getDeclaredMethod(Settings.ENTITY_LOAD_METHODNAME);
//set modifier to NOT abstract
newLoadMethod.setModifiers(newLoadMethod.getModifiers() & ~Modifier.ABSTRACT);
//set the old code attribute
newLoadMethod.getMethodInfo().setCodeAttribute(oldLoadMethod.getMethodInfo().getCodeAttribute());
copyClass.addMethod(newLoadMethod);

How can I use a Scala class as the shared root object across class loaders?

I have implemented a custom class loader in Scala that serves to isolate plugins from the main application. At present I require a Java interface to act as the shared root object so my main application can use the plugin code.
Interface acting as shared root (I would like this to be Scala):
public interface Handler {
public List<HandlerInfo> getHandlers();
}
Example plugin:
class MyPlugin extends Handler {
def getHandlers: java.util.List[HandlerInfo] = // get some handlers
}
Usage in application:
val jarFile = new File(System.getProperty("user.dir") + "/plugins/" + jarName)
val cl = new PluginLoader(jarFile, this.getClass.getClassLoader) // my custom classloader
val classToLoad = Class.forName(className, true, cl)
val handler = classToLoad.newInstance.asInstanceOf[Handler]
val handlers = handler.getHandlers
This works fine, but my problem is that I have to keep this one Java class around (and the resulting build configuration). I would like instead to use a Scala trait or abstract class, like this:
trait Handler {
def getHandlers : List[HandlerInfo]
}
Then my plugin could look like this:
class MyPlugin extends Handler {
def getHandlers: List[HandlerInfo] = // no more java.util.List
}
But I can't do this, because this line
val handler = classToLoad.newInstance.asInstanceOf[Handler]
throws a ClassCastException, presumably because the Scala compiler doesn't generate a nice clean Java interface. Is there any way around this, so I can have a Scala-only project?
The problem is not where you think it is; it's somewhere in your class loader. The default class loader works just fine, even the Java one. Evidence:
// File Handler.scala
trait Handler { def getHandlers: List[String] }
// File MyPlugin.scala
class MyPlugin extends Handler { def getHandlers = List("salmon", "cod") }
// File Interop.java
public class Interop {
public static void main(String[] args) {
try {
Class classToLoad = Class.forName("MyPlugin");
Handler handler = (Handler)classToLoad.newInstance();
System.out.println("Class loader = "+handler.getClass().getClassLoader());
System.out.println(handler.getHandlers());
}
catch (Exception e) { System.out.println("Uh-oh: "+e); }
}
}
And here we run it:
$ java -cp .:/usr/share/scala/2.10/lib/scala-library.jar Interop
Class loader = sun.misc.Launcher$AppClassLoader#1f3e8d89
List(salmon, cod)
$
with the Scala List and everything, and the new class loaded by the default Java class loader.

AbstractMethodError On a Dynamic Loaded Jar

I am loading a plug-in dynamically. Both the plug-in and the software
have been created by us.
I have an Interface lets call it Foo. There is also FooImpl that just
implements that method But FooImpl is in the jar loaded dynamically
public interface Foo {
void write(..someArgument..) throws Exception; }
I have also a PluginLoader class here is the method public
Object loadPlugin(final String jarPath, final Class
pluginInterface) {
try
{
final URI uri = new File(jarPath).toURI();
final URL url = uri.toURL();
final URLClassLoader ucl = new URLClassLoader(new URL[] { url });
try
{
final Class<?> pluginClass = Class.forName("FooImpl", true, ucl);
// Verify if plugin implements plugin interface.
if (pluginClass.getInterfaces()[0].getName().equals(pluginInterface.getName()))
{
// Instantiate plugin.
return pluginClass.newInstance();
} }//[...] </code></pre>
This part is actually working well i think so because after doing some
sysout on the pluginClass i notice: the .getMethods() =
[public void FooImpl.write(..someArgumentType..) throws Exception,
public abstract void some.package.Foo.write(..someArgumentType..)
throws Exception]
the .getGenericInterfaces() = [interface some.package.Foo]
But when i try to call the method write here is what i get
java.lang.AbstractMethodError: FooImpl.write(..SomeArgumentType..;)V
I dont know why there is a ";" and a "V"
So basically i think that it try to call the interface method instead
of the implemented one. So i'm wondering What is going on!
As usual, Thank you for your time and help
An AbstractMethodError suggests that your code is trying to use a different version of a class at runtime compared to the class it was originally built against. You need to ensure that in your execution environment, there isn't a rogue version of your interface implementation on the classpath.

Categories

Resources