4

I've got an unusual problem (well, I think that it is unusual). I need to be able to upload an uncompiled Java source file via a web interface and have the receiving controller compile it and execute a method on that compiled class.

I have come some way with it - I can upload the file, and compile it successfully. However, no matter what I try I can not load the (new) class definition and instantiate it. I keep getting java.lang.ClassNotFoundException exceptions thrown.

Here is my code;

        String fileName = patchFile.getFileItem().getName();
    String fullName = "<package name>" + fileName.substring(0, fileName.indexOf("."));

    // instantiate the Java compiler
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    JavaFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));

    // load the uploaded files into the compiler
    List<JavaFileObject> files = new ArrayList<JavaFileObject>();
    files.add(new ByteArrayJavaFileObject(fullName, patchFile.getBytes()));

    // set the classpath
    List<String> options = new ArrayList<String>();

    options.add("-classpath");
    StringBuilder sb = new StringBuilder();
    URLClassLoader urlClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
    for (URL url : urlClassLoader.getURLs()) {
        sb.append(url.getFile()).append(File.pathSeparator);            
    }
    options.add(sb.toString());

    // execute the compiler
    compiler.getTask(null, fileManager, null, options, null, files).call();

    // instantiate the class (FAILS HERE)
    Object instance = fileManager.getClassLoader(null).loadClass(fullName).newInstance();

    // close the file manager
    fileManager.close();

I have also tried;

urlClassLoader.loadClass(fullName).newInstance();

But with no luck...

Could anyone shed some light on this?

Thanks in advance, Ben

As requested, here is the stacktrace;

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassNotFoundException: com.sifourteen.papyrus.fabricators.patches.TestFormFabricator
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:656)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:343)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:119)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:177)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:187)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:109)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
root cause

java.lang.ClassNotFoundException: com.sifourteen.papyrus.fabricators.patches.TestFormFabricator
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1680)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1526)
    java.lang.Class.forName0(Native Method)
    java.lang.Class.forName(Class.java:247)
    com.sifourteen.papyrus.controllers.AdminController.patch(AdminController.java:152)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    java.lang.reflect.Method.invoke(Method.java:597)
    org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:426)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:343)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:119)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:177)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:187)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:109)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
benjimix
  • 609
  • 7
  • 18
  • 1
    Do you have a stack trace of the class loader failure you can attach? – Femi Jun 16 '11 at 04:11
  • "I keep getting java.lang.ClassNotFoundException exceptions thrown." Posting the exception stack traces would help. – Vineet Reynolds Jun 16 '11 at 04:13
  • Posted stacktrace. Should have thought of that myself. Thanks. :) – benjimix Jun 16 '11 at 04:15
  • 1
    You seem to be implying that it works fine in a non-webapp environment. E.g. just a plain Java client application with a `main()` method. Is this true? If not, did you test it as such? After all this is definitely a classpath issue. In any way, you may find the example in this question useful as starting point: http://stackoverflow.com/questions/2946338/how-do-i-programmatically-compile-and-instantiate-a-java-class – BalusC Jun 16 '11 at 04:18
  • No, I have not tested it as a client application. That will have to be my next step if I can not find a resolution here. The reason that I have shied away from that is that class-loading issues can be a bit tricky in managed web environments and wanted to make sure that I was testing "like for like". Is this kind of thing even possible in a web app context? I thought that I might be running up against security issues at one point... – benjimix Jun 16 '11 at 04:23
  • I guess what I am not fully comprehending is how the class is successfully compiled, but then can not be found immediately afterwards. One would assume that I am looking in the "wrong" classloader (as there would be more than one in my web context as I am running in Tomcat). Would there be any truth to that? – benjimix Jun 16 '11 at 04:32
  • @benjimix could you post your solution? I'm working on a similar project and having issue to compile: http://stackoverflow.com/questions/39796527/compile-dynamically-java-class-web-project – Victor Bello Oct 03 '16 at 16:30

2 Answers2

1

the class file needs to be in the classpath. (e.g. -d WEB-INF/classes)

irreputable
  • 44,725
  • 9
  • 65
  • 93
  • Nope sorry that didn't work either. While it is true that the loaded class must be in the classpath (obviously) this is an 'in-memory' compilation. I am not even sure where the compiled class is "going". I just assumed that my custom file manager would be able to find it. – benjimix Jun 16 '11 at 04:39
  • so you copied something from internet and it didn't work. I had to google that thing to understand what you are saying, and it's ... not for serious use. its toy class loader is only good for one simple class without ANY dependency on ANY other class. – irreputable Jun 16 '11 at 05:08
  • Yes, you've caught me - how embarrassing. Serves me right for getting too liberal with the copy and paste! I had just traced this exact problem when I came back in here to check if there where any updates. So now the question is - how do I get my FileManager to use a classloader that has my 'standard' URL classloader as it's parent? Just working on that now... – benjimix Jun 16 '11 at 05:14
  • I just fixed this - set the URLClassLoader as the parent of the classloader that was created by my FileManager - voila fixed! Serves me right for copying and pasting and then asking a stupid question on SO. I will award you the points for the answer. Thanks for much for taking the time to reply. – benjimix Jun 16 '11 at 05:21
0

Not sure if it will work, but worth a try anyway. Try this:

Object instance = fileManager.getClassLoader(javax.tools.StandardLocation.CLASS_PATH).loadClass(fullName).newInstance();
Femi
  • 64,273
  • 8
  • 118
  • 148
  • 1
    Ok just tried this and got this error 'Handler processing failed; nested exception is java.lang.NoClassDefFoundError: com/sifourteen/papyrus/fabricators/AbstractFabricator (wrong name: com/sifourteen/papyrus/fabricators/patches/TestFormFabricator)'. So sorry that didn't work... definitely worth a shot though. – benjimix Jun 16 '11 at 04:19
  • Is `com.sifourteen.papyrus.fabricators.AbstractFabricator` the name of the class you are trying to compile? – Femi Jun 16 '11 at 05:14