You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Dan Martens <dl...@hotmail.com> on 2000/07/27 23:23:58 UTC

Bug found when calling init methods of defined web apps

Hello all.
This is my first message to this group so please let me know if you have 
heard of this behaviour before.  There is a bug in 
org.apache.tomcat.core.ContextManager which allows for the loading of every 
defined servlet in the web.xml file four times at startup.  Not only is each 
servlets init() method called four times, but four different objects are 
created. Since the code base is fairly thick and requires a lot of debugging 
(speaking from experience) this code segment in the init() method of 
ContextManager seems to be producing the error indirectly.
<-- begin code
while(enum.hasMoreElements()) {
	    context = getContext((String)enum.nextElement());
	    try {
		initContext( context );
end code--->
Each object of this enumeration is a different Context, the problem is 
during the creation of each context, the conf/web.xml file is scanned and 
it's information stored in each seperate object.  Then upon the 
initialization of each context, the servlets which it references are 
initialized. This includes such servlets as the DefaultErrorPage, invoker 
and the jsp servlet. The reason for this behaviour occuring exactly four 
times seems to be because in a clean drop of the 3.1 release, there are four 
subdirectories in the WebApps directory, each of which is loaded as a 
seperate context.
I have created a simple fix to this situation which prevents the servlets 
from being loaded repeatedly, but since this other things may be loaded 
repeatedly as well, it is worth further investigation into the actual root 
of the problem.  The fix is in  
org.apache.tomcat.context.LoadOnStartupInterceptor.contextInit(Context) 
method. Currently as it stands the method looks like this
<------begin code
public void contextInit(Context ctx) {
	Hashtable loadableServlets = new Hashtable();
	init(ctx,loadableServlets);

	Vector orderedKeys = new Vector();
	Enumeration e=  loadableServlets.keys();

	// order keys
	while (e.hasMoreElements()) {
	    Integer key = (Integer)e.nextElement();
	    int slot = -1;
	    for (int i = 0; i < orderedKeys.size(); i++) {
	        if (key.intValue() <
		    ((Integer)(orderedKeys.elementAt(i))).intValue()) {
		    slot = i;
		    break;
		}
	    }
	    if (slot > -1) {
	        orderedKeys.insertElementAt(key, slot);
	    } else {
	        orderedKeys.addElement(key);
	    }
	}

	// loaded ordered servlets

	// Priorities IMO, should start with 0.
	// Only System Servlets should be at 0 and rest of the
	// servlets should be +ve integers.
	// WARNING: Please do not change this without talking to:
	// harishp@eng.sun.com (J2EE impact)

	for (int i = 0; i < orderedKeys.size(); i ++) {
	    Integer key = (Integer)orderedKeys.elementAt(i);
	    Enumeration sOnLevel =  ((Vector)loadableServlets.get( key 
)).elements();
	    while (sOnLevel.hasMoreElements()) {
		String servletName = (String)sOnLevel.nextElement();
		ServletWrapper  result = ctx.getServletByName(servletName);

		if( ctx.getDebug() > 0 ) ctx.log("Loading " + key + " "  + servletName );
		if(result==null)
		    System.out.println("Warning: we try to load an undefined servlet " + 
servletName);
		else {
		    try {
			if( result.getPath() != null )
			    loadJsp( ctx, result );
			else
			    result.loadServlet();
		    } catch (Exception ee) {
			String msg = sm.getString("context.loadServlet.e",
						  servletName);
			System.out.println(msg);
		    }
		}
	    }
	}
}
----end code>
If you make the Hashtable loadableServlets a class variable and add a simple 
if statement encompassing the entire method, it will skip the repeat loading 
of servlets.  Therefore the method fix is:
<-----begin code
public void contextInit(Context ctx) {
if(loadableServlets == null){
	loadableServlets = new Hashtable();
	init(ctx,loadableServlets);

	Vector orderedKeys = new Vector();
	Enumeration e=  loadableServlets.keys();

	// order keys
	while (e.hasMoreElements()) {
	    Integer key = (Integer)e.nextElement();
	    int slot = -1;
	    for (int i = 0; i < orderedKeys.size(); i++) {
	        if (key.intValue() <
		    ((Integer)(orderedKeys.elementAt(i))).intValue()) {
		    slot = i;
		    break;
		}
	    }
	    if (slot > -1) {
	        orderedKeys.insertElementAt(key, slot);
	    } else {
	        orderedKeys.addElement(key);
	    }
	}

	// loaded ordered servlets

	// Priorities IMO, should start with 0.
	// Only System Servlets should be at 0 and rest of the
	// servlets should be +ve integers.
	// WARNING: Please do not change this without talking to:
	// harishp@eng.sun.com (J2EE impact)

	for (int i = 0; i < orderedKeys.size(); i ++) {
	    Integer key = (Integer)orderedKeys.elementAt(i);
	    Enumeration sOnLevel =  ((Vector)loadableServlets.get( key 
)).elements();
	    while (sOnLevel.hasMoreElements()) {
		String servletName = (String)sOnLevel.nextElement();
		ServletWrapper  result = ctx.getServletByName(servletName);

		if( ctx.getDebug() > 0 ) ctx.log("Loading " + key + " "  + servletName );
		if(result==null)
		    System.out.println("Warning: we try to load an undefined servlet " + 
servletName);
		else {
		    try {
			if( result.getPath() != null )
			    loadJsp( ctx, result );
			else
			    result.loadServlet();
		    } catch (Exception ee) {
			String msg = sm.getString("context.loadServlet.e",
						  servletName);
			System.out.println(msg);
		    }
		}
	    }
	}
	}
}
-----end code>
This code fix ensures that if the Hashtable was not set up by a previous 
call to this method, then it will commence loading servlets.  Otherwise, if 
loadableServlets is non-null, then this method has been called before so 
skip the initialization process.
I hope I have helped here, please let me know.  You can reach me either at 
this address or dmartens@ca.ibm.com .  Thankyou for your time.
________________________________________________________________________
Get Your Private, Free E-mail from MSN Hotmail at http://www.hotmail.com


Re: Bug found when calling init methods of defined web apps

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
Dan Martens wrote:

> Hello all.
> This is my first message to this group so please let me know if you have
> heard of this behaviour before.  There is a bug in
> org.apache.tomcat.core.ContextManager which allows for the loading of every
> defined servlet in the web.xml file four times at startup.

If you are talking about the "conf/web.xml" file, and you have four web
apps in your
"webapps" directory, what you are seeing is actually the correct
behavior.  To understand
why, consider the following points:

* The "conf/web.xml" file in Tomcat 3.1 contains defaults that are
  common to all web applications.  When a context is started up,
  this file is read and processed before the "WEB-INF/web.xml" file
  for your own application is processed.  You can pretend that there
  is an invisible "include" for this file at the top of your
application's
  web.xml file, if that makes things clearer.

* By the rules of the servlet specification, every single <servlet>
  definition that is processed from a "web.xml" file must cause the
  creation of a separate servlet instance (of the appropriate class).
  This would be true even if you had two <servlet> instances that
  named the same Java class file.  The reason you want this is that
  you may well have the same servlet class, under two different
  mappings, with two different sets of initialization parameters.

* Because the "conf/web.xml" file contains commonly used servlets
  (like the JSP page compiler servlet), an instance of that servlet
  gets created for each web application.

* This is a "good thing", because each web application has its own
  custom class path (to access the WEB-INF/classes and WEB-INF/lib
  directories of each application).  If Tomcat were changed in the way
  that you are describing, the JSP page compiler for all four web apps
  would reference only the classes from the web app that was initialized
  first.

Craig McClanahan