How do I add aliases to a Servlet Context in java?

做~自己de王妃 提交于 2019-12-08 01:50:49

问题


I have a servlet running under Tomcat. I need to serve some files, I guess we can call them semi-static (which change occasionally ... they are updated by another part of the app) from an external (to the WEB-APP) directory. I have managed to do this by adding the following to my context.xml in the META-INF directory

<Context aliases="/working_dir=c:/apache_tomcat_working_dir" ></Context>

This works fine, in my HTML I refer to the file as

<img src="/myWebbApp/working_dir/fixpermin_zoom.png">

and in my web.xml inside WEB-INF I let the default server handle png files as follows

<!-- use default for static serving of png's, js and css, also ico -->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
</servlet-mapping>

So this works fine. But I want to set the external directory from inside java code, not by editing the context.xml file.

Now in the init() method of the servlet I can get the ServletContext.

    ServletContext sc =  getServletContext();

If I examine this variable sc in the debugger, I can see the alias string several levels deep, see the attached image. How can I get at this alias string programatically? I have checked the ServletContext docs, but i can't find it very helpful. Any help much appreciated.


(source: choicecomp.com)


回答1:


As you can see in your debugger, your context is Tomcat's Context Object org.apache.catalina.core.StandardContext

You can try following steps in Tomcat 6 and below:

    StandardEngine engine = (StandardEngine) ServerFactory.getServer().findService("Catalina").getContainer();
    StandardContext context = (StandardContext) engine.findChild(engine.getDefaultHost()).findChild(getServletContext().getContextPath());
    Mapper mapper = context.getMapper();

Now you can add Host alias using addHostAlias(String HostName, String alias) method of the Mapper class.

    mapper.addHostAlias(engine.getDefaultHost(), "myAlias");

Here is the code snippet for Tomcat 7:

    MBeanServer mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
    ObjectName name = new ObjectName("Catalina", "type", "Server");
    Server server = (Server) mBeanServer.getAttribute(name, "managedResource");
    StandardEngine engine = (StandardEngine) server.findService("Catalina").getContainer();
    StandardContext context = (StandardContext) engine.findChild(engine.getDefaultHost()).findChild(getServletContext().getContextPath());
    Mapper mapper = context.getMapper();
    mapper.addHostAlias(engine.getDefaultHost(), "myAlias");

If there is no host in the mapper, please try below:

    MBeanServer mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
    ObjectName name = new ObjectName("Catalina", "type", "Server");
    Server server = (Server) mBeanServer.getAttribute(name, "managedResource");
    StandardEngine engine = (StandardEngine) server.findService("Catalina").getContainer();
    StandardContext context = (StandardContext) engine.findChild(engine.getDefaultHost()).findChild(getServletContext().getContextPath());
    Mapper mapper = context.getMapper();
    //just a clean up step(remove the host)
    mapper.removeHost(engine.getDefaultHost());
    //add the host back with all required aliases 
    mapper.addHost(engine.getDefaultHost(), new String[]{"myAlias"}, engine.getDefaultHost());

Hope this helps!




回答2:


I found another method StandardContext.setAliases. Find below the full working code snippet for Tomcat 7.0.30.

        MBeanServer mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
        ObjectName name = new ObjectName("Catalina", "type", "Server");
        Server server = (Server) mBeanServer.getAttribute(name, "managedResource");
        StandardEngine engine = (StandardEngine) server.findService("Catalina").getContainer();
        StandardContext context = (StandardContext) engine.findChild(engine.getDefaultHost()).findChild(getServletContext().getContextPath());
        context.setAliases("myAlias");
        //infact aliases should be proper e.g. below 
        //context.setAliases("/aliasPath1=docBase1,/aliasPath2=docBase2");
        Mapper mapper = context.getMapper();
        mapper.removeHost(engine.getDefaultHost());
        mapper.addHost(engine.getDefaultHost(), new String[]{"myAlias"}, engine.getDefaultHost());
        mapper.addHostAlias(engine.getDefaultHost(), "myAlias");
        //infact aliases should be proper e.g. below 
        //mapper.addHostAlias(engine.getDefaultHost(), "/aliasPath1=docBase1,/aliasPath2=docBase2");

Please find my debugger screenshots below:

Before the code snippet execution:

After the code snippet execution:

Hope this is more helpful.




回答3:


Here is my working code to dynamically set Tomcat7 context alias depending on different operating systems. Sure you can improve on it

