问题
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