How to use methods of a wildcard class<?> result - java

I am trying to build a generic tool that finds a hibernate class and uses its methods by means of metadata.
So from the database I get 1: "TABLENAME" 2. "methodname"
Step 1. Finding the hibernate is done.
Class<?> result = generator.getClassFromTableName("TABLENAME");
Step 2. using the methods that are in the class that is returned is something I do not get.
Do i need to use classloader or finding it through result.getConstructors?
Easiest would be if I end up with an instance of 'TableName' and be able to acces all its methods. Hope it is clear!

Thanks #rsp for the reassurance to look within Reflection. First time to dive into it and in the end it was pretty straightforward.
Classloader to load class from string parameter
getConstructor to
find the right constructor and use it with newInstance()
getDeclaredMethod to find the right method and use it with Invoke and
the object instance
Class< ? > testdataClass = getClass().getClassLoader().loadClass(testDataClassname);
Constructor<?> tesdataClassConstructor = Objects.requireNonNull(testdataClass).getConstructor(datacontext);
Object testdataClassObject = tesdataClassConstructor.newInstance(dc);
Method buildMethod = testdataClass.getDeclaredMethod("build");
return buildMethod.invoke(testdataClassObject);

Related

How to get as a stream any class loaded from a jar not in the classpath?

I have a web-app which I want to work roughly as follows:
Someone uploads a jar (say myjar.jar) along with the name of class to analyze (say test.Test). The exact purpose of analysis is irrelevant so assume that the web-app does some static analysis on it.
I am able to upload the jar and load the class in Java using the hacks described here and here (MultiClassLoader).
If I know the methods inside the class, I can invoke them using reflection. Following assumes a method with signature foo(String)
Class c = jarLoader.loadClass("test.Test", true);
Object instance = c.newInstance();
Method foo = instance.getClass().getDeclaredMethod("foo", new Class[]{String.class});
// foo takes one para String
foo.setAccessible(true);
foo.invoke(instance, (Object) "hello");
Now, suppose, I need to find out what methods are there inside test.Test. To do this, I use the ASM framework, which needs the class as a stream. This is where I am running into a problem. I am unable to do getResourceAsStream or its equivalents. This is what I tried:
Type t = org.objectweb.asm.Type.getType(c); // uses asm library
String url = t.getInternalName() + ".class";
c.getClassLoader().getResourceAsStream(url); // gives null
Same problem if I use:
c.getClassLoader().getResourceAsStream(c.getSimpleName() + ".class");
etc.
What is the right way to get resource as stream, where the resource is a class file loaded from a jar NOT in the class path. Note that the entire thing is done via a web-app.
EDIT: Getting methods is just an example. What I really want is to get the class as a stream. I need to use ASM because it gives me other information (such as parameter names), which I cannot get via getDeclaredMethods or getMethods.
Re: "Now I need to find out what methods are there inside test.Test"
You get the class via Class c = jarLoader.loadClass("test.Test", true);. Class also has getDeclaredMethods() and getMethods().
EDIT: #Jus12, from your second link Java Tip 70: Create objects from jar files!:
protected byte[] loadClassBytes (String className)
ASM has a ClassReader(byte[] b) constructor.
You can use a URLClassLoader, which takes a List of URLs in its constructor. You can then load the class by invoking the loadClass method on the URLClassLoader instance to get the Class<?> object for the class that you are after. Once you have that, you can then use reflection to create instances, and/or get general information about the class.
I have not tried, but the URLClassLoader does also have a getResourceAsStream method.

Javassist: Convert CtMethod to java.lang.reflect.Method