public class ContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
    ServletContext context = sce.getServletContext();

    // tomcat 7.x
    try {
        MBeanServer mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
        ObjectName name = new ObjectName("Catalina", "type", "Server");
        Object server = mBeanServer.getAttribute(name, "managedResource");

        Object service = server.getClass().getMethod("findService", String.class).invoke(server, "Catalina");  //StandardService[Catalina]
        Object connectors = service.getClass().getMethod("findConnectors").invoke(service);
        Object engine = service.getClass().getMethod("getContainer").invoke(service);  //StandardEngine[Catalina]
        Object host = Array.get(engine.getClass().getMethod("findChildren").invoke(engine), 0);  //StandardHost[Catalina]
        Object stdContext = Array.get(host.getClass().getMethod("findChildren").invoke(host), 0);  //StandardContext[Catalina]
        Object mapper = stdContext.getClass().getMethod("getMapper").invoke(stdContext);
        //just a clean up step(remove the host)
        Field f1 = mapper.getClass().getDeclaredField("context");
        f1.setAccessible(true);
        Object ct = f1.get(mapper);

        Field f2 = ct.getClass().getDeclaredField("resources");
        f2.setAccessible(true);
        Object rs = f2.get(ct);

        Field f3 = rs.getClass().getDeclaredField("dirContext");
        f3.setAccessible(true);
        Object dc = f3.get(rs);

        mapper.getClass().getMethod("removeHost",String.class).invoke(mapper, host.getClass().getMethod("getName").invoke(host));
        //add the host back with all required aliases
        switch (OsCheck.getOperatingSystemType()) {
            case Windows:
                dc.getClass().getMethod("setAliases",String.class).invoke(dc,"/img/avatars=" + winAvatarAlias);
                break;
            default:
                dc.getClass().getMethod("setAliases",String.class).invoke(dc,"/img/avatars=" + linuxAvatarAlias);
                break;
        }
        String ports = "";
        for (Object o :(Object[]) connectors ) {
            ports = ports + (Integer)o.getClass().getMethod("getPort").invoke(o) + " ";
        }
        log.info("Tomcat 7.x detected, service {}, engine {}, host {}, stdContext {}, server port: {}",
                service.getClass().getMethod("getName").invoke(service),
                engine.getClass().getMethod("getName").invoke(engine),
                host.getClass().getMethod("getName").invoke(host),
                stdContext.getClass().getMethod("getDisplayName").invoke(stdContext),
                ports);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

}




回答4:


Based on Khanh's approach, here is a context listener that works for an embedded maven tomcat (v.7.0.62).

Please note the differences ("Tomcat" instead of "Catalina" and no findService("Catalina")), so that the approach works for an embedded tomcat. In contrast to Khanh, I used regular methods instead of reflection to get the BaseDirContext object.

Finally, you should note that you need to call setAliases() on the BaseDirContext object instead of the StandardContext object! Internally, StandardContext's setAliases() is just a setter, whereas BaseDirContext's setAliases() does a lot of other stuff, so that the already running tomcat indeed registers your new aliases.

import org.apache.catalina.Container;
import org.apache.catalina.Server;
import org.apache.catalina.Service;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.log4j.Logger;
import org.apache.naming.resources.BaseDirContext;
import org.apache.naming.resources.ProxyDirContext;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class AliasesContextListener implements ServletContextListener {
    private static Logger log = Logger.getLogger(AliasesContextListener.class);

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
            String aliases = "/foo=C:\\bar";

            //get current tomcat server, engine and context objects
            MBeanServer mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
            ObjectName name = new ObjectName("Tomcat", "type", "Server");
            Server server = (Server) mBeanServer.getAttribute(name, "managedResource");
            Service[] services = server.findServices();
            StandardEngine engine = (StandardEngine) services[0].getContainer();
            Container defaultHostContainer = engine.findChild(engine.getDefaultHost());

            ServletContext servletContext = sce.getServletContext();
            StandardContext standardContext = (StandardContext) defaultHostContainer.findChild(servletContext.getContextPath());
            ProxyDirContext proxyDirContext = (ProxyDirContext) standardContext.getResources();
            BaseDirContext baseDirContext = (BaseDirContext) proxyDirContext.getDirContext();

            //modify the aliases entry
            baseDirContext.setAliases(aliases);
        } catch (Exception e) {
            log.error("error while setting aliases in context listener", e);
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //not implemented
    }
}


来源:https://stackoverflow.com/questions/12715331/how-do-i-add-aliases-to-a-servlet-context-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!