Web Workflow in IDEA 7
- Damodar Chetty
- Mar 05, 2008
- Mar 10, 2008 (last updated)
In this article, I'm going to expand on some of the concepts I've touched on previously in relation to web applications and IDEA, but this time I'm going to document my experiences with setting up a production web application. The problem with real world applications is that they very rarely fit as neatly into articles as do the toy projects most books describe. In particular, the real world imposes a number of constraints that often aren't considered. In my case, the biggest constraint I had, was that I had a working Eclipse project that relied on a given folder structure, and I didn't want to jeopardize that in any way. So what if I'm having a fling with IDEA? I still want to be able to return to my main development environment in case things don't work out as hoped.
Another constraint is that my real world application comprises over 12,000 classes that support Swing applications and multiple web applications, all supported on a bed of services and utility classes (both homegrown and third party). And, for historical reasons, there isn't a clean enough separation between the different modules.
And, for those new to my articles, my major requirements during typical web development, are for my development environment to ensure that (a) a change I make to a Java file is automatically copied out to the correct location under WEB-INF\classes, and then automatically picked up the container; and to (b) an edit to a JSP file, automatically causes the associated servlet to be re-generated, and recompiled, so that refreshing the page displays the updated version.
I'm a big fan of the hot swap ability, where a class definition can be reloaded without having to restart the Tomcat container, or having Tomcat reload the web application context - which would cause my session to be lost.
While IDEA will accomplish (b) without much coaxing, the closest it gets to (a) is by having you explicitly compile the source file, which it will then automatically deploy without reloading the entire web application context.
Opinions among my friends were pretty evenly divided as to whether or not they preferred automatic deployment of changes. I myself prefer the automatic deployment, after all, if I made the change, then I expected to use it, didn't I? And, if I don't like the result, I can always revert to the original version using Local Versioning support. But hey, live and let live, and all that.
In order to understand web development and deployment under IDEA, it is easiest begin with the folders that IDEA expects to use.
The following locations are generic to all Java modules:
The Content Root
A content root specifies the top level folder under which your module's files (sources, test sources, output folders, resources, Ant builds, etc.) live. A module can have multiple content roots - with the only caveat that two content roots should not intersect, i.e., should not share folders. All the subfolders under a given content root will be listed for you, and you can mark each (using a color coded scheme) as being either a source folder, a test source folder, or whether it should be excluded from consideration.
The Source Folder(s)
and Test Source Folder(s)
These are the folders that you explicitly mark as containing Java source files, property files, etc.
The Compiler Output
This is the folder to which IDEA will copy compiled class files as well as project resources when you request it to build your project.
The next few locations are specific to Web Modules or Facets:
The Location of the
You specify the full path name to your deployment descriptor. This is the web.xml that you expect to deploy with your application, and so will contain all your application specific configuration elements.
The web.xml file that you specify here is copied to a WEB-INF folder within your exploded folder's root (see below for more details).
The Location of your
This is the full path name to your context fragment (context.xml). With Tomcat 5.x, I need to explicitly mark my context as not reloadable, else Tomcat's auto reload mechanism tends to interfere with IDEA's hot swap functionality.
Note that a context fragment is an XML file, in Tomcat, that describes your Web application’s document root (where the files are physically located), and its path (the context root used to access the Web app). Edit the file to ensure that the reloadable attribute is set to False. This ensures that Tomcat does not try to reload the web application context when it detects changes in the folders that it monitors. In other words, we'll let IDEA drive the reload process. A typical context fragment may look like this:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/" debug="0" privileged="true" reloadable="false" caseSensitive="false" />
You create one using the Application Server Specific Descriptor button on the Web Settings tab.
The Web Resource
This is the folder that contains all your static web content. I.e., your *.html, *.js, *.css, *.xml, etc. files will live somewhere under this folder.
My JSP files live in a folder under here, as well. Even though these are "sources", you should not add this folder as a Source folder entry in your general module.
Adding a JSP folder as a source has the unfortunate effect of having all the JSPs being considered to be resource files by the build process, which will then copy these out to WEB-INF\classes, not quite the desired effect.
You must also specify a "Path Relative to Deployment Root" which maps your web resource directory to a particular folder tree in your exploded/unzipped WAR directory. I.e., I have this set to "/", meaning that the contents of my Web resource folder will be copied out to the root of the exploded WAR directory.
This is really one of the best features of IDEA. You can assemble your exploded WAR folder with content from various disparate folders. For e.g., if your JSP files live under a different folder structure, you can set it up so that each of those folders is mapped to the appropriate location under the exploded WAR folder. This is a flexible way to map "source-folder" ==> "target subfolder under exploded folder", with "/" signifying the exploded folder itself. It is crucial that you understand how this works because it makes it a breeze for you to assemble your deployed application.
The Location into
which your WAR file will be unzipped
This is the location which represents your application to Tomcat, i.e., it maps to %TOMCAT_HOME%/webapps, using the context root that you specify (in my case this was "/" for the ROOT context).
This is one busy folder since it represents your deployed application. It is the target folder into which all the following will be copied:
o Compiled classes from your compiler output path, or from other module dependencies, will be copied into a WEB-INF\classes created under this location.
o JARs that your web application needs (your dependencies list) will be copied into a WEB-INF\lib created under this location.
o web.xml from the specified location will be copied into a WEB-INF created under this location.
o All static content as well as all your JSPs.
The work location
for your compiled JSPs
When a JSP is accessed for the first time, it must be converted into a servlet, and compiled. The compiled servlet is located (in Windows XP), under C:\Documents and Settings\chetds\.IntelliJIdea70\system\tomcat_Unnamed_xxxxxx\work\Catalina\localhost\_\org\apache\jsp, where the xxxxxx is replaced by your module name concatenated with a hex string.
The interesting thing is that these folders need not be mutually exclusive. In my particular case, my web resource directory not only contains my static content and JSP files, but is also my exploded WAR folder, and contains my context fragment, and my deployment descriptor.
As a first step when setting up your project, make sure that you have these folders identified for your specific environment.
I'd like to point out that every developer I've met has their own ideas about how projects should be setup, and given that most IDEs are very accommodating in terms of folder structure, there's often multiple ways to skin this cat. I detail the selections that worked for me, but feel free to experiment.
This particular project uses Tomcat 5.5.20 and IDEA 7.0.2, and has its folders set up as follows:
1. Java sources found under d:\dev; with the he actual code under d:\dev\src, tests under d:\dev\tests.
My JSPs are found in subfolders of d:\dev\WebContent\jsps; the deployment descriptor is in d:\dev\WebContent\WEB-INF, and compiled classes/JARs are in the usual places under WEB-INF.
To start with, I created a general Java module with the following properties:
§ Content Root: d:\dev
§ Source/Test Source Folders: d:\dev\src, d:\dev\tests, and d:\dev\WebContent\jsps
§ Compiler Output Folder: d:\dev\deploy\classes
§ Location of the web.xml file: d:\dev\WebContent\WEB-INF\web.xml
§ Location of your context fragment: d:\dev\ROOT\META-INF\context.xml
§ Web Resource Directory: d:\dev\WebContent
§ Location into which your WAR file will be unzipped: d:\dev\WebContent
§ The work location for your compiled JSPs: Automatically generated by IDEA.
In this final section, we'll follow the steps that are taken when you choose to rebuild your project.
1. First, all your sources are compiled and copied to the specified compiler output path (d:\dev\deploy\classes).
2. Next, your test sources are compiled and copied over to your compiler's output path.
3. Then your resource files are copied over to the compiler's output path.
4. Then, all compiled classes and JARs are copied over to the exploded module directory. I.e., d:\dev\WebContent\WEB-INF's classes or lib. Since my static content folder is the same as my exploded module directory, IDEA does not need to copy the static content and JSPs. Else these have to be copied too.
up your web application in Debug mode.
Your Run/Debug configuration's Deployment tab's left box describes the Web facets in your project, with the module associated with that facet specified in parentheses. The Deployment Source drop down on the right indicates the final leaf subfolder of your module's exploded WAR folder (in our case WebContent for d:\dev\WebContent).
try editing a Java source file, and then use the Build > "Compile
filename.java" option (Ctrl+Shift+F9). This will start off the compile,
and when its done will show the following message in the status bar:
Now, verify that this has happened by exercising your web application.
To summarize this action ... when you edit a source file, you are editing it in your source tree. When you compile it, the compiled .class file ends up in the folder specified in your module's compiler output path setting (here, d:\dev\deploy\classes). From there, it ends up in the appropriate location within the web module's exploded WAR folder (i.e., in a subfolder of WEB-INF\classes).
edit a JSP and save it. Refresh your page in the browser, to see your changes.
To summarize this flow ... when you edit a JSP file, you are editing it in your source tree (in my case, this is actually registered as a source folder for my Java module). If this is the same location as your exploded WAR file, no copy occurs. Else, the JSP must first be copied into the exploded WAR folder. Finally, when you request the page in your browser, a check against the timestamps will cause the servlet for the JSP to be regenerated and compiled. The compiled servlet will then service your request.
An important tip when using IDEA and Tomcat, is that in the Debug Window, ensure that you click the red Close icon, rather than the Stop icon . Then, make sure that you terminate the container process after disconnection. Else, you'll start seeing tons of processes called "java" in your Windows Task Manager.
Please note that all the information here comes from trial and error - the only IDEA book I could find is at least a couple of versions old, and the IDEA help documentation needs a serious overhaul. E.g., it kept referring to a "Synchronize exploded directory" checkbox that I could not find no matter how hard I looked. If you have any comments/corrections on this text, give me a shout and I'll update it accordingly.
- x - x - x - x - x - Fin - x - x - x - x - x -