I currently need to change the annotation of a java.lang.reflect.Method Object, which should be a clone of the original method so the original one wont get modified. To do so I downloaded the Library Javassist. So, basically, the optimal code to do so would be:
java.lang.reflect.Method myMethod = /*obtain it*/;
java.lang.reflect.Method myMethodClone = myMethod.clone();
myMethodClone.removeAllAnnotations();
myMethodClone.addAnnotation("#MyAnnotation(something=\"something\", etc");
But a code similar to this pseudo-code unfortunately isn't possible. I tried to use javassist to solve my problem, but then I encountered another problem: I can't convert Javassists CtMethod Object into a Method Object, at least not without changing the class where the original method is.
Anyone has an idea how to solve this?
javassist uses his own class hierarchy,not the Java one. If you want to use javassist start reading official doc and how it works.
About your question: conversion Method <-> CtMethod is impossible. In addiction, what do you intend to do with cloned method? If you want to "duplicate" a method in which class will it live? In the same of the original method? Impossible, because you will receive a "method already present" (or similar).
javassist can solve your problem but a full answer if not possible because the question is pretty vague. My advice is to start from official doc or using this tutorial
I managed to get my code working by using the default java Annotation & Method class plus some reflection.
Here's how I did it (Probably won't help anyone, since my problem was really specific, but you never know...)(Pseudo-Code):
//Create Annotation
MyAnnotationOld oldAnnotation;
MyAnnotation modifiedAnnotation = new MyAnnotation{
public Class<? extends java.lang.annotation.Annotation> annotationType() {return oldAnnotation.annotationType();}
public String propertyWhichShallRemainTheSame() {return oldAnnotation.propertyWhichShallRemainTheSame();}
public String propertyWhichShallBeModified() {return "Modified Thingy";}
}
//Copy Method
Method toCopy;
Method copyMethod = Method.class.getDeclaredMethod("copy", (Class<?>[])null);
copyMethod.setAccessible(true);
Method copiedMethod = (Method) copyMethod.invoke(toCopy, (Object[]) null);
//Add annotation to copied method
Field field = Method.class.getDeclaredField("declaredAnnotations");
field.setAccessible(true);
//Intantiate field !!IMPORTANT!! If you don't do this, the field will be null and thus return an error.
copiedMethod.getAnnotations();
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) field.get(copiedMethod);
annotations.put(MyAnnotation.class, modifiedAnnotation);

mockito - how to check an instance inside a method

I am new to Mockito, I am trying to verify the attributes of an object which gets created inside a method.
pseudo code below:
class A{
...
public String methodToTest(){
Parameter params = new Parameter(); //param is basically like a hashmap
params.add("action", "submit");
return process(params);
}
...
public String process(Parameter params){
//do some work based on params
return "done";
}
}
I want to test 2 things:
when I called methodToTest, process() method is called
process() method is called with the correct params containing action "submit"
I was able to verify that process() is eventually called easily using Mockito.verify().
However trying to check that params contains action "submit" is very difficult so far.
I have tried the following but it doesn't work :(
BaseMatcher<Parameter> paramIsCorrect = new BaseMatcher<Parameter>(){
#Overrides
public boolean matches(Object param){
return ("submit".equals((Parameter)param.get("action")));
}
//#Overrides description but do nothing
}
A mockA = mock(A);
A realA = new A();
realA.methodToTest();
verify(mockA).process(argThat(paramIsCorrect))
Any suggestion ?
If you have got verify() to work, presumably it is just a case of using an argument matcher to check the contains of params.
http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#3
The example given in the above docs is verify(mockedList).get(anyInt()). You can also say verify(mockedList).get(argThat(myCustomMatcher)).
As an aside, it sounds like you are mocking the class under test. I've found that this usually means I haven't thought clearly about either my class or my test or both. In your example, you should be able to test that methodToTest() returns the right result irrespective of whether or not it calls process() because it returns a String. The mockito folk have lots of good documentation about this sort thing, particularly the "monkey island" blog: http://monkeyisland.pl/.
Just pass Parameter in as a constructor argument to a constructor of the class A, then use a mocked instance/implementation of Parameter in your test and verify on the mock. That is how it is normally done - you separate your classes and compose them using constructor injection, that enables you to pass in mocks for testing purposes (it also allows rewiring the application and exchanging some commons a lot easier).
If you need to create Parameter on every function invocation you should use a factory that creates Parameter instances and pass that in. Then you can verify on the factory as well as the object created by the factory.

Is it safe to define a method that has the same name as the class in scala?

