Jacob Kjome wrote:I think you've missed the point of what is being done with "javax.servlet.context.tempdir".
Yes, I assumed you were using it for it's intended purpose.
I figured as much. This unorthodox usage was just a last ditch effort to try to make things more automatic, require less configuration, and avoid some maintenance issues. I'd never log to a temp directory. The configuration defaults to logging to the WEB-INF/logs directory of the context being deployed. This assumes that the context isn't being deployed directly from a .war file where there is no File IO possible. In that case, one can set the context-param "log4j-log-home" to any arbitrary location which will override the default.
It is absolutely, utterly, guaranteed that any and all containers claiming to have support for the servlet spec *will* provide a temporary directory that is unique for each web application. If they don't, they aren't compliant. I have no worries about that.
Here is what you are missing. I don't care about using the actual directory. I just care about the directory name. What I was trying to accomplish was a way that one didn't have to keep multiple configuration files in sync with the name of some property...and I also needed to have some pretty decent assurance that that name wouldn't exist already as a system property. Since webapps must have unique context path names, that turns out to be a pretty good way to name system properties.
However, the servlet spec doesn't provide any direct way to obtain the servlet context path (eg... /mycontext) via the ServletContext, which is all I have to work with in the servlet context listener. However, I can grab the value of the system property "javax.servlet.context.tempdir". As it turns out, the way Tomcat name tempdirs is very consistent. For instance, a context with the path of "/mycontext" would have a tempdir named something like this...
C:\Java\Apache\Jakarta\tomcat-4.1.24\work\Standalone\localhost\mycontext
So, I just grab the string following the last index of the value returned by File.separator and, low and behold, I've obtained the name of the currently running context.
Is this any different from ServletContext.getServletContextName() (available in 2.3) or would that be equivalent when available?
I sure wish getServletContextName() would work. However, what that returns is the value of the <display-name> tag in the web.xml. So, what it will return will be somewhat arbitrary or null if not provided. As such, It isn't useful here. I also don't want to overload the usage of <display-name> for something specific to the configuration of the Log4j InitContextListener.
I take that and append ".log.home", use that as a system property name, and set the physical path to where logs should be written for that context which could either be the default of the WEB-INF/logs of the webapp or a user defined path.
What breaks down in other servlet containers is that the string following the last index of the value returned by File.separator for a context with a path of "/mycontext" might be "some-other_random-string_having-nothing_to-do_with-the_name-of_the-context_path". Now, in one sense, this is fine because it is unique since we don't want to accidentally overwrite a system property being used by some other application. However, it is far from predictable and might change with each deployment of the webapp in some containers. We need to be able to put something we can count on in log4j.xml such as the following an know it will just work...
<param name="File" value="${mycontext.log.home}/main.log" />
In summation, I require...
1. a name that is reasonably guaranteed to be unique in the VM
- this is achieved by using the name of the context path which must be unique within the container so a system property named after something that is already unique is pretty well guaranteed to be itself
2. not have to specify this name in multiple configuration files. Only log4j.xml.
- It is extra work to have to change things in two places. To avoid this, instead of requiring the use to have to provide a context-param with the name of the system variable, I just have the configuration derive it on its own. This also prevents arbitrary naming that might not be as unque in the VM as one might hope.
3. the name must be predictable
- one has to reference a system property name in log4j.xml that they can be assured will exist at runtime, otherwise it is of no use.
Defining a naming scheme based on [context path].log.home is entirely predictable and the dynamically derived system variable name works for this every time under Tomcat.
Let me ask some stupid questions, since I do not really know the purpose of the ContextClassLoaderSelector:
Ok
How does this work outside of a servlet container? Or is it only intended to run inside a container?
The only "intended" purpose of ContextClassLoaderSelector is to run inside a servlet container. It also assumes a certain classloader hierarchy where each webapp runs in its own classloader. I haven't really tested this outside of Tomcat and can't guarantee it works anywhere else, although I think it should. However, there is also a ContextJNDISelector which takes advantage of the standard JNDI interface that is guaranteed to be provided by all servlet containers and J2EE containers in general.
Now, the idea of both of these is to allow for a distinct logging environment for each webapp with log4j.jar sitting in a parent classloader (not in WEB-INF/lib of a webapp). Normally, all apps would end up sharing the same logger repository. However, with logger repositories keyed on particular classloaders or particular JNDI namespaces, one can obtain separate logger repositories for each webapp context.
Read this for more info... http://www.qos.ch/logging/sc.html
What are the other configuration files you refer to?
Note that the configuration is for the servlet context listener "InitContextListener" which does the configuration. the repository selectors are just classes that implement the log4j RepositorySelector interface. They don't have any configuration at all.
The configuration files involved with the InitContextListener are web.xml and log4j.xml (or whatever log4j configuration file you specify). The web.xml contains <context-param> elements that define specific parameters that the InitContextListener reads. Based on that, it configures log4j and installs a repository selector. Note that once one app sets the repository selector, the other apps are destined to use that one since log4j can have only one repository selector defined. You should read the Javadoc for the InitContextListener and each of the repository selectors I mentioned as they describe all of this in pretty good detail. You can find it in the jakarta-log4j-sandbox module in CVS.
Might there be a way to point both places to the same config file?
I'm not sure I understand what you are getting at here? The web.xml file is responsible for providing information to the InitContextListener. The only thing log4j.xml needs is the name of the system property. It is the name of the system property that needs to stay in sync. If, for instance, you had this line in the configuration of a FileAppender...
<param name="File" value="${mycontext.log.home}/main.log" />
Then you'd better make darned sure that the web.xml has a <context-param> that looks something like this (this is what I need to add, BTW)...
<context-param> <param-name>log4j-system-variable-name</param-name> <param-value>mycontext.log.home</param-value> </context-param>
Of course that isn't an issue in Tomcat since the dynamically generated system variable name will be exactly "mycontext.log.home" without having to specify it in web.xml, but for other servlet containers, specifying the context-param may be necessary. If it is necessary, then if you change it in one place, you'd better change it in the other. That is why I tried (and partially succeeded) to avoid having this context-param at all. But, alas, it must be provided. Also, you need to make sure this name is unique within the JVM running the container. Again, using the naming scheme [context path].log.home should provide a unique name.
What happens if tomcat changes the way it implements the temporary directory mechanism?
Which is exactly why I need to provide the new context-param allowing for optional override of the dynamic generation of the system variable name. This should be done sometime this weekend.
Jake