One of the most common issues about the JavaServer Page (JSP) newsgroups is related to recompilation. I don't want to re-compile JSP, but I have to do so. This is a headache for many developers. This article will describe the scenario of re-compilation, and introduce each apparently "undesirable" scenario starting from explaining the internal operations of the Weblogic JSP Container, and apply the container expiration check algorithm (stale Checking Algorithm ). In addition, this article will also discuss the control of JSP and Servlet class overload parameters. This is strongly recommended for servers running in production mode.
JSP Container expiration Check Mechanism
In WebLogic, JSP is compiled into a. Class file. The term expiration check mechanism (stale checking mechanism) is used to determine whether a special JSP. Class file is older ("expired") than the current JSP file. The WebLogic JSP Container ensures that any JSP and its related files can be re-compiled only after modification. Viewing the generated Java code is a good way to understand the internal operations of JSP containers. We will use a JSP as an example, use the command line JSP compiler to compile it, and view the generated source code. The JSP compiler (weblogic. jspc) is provided along with the standard WebLogic Server Installation toolbox.
Consider a simple JSP page called foo. jsp:
A simple JSP page
Now we use the command line JSP compiler to compile this JSP and specify an option called keepgenerated. As the name suggests, this option generates corresponding Java code for the JSP page and saves the code on the disk.
java weblogic.jspc -keepgenerated -d ./WEB-INF/classes foo.jsp[jspc]warning: expected file /WEB-INF/web.xml not found,tag libraries cannot be resolved.<Jul 11, 2004 7:29:26 PM PDT> <Warning> <HTTP><BEA-101181> <Could not find web.xml under WEB-INF in the doc root: ..>
The compiler generates the. Java file and its corresponding. Class file in the output directory (-d) specified as the preceding option. It places the generated class file in the package called jsp_servlet, which happens to be the default JSP package prefix (unless in weblogic. ). Therefore, the generated Java file can be. /WEB-INF/classes/jsp_servlet found, and called _ Foo. java.
Note that we can ignore the warning sent by the compiler about the web. xml file not found, because the tag library is not actually used at this time.
In the generated code (_ Foo. Java), the most relevant part of our discussion is the staticisstale () method, as shown below.
Listing 1. staticisstale () method
public static boolean _staticIsStale(weblogic.servlet.jsp.StaleChecker sci) {if (sci.isResourceStale("/foo.jsp", 1089594167518L, "8.1.2.0","America/Los_Angeles"))return true;return false;}
The above Code clearly shows that the isresourcestale () method on the weblogic. servlet. jsp. stalechecker interface is called to determine whether the JSP has been modified. Parameters of the isresourcestale () method are as follows:
1. The resource to be checked, such as/Foo. jsp.
2. The timestamp (long integer) of the JSP page ).
3. WebLogic release build.
4. Default Time Zone of the current machine.
The JSP Container calls the _ staticisstale () method by implementing the stalechecker interface. This implementation receives a callback (isresourcestale () with the parameters shown in Listing 1 ()). With these parameters, this implementation can only receive all required information to infer whether a given resource has expired. When the resource (parameter 1)/Foo. when the JSP timestamp (parameter 2) is newer than the timestamp stored in the compiled class file (the parameter is greater), or when the release version is different, the JSP Container considers the JSP. class file "expired ".
Let's take a look at some of its important conclusions:
- Because the timestamp of the JSP page is stored in the class file and calculated during compilation, modifying the timestamp of the class file does not affect the expiration check process. (This is a very common absurd statement. I hope the above example will refute it clearly .)
- 4th parameters, that is, the time zone, are only used for deployment in the archive format (. War.
- WebLogic releases change with each service package, so you need to recompile all JSPs for each service package. This requirement is made to ensure that the JSP class can fix all compiler defects or change all JSP runtime in a newer service package or release version.
Static inclusion?
The next logical question people will ask is: Will JSP recompile the page even if only one static inclusion file of a specific JSP page is modified? The answer is yes. Even if a file such as static inclusion is modified, the entire page needs to be re-compiled (more suitable as a "compilation unit ). To learn how containers handle this dependency, consider the following JSP containing static inclusion files named Baz. Inc.
Listing 2. Foo. jsp
A simple jsp page.<%@ include file="baz.inc"%>
Listing 3. Baz. inc
--Simple Static Include--
Re-run the preceding command line for the JSP compiler Foo. jsp. Now a Java file is generated, which contains a small piece of interesting code as shown below. As you can see, each sub-object is processed using the _ staticisstale () method, so that even if a sub-object is modified (here it is Baz. INC), and re-compile the entire JSP or "compilation unit ". The JSP Container expects the Root JSP page (FOO. jsp) to return a Boolean value indicating whether it has expired. Therefore, each generated class file generates code to check all its related files.
public static boolean _staticIsStale(weblogic.servlet.jsp.StaleChecker sci) {if (sci.isResourceStale("/foo.jsp", 1089616972487L, "8.1.2.0", "America/Los_Angeles"))return true;if (sci.isResourceStale("/baz.inc", 1089616984268L, "8.1.2.0", "America/Los_Angeles"))return true;return false;}
In short, the Weblogic JSP Container allows each JSP. Class to maintain its own list of sub-objects and store the status (timestamp) of the original JSP (and its sub-objects) based on this list ). Container for JSP. class calls the _ staticisstale () method, and then JSP. class calls back the JSP Container and uses weblogic. servlet. JSP. stalechecker. isresourcestale () returns all information required to determine whether a single resource has expired. This greatly simplifies the expiration check task and eliminates the need to maintain a timestamp for each JSP at a single location.
Scenario where JSP is recompiled
We have analyzed some factors to consider when performing the expiration check on the JSP Container. Now, let's take a look at some common scenarios for re-compiling JSP:
1. Use a copy of the build script to modify the JSP timestamp. This may cause re-compilation of all JSPs.
Consider the scenario where all JSPs are located in the directory named SRC. Assume that a build script copies all JSPs and compiles the servlet Java file into the build directory. The script then runs weblogic. jspc on the src directory and puts all compiled JSPs into the build directory. Here, copying JSP to the build directory may change the JSP timestamp (unless the build script uses CP-P/-m to retain the file timestamp ). When the web application is deployed from the build directory to the server, all JSPs must be re-compiled, this is because JSP classes are compiled with JSP files older than the deployed JSP files (that is, the JSP files copied to the build directory. This is one of the most common re-compilation situations, which can be avoided by ensuring that the file timestamps are retained during the copy operation.
2. Modifying the packageprefix parameter of weblogic. XML will cause recompilation. The expiration check mechanism is used to find the packageprefix In the weblogic. xml file of a special web application and search for a class named <packageprefix>. _ Foo. Class for/Foo. jsp. Suppose we use weblogic. jspc to pre-build all JSPs, place them in the WEB-INF/classes directory of the Web application, and then deploy the web application archive (WAR) to the server. Suppose we have a JSP named Foo. jsp in this web application. In the absence of "packageprefix" in weblogic. XML, the expiration check mechanism will look for the jsp_servlet. _ Foo. Class class. Now let's modify weblogic. xml and add a package prefix, such as com. Bar, and redeploy the same war to the server. Access to foo. jsp will re-compile JSP, because the expiration check mechanism will look for the class named "com. Foo. _ Foo. Class. This problem can be avoided by ensuring that the-package parameter is used and the same package name is used when the weblogic. jspc command is called.
3. Modifying the workingdir parameter of weblogic. XML also causes re-compilation. In this case, in addition to the common web application class path, the JSP Container will also find the JSP class in the new "workingdir. Before the new version is deployed, the original directory contains JSP classes, but the JSP containers cannot find them. Therefore, the requested JSP will be re-compiled.
Note: scenarios 2 and 3 clearly explain that you need to re-build or pre-compile Web applications even when modifying weblogic. xml. Deploy the pre-compiled war after all the modifications to ensure that the JSP does not need to be re-compiled.
4. Assigning pre-built war components to an updated WebLogic Server will cause re-compilation of all JSPs. As described in the expiration check mechanism section, the JSP Container recompiles all JSPs when deploying JSP to servers of different versions. This is done to ensure that all JSP compilers/runtime enhancements or defects in a specific version or service package are available in the generated code (without this restriction, some classes may end the operation by referencing non-existent methods during JSP running ). Ideally, the pre-compiled JSP build script must use the same version of weblogic. jar as the deployed server. We recommend that you add all patches and periodic fixes used by the server to the class path used by the build script. In short, the build and deployment environments must be exactly the same. This avoids any unnecessary re-compilation issues after deployment.
Further control expiration check
When the container executes the expiration check, the control allows us to tune the container and make it run better, so it provides better response time for JSP and servlet. Each expiration check requires the JSP Container to go to the disk and re-read the last modification time for the specific JSP. When the call is too frequent, this process can cause performance degradation because it affects the JSP response time. Ideally, an expiration check is required to check that the development is very active, especially when the application changes frequently. By clicking refresh/reload on the browser and re-compiling the JSP Container and reload the new page, you can test the modifications made to the special JSP. However, in the production mode, the same practice may lead to lower performance.
The default values of the following parameters are the most suitable for the development mode. We recommend that you modify these values during deployment in the production environment.
Pagecheckseconds
For each new request of the compiled JSP, the container uses the configuration file (weblogic. XM) Check the value of pagecheckseconds. If the interval between the last expiration check and the current time is greater than that of pagecheckseconds, perform the expiration check. For example, assume that the value of pagecheckseconds is set to 10 seconds. For requests from Foo. jsp, the container performs a check to check whether the interval between the current time and the last expiration check is greater than pagecheckseconds. It is assumed that the page was re-compiled and accessed 10 seconds ago. The container will check whether the class has expired. During this interval, no request expiration check is performed for foo. jsp.
If the JSP page is not in development mode and does not need to be checked every 1 second (default), we strongly recommend that you change the parameter value to-1 (no expiration check will be performed) or change to a value like 60 seconds. This avoids calling file. lastmodified (), reloading the JSP class, and checking the timestamp every time you access JSP.
Listing 4. A code snippet from weblogic. xml shows how to set this parameter.
<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN""http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd"><weblogic-web-app><jsp-descriptor><jsp-param><param-name>pageCheckSeconds</param-name><param-value>10</param-value></jsp-param></jsp-descriptor></weblogic-web-app>
Note that for JSP in Web applications that never individually change the production environment, it is best to configure the container to never perform an expiration check on servlets and JSP.
Servlet-reload-check-secs
In development mode, when the servlet is modified and re-compiled to, say, the rapidly growing war's WEB-INF/classes directory, we expect the request to be executed from the browser, the container calls its latest servlet version. To address this issue, Weblogic's Web container checks every servlet-reload-check-secs interval to see if any file in the WEB-INF/classes has been modified. The default value of this parameter is 1 second. This is a good default value for developers who want to see the latest changes to the servlet class but do not need to redeploy the application. But before entering production, you must change this value to-1 (the Servlet File will never be reloaded ). In production mode, individual classes do not change. It is always the best choice to set the servlet-reload-check-secs value to-1.
Listing 5. weblogic. xml example where the servlet-reload-check-secs value is set to-1
<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN""http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd"><weblogic-web-app><container-descriptor><servlet-reload-check-secs>-1</servlet-reload-check-secs></container-descriptor></weblogic-web-app>
JSP Class Loader
We will end our discussion by checking how the WebLogic Server loads the JSP class. Each JSP is loaded in its own class loader (usually called a one-time class loader. This type of loader is a sub-loader of the Web application class loader, responsible for loading the relevant JSP class and its internal class (if any ). Curious readers may wonder why WebLogic loads JSP in each JSP class loader. Is it really complicated? WebLogic cannot simply use the application class loader to make life easier? All these questions are justified, and every person seeking for a WebLogic classloader Heaven should ask themselves these questions. To solve these problems, let's imagine that our web application has several JSP, a few Servlets, a filter, and hundreds of tags containing the tag processor class. Now, assume that all these classes are loaded into a single class loader. If you modify a single JSP and Click Reload on the browser, the following things will have to happen:
- The JSP Container will have to recompile the page.
- Will have to discard the whole Web application Class Loader used to load earlier class versions.
- You have to create a new Web application class loader and reload and reinitialize all servlets and JSPs (including the one you just modified ).
Java does not allow reuse of class loaders to reload new versions of classes. Instead, you have to discard the class loader and create a new one. For this reason, the above scenario is very out of date; even if only one class is changed, the application server has to overload a large number of classes.
Now, let's see how WebLogic implements its class loader solution. Consider the scenario mentioned above. If we modify JSP and happen to reload it in the browser, the server will execute the following tasks:
- Recompile the page for JSP containers.
- It discards the JSP class loader of the old version used to load the JSP class.
- It creates a new JSP class loader that uses the web application class loader as the parent loader and provides services for this page.
As you can see, only one JSP class must be reloaded. The whole web application Class Loader remains unchanged and is not affected by small changes made to JSP. Therefore, when modifying a single JSP, the container will discard the old class loader, recompile and only reload the class generated for this JSP. This avoids overloading or discarding the entire web application class loader. This is a big victory when only some JSP files in a special web application change frequently.
Conclusion
With these knowledge about the internal organization of JSP containers, we can not only avoid unpleasant and unnecessary re-Compilation of JSP containers, you can also use the pagecheckseconds and servlet-reload-checks-secs parameters to improve the page response time.
Source:
Http://dev2dev.bea.com/pub/a/2005/01/jsp_reloaded.html
Author Profile |
|
Nagesh Susarla is a senior software engineer at BEA Systems's WebLogic Server development team. He is dedicated to designing and implementing Servlet/jsp containers for WebLogic servers and is also a super fan of anlr (analyzer/generator. |