Java class loading from jar file at runtime in Tomcat

954 views Asked by At

I need to load some servlet from jar file and map it to the URL in Tomcat.

Servlet code is

package net.arturik.mymodulepackage;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet(name = "QWERTY", urlPatterns = {"/qqq"})
public class QWERTY extends HttpServlet {


    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            /* TODO output your page here. You may use following sample code. */
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Servlet QWERTY</title>");            
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Servlet QWERTY at " + request.getContextPath() + "</h1>");
            out.println("</body>");
            out.println("</html>");
        }
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }


    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }


    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>

}

Servlet compiled and stored in file MyModulePackage-1.0-SNAPSHOT.jar.

In Java Web App I has ServletContextListenerImpl class:

package net.arturik.testosgispring;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebListener;


@WebListener
public class ServletContextListenerImpl implements ServletContextListener {

    @Override
    public void contextInitialized(final ServletContextEvent sce) {

        try {
            System.out.println("++++++++++++++");

            final ServletContext servletContext = sce.getServletContext();

            //////////////////////////////////////////////
            String pathToJar = "m:\\myhostingpanel\\myhostingpanel\\Tests\\MyModulePackage\\target\\MyModulePackage-1.0-SNAPSHOT.jar";

            JarFile jarFile = new JarFile(pathToJar);
            Enumeration e = jarFile.entries();

            URL[] urls = {new URL("jar:file:" + pathToJar + "!/")};
            URLClassLoader cl = URLClassLoader.newInstance(urls, this.getClass().getClassLoader());

            while (e.hasMoreElements()) {
                JarEntry je = (JarEntry) e.nextElement();
                if (je.isDirectory() || !je.getName().endsWith(".class")) {
                    continue;
                }
                // -6 because of .class
                String className = je.getName().substring(0, je.getName().length() - 6);
                className = className.replace('/', '.'); 
                Class c;
                Object obj;
                try {
                    System.out.println("className !!! " + className);
                    c = cl.loadClass(className);
                    obj = c.newInstance();
                    System.out.println(c.getPackage() + " " + c.getName());
                    Class noparams[] = {};
//                    Method method = c.getDeclaredMethod("qqq", noparams);
//                    method.invoke(obj, null);
//                    MyInter obj1 = (MyInter) c.newInstance();
//                    obj1.qqq();

                    System.out.println("registering");
                    final ServletRegistration.Dynamic dynamic = servletContext.addServlet("QWERTY", c);
                    dynamic.addMapping("/qqq");

                    final Map<String, ? extends ServletRegistration> map = servletContext.getServletRegistrations();
                    for (String key : map.keySet()) {
                        System.out.println("Registered Servlet: " + map.get(key).getName());
                    }
                    System.out.println("end of registering ");

                } catch (Exception ex) {
                    ex.printStackTrace();
                }

            }

            //////////////////////////////////////////////
        } catch (IOException ex) {
            Logger.getLogger(ServletContextListenerImpl.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }

    @Override
    public void contextDestroyed(final ServletContextEvent sce) {
        //NO-OP
    }
}

This code got output

++++++++++++++
className !!! net.arturik.mymodulepackage.QWERTY
package net.arturik.mymodulepackage net.arturik.mymodulepackage.QWERTY
registering
Registered Servlet: default
Registered Servlet: jsp
Registered Servlet: QWERTY
Registered Servlet: T1
Registered Servlet: mvc-dispatcher
Registered Servlet: T2
Registered Servlet: T3
end of registering 

As we can see - servlet registered successfully.

But if I try to open page /qqq , Tomcat got Exception

22-Nov-2014 02:07:56.846 SEVERE [http-nio-8082-exec-5] org.apache.catalina.core.StandardWrapperValve.invoke Allocate exception for servlet QWERTY
 java.lang.ClassNotFoundException: net.arturik.mymodulepackage.QWERTY

As I understand - it is a class visibility problem - my class initialized by URLClassLoader and my class doesn't passed to the main (system) Class Loader.

How can I pass my class, loaded by URLClassLoader to the system class loader for global visibility?

0

There are 0 answers