Javassist - Remove static modifier from method - java

I'm writing some Javassist code to intercept method calls and replace them with a proxy. To do that I'm using ExprEditor to replace the call in the following manner:
public static void main(String[] args) {
try {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get("Test");
CtMethod meth = ctClass.getDeclaredMethod("main");
meth.instrument(new ExprEditor() {
#Override
public void edit(final MethodCall m) throws CannotCompileException {
try {
if (m.getClassName().contains("Functions")) {
/* MAKE NON STATIC
CtClass old = pool.get(m.getClassName());
m.getMethod().setModifiers(Modifier.PUBLIC);
old.toClass();
*/
String s = m.getClassName() + " proxy = (" +
m.getClassName() + ") " + Main.class.getName() + ".create(" + m.getClassName() + ".class);" +
" $_ = proxy." + m.getMethodName() + "($$);";
m.replace(s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
Class clazz = ctClass.toClass();
Method method = clazz.getMethod("main", String[].class);
method.invoke(null, new Object[]{new String[]{}});
} catch (InvocationTargetException e) {
e.getCause().printStackTrace();
} catch (NoSuchMethodException | IllegalAccessException | NotFoundException | CannotCompileException e) {
e.printStackTrace();
}
}
This works as desired as long as the method isn't static. Now I'm trying to change static methods to non static through the use of the commented code.
It seems to me that this should work and there are similar usages in the Javassist docs for other modifiers, but when I uncomment and run it I get the following error message:
javassist.CannotCompileException: by java.lang.ClassFormatError: Arguments can't fit into locals in class file Functions/Color
I've also tried to remove the static modifier instead of just setting modifiers to public as such
m.getMethod().setModifiers(m.getMethod().getModifiers() & ~Modifier.STATIC);
But the problem remains.
Is this actually possible?

So you are trying to remove a static modifier from a reserved entry point method name "main" in class Test. I think the compiler won't let you do that, because main is a reserved method name and can only have one predefined signature. And also, static methods are problematic; when called from within the class, if you remove the static modifier, all calls to them would also cause a compilation error, because they were not ment to be instance methods in original code.

Related

Instantiate and use an object with only the textual class name in Java

I have several classes in the same package in Java. I want to instantiate objects of these classes from an array that has the class names as strings.
Here is an example of a class I would like to use, they all have the same structure.
class Class1 {
public String[] firstMethod(){
String[] data = {"NEW_ITEM"};
return data;
}
}
Here is the class I am attemtempting to instantiate them from.
class Main {
static {
String[] classes = {"Class1","Class2"};
for (String cls : classes) {
try {
Object o = Class.forName(cls).newInstance();
o.firstMethod();
} catch(ClassNotFoundException | IllegalAccessException | InstantiationException ex) {
System.out.println(ex.toString());
}
}
My problem is that when I try to call firstMethod() using the object o, I am getting this error.
exit status 1
Main.java:19: error: cannot find symbol
o.firstMethod();
^
symbol: method firstMethod()
location: variable o of type Object
1 error
I suspect that it is because it is of type Object and not type Class1. I have seen solutions where you typecast the object to the object of the class that you need. However when you typcast, you need to use the name of the class, which is exactly what I am trying to avoid. I need to use the class name as a string.
Does anyone know of a solution where I can call methods with the objects that are created?
You can't call your method the way in your code because you have an object which does not know the type Class1. You need to cast it explicitly like
((Class1)o).firstMethod()
which I don't think this is what you want.
Or, you can iterate through object methods and invoke it dynamically like below:
String[] classes = {"com.yourpackage.Class1", "com.yourpackage.Class2"};
for (String cls : classes) {
try {
Object o = Class.forName(cls).newInstance();
for(Method m : o.getClass().getMethods()) {
System.out.println(m.getName());
if ("firstMethod".equals(m.getName())) {
String[] data = (String[])m.invoke(o, null); // here are the parameters
for(String d : data){
System.out.println(d);
}
}
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException ex) {
System.out.println(ex.toString());
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
The output is :
NEW_ITEM

fail using a variable to call a method in java-android

I've tried using a variable to invoke a java method, using method.invoke(), as suggested in this example. But it seems there should be an object or something as a parameter in method.invoke(). I've tried using null, but the method didn't get invoked. My code is as follows:
String ACTION = "cart";
Method method = SolverService.class.getDeclaredMethod("Method" + ACTION);
method.invoke(null);
I've got a method as:
public void Methodcart(){
Toast.makeText(this,"Method called",Toast.LENGTH_LONG).show();
}
PS: I HAVE TO make this method.invoke() work. Otherwise, I need to write a very long list of switch-case statements.I've gone through the documentation but couldn't understand much about the object instance i might need to use here as I'm new to android app developing.
You can try something similar to the code shown below (Java Reflection) -
Suppose I have a class ClassWithMethods.java with the methods I want to invoke in some other class as shown below -
public class ClassWithMethods {
private int counter;
public void printIt(){
System.out.println("printIt() no param");
}
public void printItString(String temp){
System.out.println("printIt() with param String : " + temp);
}
}
Now I also have another class TestApp.java which will invoke methods of the ClassWithMethods class at runtime using Java Reflection -
public class TestApp {
public static void main(String[] args) {
//no paramater
Class noparams[] = {};
//String parameter
Class[] paramString = new Class[1];
paramString[0] = String.class;
//int parameter
Class[] paramInt = new Class[1];
paramInt[0] = Integer.TYPE;
try{
//load the ClassWithMethods at runtime
Class cls = Class.forName("com.myapps.ClassWithMethods");
Object obj = cls.newInstance();
//call the printIt method
Method method = cls.getDeclaredMethod("printIt", noparams);
method.invoke(obj, null);
//call the printItString method, pass a String param
method = cls.getDeclaredMethod("printItString", paramString);
method.invoke(obj, new String("someString"));
}catch(Exception ex){
ex.printStackTrace();
}
}
I am using Java Reflection in my current project (since you mentioned you are using Android Studio) to get Battery Capacity of device from PowerProfile class which is internal to the Android OS.
public double getBatteryCapacity() {
Object mPowerProfile = null;
try {
mPowerProfile = Class.forName("com.android.internal.os.PowerProfile")
.getConstructor(Context.class)
.newInstance(getContext());
} catch (Exception e) {
e.printStackTrace();
}
try {
// get access to method named "getAveragePower()" in the class "PowerProfile"
Method getAveragePower = Class.forName("com.android.internal.os.PowerProfile").getMethod("getAveragePower", String.class);
//Get total battery capacity in mAh.
double batteryCapacity = (Double) getAveragePower.invoke(mPowerProfile, "battery.capacity");
return batteryCapacity;
} catch (Exception e) {
e.printStackTrace();
}
return 0.0;
}
Here is a screenshot of how the actual method structure looks like in the PowerProfile class -

Class body - Call function to instantiate variable (unhandled exception type)

I'm having a small issue with my java code.
public class test {
static char[] pass = getMac(); // getting error on this line
public static char[] getMac() throws UnknownHostException
{
...code...
return x;
}
}
I am already throwing the exception in the method but i'm getting the error on this line too :
static char[] pass = getMac(); // getting error on this line
unhandled Exception Type : unknownHostException
is there any way to fix this ?
Thanks
I have tried :
try
{
static char[] pass = getMac();
}
catch (UnknownHostException e)
{
.....
}
but it doesn't work in the main class .
I am already throwing the exception in the method...
Right. That's the problem. By saying that the method throws that exception, you're forcing the calling code to handle it. Java's class initialization code isn't going to handle it for you, so you're getting an unhandled exception error.
Either handle it in the method, or defer initializing that static field until a time when you can handle it*. Note that static initializer blocks are allowed to include flow logic, so that's also an option.
Handling it in the method:
public static char[] getMac()
{
try {
// ...
return x;
}
catch (UnknownHostException e) {
// Appropriate handling
return null; // Or whatever's appropriate
}
}
Using a static initializer block:
public class test {
static char[] pass;
static {
try {
// ...
pass = x;
}
catch (UnknownHostException e) {
// Appropriate handling
pass = null; // Or whatever's appropriate
}
}
}

NoSuchMethodException when Dynamically Added Jar's to Classpath

I'm trying to dynamically add Jar's to my programs classpath at run time using THIS method i found since it seems like it worked for a lot of people. When using addPlugin() it throws a NoSuchMethodException (commented in the code below).
Can someone please tell me what I'm missing in order to get this working? I'm not too familiar with this and I've tried looking it up before.
public final class PluginLoader {
private static final Class[] _PARAMS = new Class[] {URL.class};
public static void addPlugin(File plugin) throws PluginException {
URLClassLoader plLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Class plClass = URLClassLoader.class;
try {
Method m = plClass.getDeclaredMethod("addPlugin", _PARAMS); //ERROR HERE
m.setAccessible(true);
m.invoke(plLoader, new Object[] {plugin.toURI().toURL()});
} catch (Exception ex) {
ex.printStackTrace();
throw new PluginException("ERROR: Could not add plugin '" + plugin.getName() + "' to System ClassLoader");
}
}
}
Usage:
PluginLoader.addPlugin(new File("../path/to/jar.jar"));
Constructor<?> cs = ClassLoader.getSystemClassLoader().loadClass("my.main.class.Main").getConstructor(String.class);
Change:
Method m = plClass.getDeclaredMethod("addPlugin", _PARAMS);
to:
Method m = plClass.getDeclaredMethod("addURL", _PARAMS);

Get the method name and it's contained parameters by parsing the exception

When I received an exception such as IOException or RunTimeException, I can only know the line number in the class.
First of my question. Is it possible to retrieve the method name through exception?
Second, is it possible to retrieve the method and the parameter of this method by line number?
p.s. I need to know the exact method name and its parameters, because I want to distinguish the overloading methods. To distinguish overloading methods, all that I know is to determine its parameters.
try{
//your code here}
catch(Exception e){
for (StackTraceElement st : e.getStackTrace())
{
System.out.println("Class: " + st.getClassName() + " Method : "
+ st.getMethodName() + " line : " + st.getLineNumber());
}
}
as you can see in the code above, you can get the stackTrace and loop over it to get all the method names and line numbers, refer to this for more info http://download.oracle.com/javase/1.4.2/docs/api/java/lang/StackTraceElement.html
If you look at the stacktrace you can know in which line the error occurred.
When using an overriden method you get the exact class name, source file and line number, you just have to know how to read it.
From that page:
java.lang.NullPointerException
at MyClass.mash(MyClass.java:9) //<--- HERE!!!!
at MyClass.crunch(MyClass.java:6)
at MyClass.main(MyClass.java:3)
This says, the problem occurred in line 9 of file MyClass.java in the method mash, which was in turn invoked by the method crunch at line 6 of the same file which was invoked by main in line 3 of the same file.
Heres the source code:
class MyClass {
public static void main(String[] args) {
crunch(null); // line 3
}
static void crunch(int[] a) {
mash(a); // line 6
}
static void mash(int[] b) {
System.out.println(b[0]);//line 9, method mash.
}
}
Basically you just have to ... well read it!
Stacktraces are a bit hard to grasp the first time, but later they become a very powerful tool.
I hope this helps.
pass it the exception and it will print the parameter types of the methods along with the exception
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
public class Main
{
public static void main(String[] args)
{
new Main().run();
}
public void run(){
try
{
new Car().run(60, "Casino");
}
catch (Exception e)
{
detailedException(e);
}
try
{
new Engine().run(10);
}
catch (Exception e)
{
detailedException(e);
}
}
public void detailedException(Exception e)
{
try
{
StringBuilder buffer = new StringBuilder(e.getClass().getName()).append(" \"").append(e.getMessage()).append("\"\n");
for (var trace: e.getStackTrace())
{
buffer.append("\tat ").append(trace.getClassName()).append(".").append(trace.getMethodName()).append("(").append(trace.getFileName()).append(":").append(trace.getLineNumber()).append(")[");
Class<?> clazz = Class.forName(trace.getClassName());
ArrayList<Method> methods = new ArrayList<>(Arrays.asList(clazz.getMethods()));
methods.removeIf(m -> !m.getName().equals(trace.getMethodName()));
Method method = methods.get(0);
for (var param: method.getParameters())
{
buffer.append(param.getName()).append(":").append(param.getParameterizedType().getTypeName()).append(", ");
}
buffer.append("]->").append(method.getGenericReturnType().getTypeName()).append("\n");
}
System.err.println(buffer);
}
catch (Exception parseFailed){
e.printStackTrace();
}
}
}
class Car extends Engine
{
public void run(int when, String where) throws Exception
{
super.run(25);
}
}
class Engine
{
public String run(int For) throws Exception
{
throw new Exception("need more fuel");
}
}

Categories

Resources