【问题标题】:How do I add aliases to a Servlet Context in java?如何在 java 中为 Servlet 上下文添加别名?
【发布时间】:2012-09-24 18:49:56
【问题描述】:

我有一个在 Tomcat 下运行的 servlet。 我需要提供一些文件,我想我们可以从外部(到 WEB-APP)目录中将它们称为半静态的(偶尔会更改......它们由应用程序的另一部分更新)。 我设法通过将以下内容添加到 META-INF 目录中的 context.xml 来做到这一点

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

这很好用,在我的 HTML 中我将文件称为

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

在我的 web.xml 里面的 WEB-INF 我让默认服务器按如下方式处理 png 文件

<!-- 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>

所以这很好用。但我想从 java 代码中设置外部目录,而不是通过编辑 context.xml 文件。

现在在 servlet 的 init() 方法中,我可以获得 ServletContext。

    ServletContext sc =  getServletContext();

如果我在调试器中检查这个变量sc,我可以看到几个层次的别名字符串,见附图。如何以编程方式获取此别名字符串? 我已经检查了 ServletContext 文档,但我找不到它很有帮助。 非常感谢任何帮助。


(来源:choicecomp.com)

【问题讨论】:

    标签: servlets static context.xml aliases


    【解决方案1】:

    正如您在调试器中看到的,您的上下文是 Tomcat 的上下文对象org.apache.catalina.core.StandardContext

    您可以在 Tomcat 6 及以下版本中尝试以下步骤:

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

    现在您可以使用 Mapper 类的 addHostAlias(String HostName, String alias) 方法添加主机别名。

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

    这是 Tomcat 7 的代码 sn-p:

        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");
    

    如果映射器中没有主机,请尝试以下操作:

        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());
    

    希望这会有所帮助!

    【讨论】:

    • 感谢您的回答,但这对我在 Tomcat 7 上不起作用。我已经检查了调试器,并且在 addHostAlias 调用之后别名部分保持不变。顺便说一句,您在哪里找到此信息?有没有关于 Tomcat 服务器内部的好教程,或者你在浏览 Tomcats 源 javadoc 还是什么?
    • 这是工作代码。请尝试我更新答案的最后一部分。
    • 再次感谢,但这仍然不起作用。你在哪里调用这个sn-p代码?在 servlet Init() 函数内部?你怎么知道这行得通?你能在调试器中看到上下文别名成员的变化吗?服务器是否找到您正在提供的文件?我已将虚拟名称放入我的 context.xml 文件中的别名中,然后我尝试通过您的方法添加的别名:a)不显示在调试器中,并且 b)servelet 无法从我的工作目录中提供静态文件
    • 是的。我正在使用 Tamcat 7.0.30 并在我的测试 servlet 的 doGet 方法上调用此代码 sn-p。在我的调试器中,在执行此代码 sn-p 之前,我可以看到没有分配别名(在我的情况下,也没有主机)。执行后,我可以看到主机添加了给定的别名。
    【解决方案2】:

    我找到了另一种方法StandardContext.setAliases。在下面找到 Tomcat 7.0.30 的完整工作代码 sn-p。

            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");
    

    请在下面找到我的调试器截图:

    在代码 sn -p 执行之前:

    代码sn -p执行后:

    希望这更有帮助。

    【讨论】:

    • 嗨 Yogendra,感谢这个函数 context.SetAliases(... 确实改变了我在调试器中看到的别名。但是我的 servlet 仍然没有从我的别名目录中提供静态文件。我具有以前在 context.xml 文件中为我工作的相同别名字符串。我认为现在不再需要引用 mapper 的代码行?您在哪里可以找到相关信息或文档?再次感谢
    • 我主要提到了Javadocs。这就是它所说的:“设置当前别名配置。别名列表应采用“/aliasPath1=docBase1,/aliasPath2=docBase2”形式,其中 aliasPathN 必须包含前导 '/' 并且 docBaseN 必须是.war 文件或目录。”
    【解决方案3】:

    这是我根据不同操作系统动态设置 Tomcat7 上下文别名的工作代码。当然你可以改进它

    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】:

      基于 Khanh 的方法,这是一个适用于嵌入式 maven tomcat (v.7.0.62) 的上下文侦听器。

      请注意差异(“Tomcat”而不是“Catalina”并且没有 findService("Catalina")),以便该方法适用于嵌入式 tomcat。与 Khanh 相比,我使用常规方法而不是反射来获取 BaseDirContext 对象。

      最后,您应该注意,您需要在 BaseDirContext 对象而不是 StandardContext 对象上调用 setAliases()!在内部,StandardContext 的 setAliases() 只是一个 setter,而 BaseDirContext 的 setAliases() 做了很多其他的事情,所以已经运行的 tomcat 确实注册了你的新别名。

      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
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2018-06-22
        • 2018-06-22
        • 2018-12-11
        • 2010-12-21
        • 1970-01-01
        • 2021-07-19
        • 2014-12-30
        • 1970-01-01
        • 2014-12-24
        相关资源
        最近更新 更多