I am trying to introduce Scala into my Android project, which uses Guice for DI. For Guide to work, I need to add the #Inject annotation to the constructor I would like Guice to use. In my case I created a Scala class and I need to use it in my Java code.
scala:
class scalaClass1(a: String) {
var myA = a
#Inject
def this() = { this("test") }
}
This looks alright, correct? But in another case the constructor does not have any parameters, so I tried
scala:
class scalaClass2() {
var myA: String = null
#Inject
def this() = { this() }
}
And I got an syntax error. Something like recursive definition. Then I tried this:
scala:
class scalaClass2() {
var myA: String = null
#Inject
def scalaClass2() = { this }
}
The code compiled and the app works well on my phone. I have no idea why. I browsed in google, but I could not find any definition/explanation about having a method that has the same name as the class. Why this works? Is there any better solution to my problem?
If you need to to apply #Inject to a constructor without parameters you can use this:
class scalaClass2 #Inject () {
// whatever
}
Note the mandatory empty parentheses. You need them to apply an annotation on the primary constructor. But in this particular case you don't even need #Inject; see below.
In your second example (when you define def this() = { this() }) you are getting an error because you can't define multiple constructors with the same signature, and that's exactly what you are doing - you define primary constructor without parameters and immediately you define secondary constructor, again without parameters.
And in the third example you're really defining a method named scalaClass2 which returns this. It is perfectly valid, but it is not a constructor. As far as I remember, Guice does not need #Inject annotation on parameterless constructor when it is the only constructor in the class, so you can inject scalaClass2 or ask it from Guice, who will create it for you. But you don't really need scalaClass2 method; Guice may call it as a part of method injection procedure but it won't do anything.
Firstly, according to convention class names should start with upper case and methods with lower. But if we would not follow them I would say it is not safe.
Consider having a companion object to the class with apply method defined.
class Person(val name: String, val age: Int)
object Person {
def apply(name: String, age: Int) = new Person(name, age) }
and then create a method with same name and list parameters:
def Person(lastName: String, score: Int): String = s"${lastName} got ${score} points in last game"
Now if you want to make use of object apply method you cannot do it in regular way:
Person("McKenzie", 1000)
will yield McKenzie got 1000 points in last game

Generic List and reflection

I'd like to call via reflection the following method, but I have problem to specify the correct signature:
public void executeRule(List<Node> params, SomethingStrangeFound callMeBack) throws IOException
{
...
}
I tried something like this:
Class partypes[] = new Class[2];
partypes[0] = Class.forName("java.util.List");
partypes[1] = Class.forName("vp.SomethingStrangeFound");
Method meth = cls.getMethod("executeRule", partypes);
It doesn't work because I use "java.util.List" when it must be "List<Node>", but I have no idea how to specify it.
If I just use "java.util.List", I have the following error calling cls.getMethod("executeRule", partypes):
NoSuchMethodException: vp.RuleWebXmlContextParamFacesPortletRenderStyles.executeRule(java.util.List, vp.SomethingStrangeFound)
Any help?
P.S.
At debug time, I see "List<Node>" is resolved with:
(Ljava/util/List<Lorg/w3c/dom/Node;>;Lit/vp/SomethingStrangeFound;)V
but it doesn't help me.
If both parameter classes are available at compile time you can initialize parameter array like this:
Class partypes[] = new Class[] { List.class, SomethingStrangeFound.class };
Method meth = cls.getMethod("executeRule", partypes);
This will guarantee that you have not mistyped qualified class names.
If this still does not work, check available methods in your class using Class#getMethods() method:
for (Method method : cls.getMethods()) {
System.out.println(method);
}
I ran your code and it worked for me.
You have not provided the definition of cls, but I assume it is something like
Class cls = RuleWebXmlContextParamFacesPortletRenderStyles.class;
Since you get a NoSuchMethodException, Class.forName already passed successfully, so no typo there (unless you have multiple SomethingStrangeFound classes, which would mean that you got the package wrong).
Because of this the last thing that comes to my mind is that perhaps you compiled RuleWebXmlContextParamFacesPortletRenderStyles before adding the method in question. Clean your code and recompile.
With partypes of size 2, getMethod will search for a method called "executeRule" with 2 parameters, one of type "java.util.list" and other of type "vp.SomethingStrangeFound"
But your method has only 1 parameter. You need to add a second parameter of type "vp.SomethingStrangeFound" to your method or set partypes size to 1.

Categories