I have been getting my hands dirty with Java reflection and have come across a bit of a hurdle. If I try to call the following method via reflection I get a java.lang.NoSuchMethodException: org.demonking.CrossHandler.HandleRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) thrown.
Here is the code for the method:
public void HandleRequest(HttpServletRequest req,HttpServletResponse resp)
{
try{
System.out.println("i am here");
//RequestDispatcher view = req.getRequestDispatcher("CrossFile.jsp");
// don't add your web-app name to the path
// view.forward(req, resp);
}catch(Exception ex)
{
System.out.println("in exception");
}
}
and here is the reflection code:
try {
Method m=cls.getMethod("HandleRequest", HttpServletRequest.class,HttpServletResponse.class); // error on this line
//Object obj=cls.newInstance();
//m.invoke(obj,new Object[]{req,resp});
} catch (Exception ex) {
ex.printStackTrace();
System.out.println(ex.getCause());
}
Note that other methods called via reflection works just fine.
here is how i am loading the class via classloader.Also note that the reflection code is placed in a servlet class's doGet method of a web app where as the class loaded and method being called reside in a different non-web application.Also the same class's other method are working just fine via reflection only this method which has httpservletrequest and response parameters is not working.
File file = new File("C:\\Users\\Demonking\\Documents\\eclipseprojects\\ADemoOne\\bin\\");
URL url = file.toURI().toURL();
URL[] urls = new URL[] { url};
ClassLoader cl = new URLClassLoader(urls);
Class<?> cls = cl.loadClass("org.demonking.CrossHandler");
try {
Method m=cls.getMethod("HandleRequest", HttpServletRequest.class,HttpServletResponse.class);
Object obj=cls.newInstance();
m.invoke(obj,new Object[]{req,resp});
} catch (Exception ex)
{
ex.printStackTrace();
System.out.println(ex.getCause());
}
Related
Actually i am having a spring main class as follows.
ClassLoader loader = null;
try {
loader = URLClassLoader.newInstance(new URL[]{new
File(plugins + "/" + pluginName + "/" + pluginName +
".jar").toURI().toURL()}, getClass().getClassLoader());
} catch (MalformedURLException e) {
e.printStackTrace();
}
Class<?> clazz = null;
try {
clazz = Class.forName("com.sample.Specific", true, loader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method method = null;
try {
method = clazz.getMethod("run",new Class[]{});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
method.invoke(clazz.newinstance,new Object[]{});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Specific Class is follow :
package com.sample
#Service
public class Specific {
#Autowired
private FD fd;
public void run(){
fd.init();
}
}
#Autowired FD comes to be null. Can anyone give me some solution as i also know new operator will not work for #autowired. As i am loading class with new instance then only it becomes null. Can anyone guide me in this thing
Spring has its own way to provide you new objects. As long as you're consistent using #Autowired and #Component/#Service/#Repository/#Controller there should be no problem
And since all "business" object instantiation is handled by Spring you should never use new. If you have no other way of getting an instance (something I realy doubt about it) you can use ApplicationContext.getBean() but as I said, in most cases this is not required (and this is also a bad practice)
If you need several instances of a class instead of injecting them (by using #Autowired) you can inject a Provider<T>
UPDATE
Since the class is known at runtime you need to inject an ApplicationContext and use it to get the bean:
public class TheClassWhereYouAreCreatingTheObject {
#Autowired
private ApplicationContext context; // You definitely need this
public void theMethodWhereYouAreCreatingTheObject() {
Class<?> clazz = ... // getting the object class
Object instance = context.getBean(clazz); // getting and instance trough Spring
// If you know that kind of object you will get cast it at call its methods
((Specific) instance).run();
// If you know anything about the class you will have to use reflection
Method method = clazz.getMethod("run", new Class[]{});
method.invoke(instance, new Object[]{});
}
}
Add Specific Service bean inside your main class. As long as the service is inside one your component scan packages then you shall be fine. Do not use new operator.
#Autowired
private Specific specific;
If you want to take advantage of autowiring then I think we have to think from spring terms.
you can use Beanutils to create a new instance and play with reflections supporting spring features.
Please go through below methods:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html
We're trying to debug an unreproducible issue with WebStart, where access to resources inside Jars will "randomly" fail. Maybe one every 1000 application run will end with this error, which can happen anywhere where resources are read from a jar.
Searching in Google and the Java Bug database brought nothing similar (or at least, nothing helpful).
We are trying to get more info into what happens on the client by "instrumenting" the application so we track all calls to ClassLoader.getResource(String) (including indirectly over ClassLoader.getResourceAsStream(String)). Without changing the app code, I have created a "launcher" that would run the whole app with a custom classloader.
Unfortunately, it seems my ClassLoader is somehow bypassed. I do not see any of the expected System.out output. Here is what I tried:
private static final class MyClassLoader extends ClassLoader {
private MyClassLoader() {
super(TheClassThatMainIsIn.class.getClassLoader());
}
#Override
public URL getResource(String name) {
System.out.println("getResource("+name+")");
// Snip
return super.getResource(name);
}
#Override
public InputStream getResourceAsStream(String name) {
System.out.println("getResourceAsStream("+name+")");
final URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (final IOException e) {
return null;
}
}
}
public static void main(String[] args) {
System.out.println("Starting MyRealApp Launcher ...");
final MyClassLoader loader = new MyClassLoader();
try {
Class<?> realAppClasss = loader.loadClass("MyRealAppClass");
Method main = realAppClasss.getMethod("main", String[].class);
main.invoke(null, (Object) args);
} catch (final RuntimeException e) {
throw e;
} catch (final Error e) {
throw e;
} catch (final InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new UndeclaredThrowableException(cause);
} catch (final Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
What am I doing wrong here?
Yes. This works, in principal.
However, you've to account how the resource loading code get's to the class loader. Since the class don't show up, it looks like they use the parents class loader.
You've to account different scenarios:
Code using context class loader, like:
Thread.currentThread().getContextClassLoader().getResource("via-context");
This is easy to achieve, by setting it before calling into main:
Thread.currentThread().setContextClassLoader(loader);
Method main = realAppClasss.getMethod("main", String[].class);
main.invoke(null, (Object) args);
Next thing you've to account is code which 'takes' class loader from current class, and load it that. When you're class is loaded via the parent class loader, it will also use that class loader to get the resource. Like:
MyRealAppClass.class.getResource("via-class");
MyRealAppClass.class.getClassLoader().getResource("via-class");
objectInfApp.getClass().getClassLoader().getResource("via-class");
To avoid that you've to ensure that the apps classes are actually loaded with your class loader, not the parent. For a simple main, you can extend from the URL class loader, skip any parent and user the original class path for the URL's. Like:
// URL class loader to lookup in jars etc
private static class MyClassLoader extends URLClassLoader
{
public MyClassLoader(URL[] urls) {
// Use the given URLs and skip any parent class loader, directly go to the system loader
super(urls,null);
}
// ...
// Then setup the class path
String[] classPath = System.getProperty("java.class.path").split(";");
URL[] classPathUrls = new URL[classPath.length];
for (int i = 0; i < classPath.length; i++) {
classPathUrls[i] = new File(classPath[i]).toURL();
}
MyClassLoader loader = new MyClassLoader(classPathUrls);
This should cover the most basic cases. When you're actual application itself has more class loader trickery, there might more you need to setup.
I'm trying to call different action methods depending on something I put in the session earlier. For this I override the onRequest method in Global like it's recommended in Play's tutorial. I use Java reflection to construct a new method with the same name and parameters but in a different class B. Class B and and the original class with the original actionMethod implement the same interface. So there shouldn't be a problem.
My onRequest in Global looks like:
#Override
public Action onRequest(Request request, final Method actionMethod) {
if (checkSomething) {
return super.onRequest(request, getNewActionMethod(actionMethod));
}
return super.onRequest(request, actionMethod);
}
private Method getNewActionMethod(Method oldActionMethod) {
String name = oldActionMethod.getName();
Class<?>[] parameterTypes = oldActionMethod.getParameterTypes();
Method newActionMethod = null;
try {
newActionMethod = B.class.getMethod(name, parameterTypes);
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return newActionMethod;
}
The Problem here is that Play just ignores my new action method and keeps insisting to call the old one. Am I missing something?
I'm using Play framework 2.2.3.
I´m trying to load a class from a jar, I´m using a classLoader.
I have this parts of code for prepare the classLoader:
private void loadClass(){
try{
JarFile jarFile = new JarFile( Path);
Enumeration e = jarFile.entries();
URL[] urls = { new URL("jar:file:" + Path +"!/") };
classLoader = URLClassLoader.newInstance(urls);
} catch (MalformedURLException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Now I load a class, and I try to get a new instance
....
loadClass();
Class device = classLoader.loadClass( "org.myPackage.MyClass");
MyMotherClass Device = ( MyMotherClass) device.newInstance();
...
MyClass extends of MyMotherClass, and when I do classLoader.loadClass( "org.myPackage.MyClass"), the MyMotherClass it is in the classLoader.
At the moment, all right.
now, in device.newInstance(), I get a exception. The problem is the other classes that are used by MyClass, are not in the classpath.
What can i do?
I have a another method that load all the needed classes in the classLoader, but does not work when I get the new instance.
I can not change MyClass and the others
Here's some code I use to load a jar dynamically at run-time. I exploit reflection to circumvent the fact that you ain't really supposed to do this (that is, modify the class path after the JVM has started). Just change my.proprietary.exception to something sensible.
/*
* Adds the supplied library to java.class.path.
* This is benign if the library is already loaded.
*/
public static synchronized void loadLibrary(java.io.File jar) throws my.proprietary.exception
{
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[]{url});
} catch (final NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new my.proprietary.exception(e.getMessage());
}
}
i have a problem is:
java.lang.Exception: ServletConfig has not been initialized
I searched for it nearly 2 days but i did not have a solution for me. Every one had said that
super.init(config) must be used. I have tried this, but there is nothing change for me.
My init method;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
AppServiceServlet service = new AppServiceServlet();
try {
service.getir();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
AutoCheckStatus.autoCheckStatus(600000);
}
and my AppServiceServlet;
public List<SswAppServiceDto> getir() throws Exception {
try {
final WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this
.getServletContext());
setiAppServiceBusinessManager((IAppServiceBusinessManager) context.getBean(BEAN_ADI));
List<SswAppService> result = getiAppServiceBusinessManager().getir();
List<SswAppServiceDto> list = DtoConverter.convertSswAppServiceDto(result);
for (int i = 0; i < result.size(); i++) {
AppService appService = new AppService();
appService.setServiceName(result.get(i).getName());
appService.setUid(result.get(i).getServiceUid());
appService.setHost(result.get(i).getHost());
appService.setPort((int) result.get(i).getPort());
SystemConfiguration.appServiceList.put(appService.getUid(), appService);
}
return list;
} catch (RuntimeException e) {
throw new Exception(e.getMessage(), e.getCause());
}
}
The exception is thrown in this line;
final WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
in AppServiceServlet and says:
java.lang.Exception: ServletConfig has not been initialized.
Pls help.
This call:
AppServiceServlet service = new AppServiceServlet();
Instantiates a servlet instance via new, which circumvents the normal, container managed creation of a servlet. As such, critical class variables (for example, the servlet config) don't get properly initialized.
Later on, you are making a call to getServletContext, which simply redirects to getServletConfig().getServletContext(), but because the servlet configuration was never completed you get an exception.
Infact, calling new on a servlet the way you are is non-compliant with the specification - servlets are supposed to be maintained by the web app container. The proper way to launch a startup servlet is either via configuration in your web.xml file, or via annotation.