A walk through Jetspeed2
by Michael N. Lipp <mnl AT mnl DOT de>
In the past few weeks, I have bundled Jetspeed as an EAR] and got it eventually running in JBoss 3.2.7. This documents what I have learned about Jetspeed2 and haven't found in the existing documentation. "Haven't found" implies both "isn't there" and "is well hidden".
This page reflects the approach that I take trying to understand a project. There is some information about the the directory structure that presents itself after unpacking the source. This is followed by an outline of the major components, starting at something well known (the servlet, in my case), and eventually some information about the frameworks and libraries used (this may be a "read first", depending on your approach). Finally there are some miscellaneous issues that didn't fit in the categories above.
Jetspeed2 is a maven based project with several sub-projects, organized in subdirectores of the project tree1.
This "main subproject" is the Jetspeed2 container.
- This directory contains several sub-projects. Each sub-projects provides a portlet application that can be deployed in the Jetspeed2 portlet container. The more important (because tightly connected with Jetspeed2) portlets applications are
- This is a portlet application for managing the security component of Jetspeed2.
[And some more I haven't found out enough about yet to describe them.]
The Jetspeed2 container
The Jetspeed2 container is a servlet (org.apache.jetspeed.engine.JetspeedServlet). The servlet uses three initialization parameters (defined in "web.xml") that point to more configuration information2
"properties" --> "WEB-INF/conf/jetspeed.properties"
"configuration" --> "WEB-INF/conf/jetspeed-configuration.xml"
"applicationRoot" --> "webContext" (reserved word, eventually maps to servlet context's real path)
"engine" --> "org.apache.jetspeed.engine.SpringEngine"
The actual work is done by an engine. The default engine is based on the Spring Application Framework. The available components are configured by the "*.xml" files in "WEB-INF/assembly". ComponentManager lists a subset of the components needed by the default engine. Some more components are described on the pluggable engine components page. The security service related component configuration files are described in the official documentation.
Requests are passed through a pipeline component ("jetspeed-pipeline" by default). Pipelines are defined in "WEB-INF/assembly/pipelines.xml". Pipelines consist of valves that process the request.
Frameworks and libraries
The Spring Application Framework is based on the old concept of JavaBeans3. Roughly speaking, Spring provides a means to configure a bean factory, usually with an XML file. For every JavaBean you can specify the properties, either as sacalars or by linking to other bean configurations. At runtime, the bean factory can be queried for instances of the configured JavaBeans, which are usually -- though not necessarily -- created by the framework as singletons (resolving all links to referenced JavaBeans). In addition, Spring provides AOP features that enable you to modify the behaviour of existing JavaBeans without writing code.
This concept is complemented with a rich library of components, many of which provide "wrappers" around components from other frameworks.
Persistence is provided by the ObJectRelationalBridge which is a lesser known alternative to Hibernate (Google provides some references to comparisons). The OJB framework is wrapped by some Spring components. Regrettably these components have not been documented yet. Some general remarks about O/R wrappers are applicable to OJB as well.
The DAOs configured in Jetspeed are
<bean id="portletRegistryImpl" ... />
<bean id="portletEntityAccessImpl" ... />
<bean id="capabilitiesImpl" ... />
<bean id="PreferencesProviderImpl" ... />
<bean id="PropertyManagerImpl" ... />
Each DAO bean has a property that is set to the name of the file that specifies the O/R mapping.
The implementation classes of these beans implement the "top-level access" to the persistet objects (i.e. the queries). The DAOs are augmented with the transaction aspect, a proxy bean that monitors the access to the DAO's methods. Only the respective proxy (with a more intuitive name e.g. <bean name="portletRegistry" ... />) bean is used in the program.
A portal that is designed to run in the J2EE environment and that supports portlets has an ambivalent nature. On the one hand side, it is a component in the J2EE environment. On the other hand side, it is a container for components (the portlets).
Deployment as "Container Extension"
The container nature implies the temptation to install the portal as an "extension" of the application server. Using JSR-168 portlets, this approach is backed by the fact that portlet applications are themselves web applications. And as you don't want to re-implement a web container as part of your portal implementation, you want the use the existing web container to run the portlet applications4.
Using the default web container to run the portlet application requires that different web applications (the portal and the portlet applications) interact5. And it requires that the web application container and the portal (web) application exchange information about the portlet applications deployed.
Jetspeed2's standard approach is to actively deploy the portlet applications. The "default" Jetspeed2 installation is an "exploded" war in the deployment directory of the Web server or application server (e.g. Tomcat ".../webapps/" and JBoss ".../server/default/deploy/"). Within its directory structure, Jetspeed2 has a "watched" directory for portlet applications. Whenever you drop a portlet application in the ".../jetspeed2.war/WEB-INF/deploy/" directory, it will be deployed in the web container and the portlets will be made available in the portal (this emphasizes the "container extension" character).
It would be nice if you could rely on JSR-88 being implemented and use this API for deployment. Currently, however, Jetspeed2 has to resort to using an application server specific component that is configured as bean "org.apache.jetspeed.tools.pamanager.servletcontainer.ApplicationServerManager" in "jetspeed-spring.xml".
The current (M3) Jetspeed2 binary distribution takes this deployment approach even further. It doesn't present Jetspeed2 portal as a component that you integrate. Rather it provides a fully configured, stand-alone portal application that you can use to run your portlet applications.
Deployment as Web Application within an EAR
I think you cannot be sure about the J2EE compliance of a module unless you have successfully packaged it as a module in an EAR -- and then you have to successfully deploy this EAR in at least two application servers. Jetspeed2 presents several challenges in this respect.
First, JBoss 3.2.x and 4.0.1 use by default a common repository for all classes loaded during the deployment of an EAR. This is not compliant with the servlet specification that prescribes individual classloaders for each WAR with libraries in WAR/WEB-INF/lib/ (and classes in WAR/WEB-INF/classes/) taking precedence over classes found elsewhere. Starting with JBoss 4.0.2 the specified behaviour has been made the default.
While using a common repository has its advantages, it breaks Jetspeed2. There may be several problems, but I can only report about the first I have encountered (and that made me change the JBoss 3.2.7 behaviour to the "WAR first" behaviour). The problem is that Velocity uses a singleton pattern for its engine. This singleton is initially configured with the path information required to find the templates. And this is, of course, done by the first WAR that uses Velocity being deployed. Subsequently deployed WARs that also want to use Velocity find an already configured Velocity engine and skip the configuration, so the information about the additionally available templates is lost. Using individual classloaders for the WARs and putting the velocity.jar in each WAR/WEB-INF/lib fixes this because now every WAR gets its own singleton6.
[To be continued.]
If --like me -- you don't use maven for your projects here's how it works in a nutshell: every "project.xml" in the tree defines a project. There isn't much information in these files. That's because maven takes "project.xml" mainly as a "sub-project tree marker". It then takes into consideration the layout of the subtree (e.g. if there is a "WEB-INF/" directory, the sub-project obviously results in a WAR) and acts accordingly. "project.properties" supplies some additional information (though I haven't understood yet why this information isn't put in project.xml as well) and "maven.xml" only provides invocable "goals". This approach is the reason why all maven based projects have this rigid, fancy layout. (1)
IMHO this is a bit of over-configurability that makes things hard to use. The names of the configuration files could have been hard-coded. (2)
Of course, there is no reason why "old" should be "bad". But sometimes I get the feeling that JavaBeans is the highest level of abstraction that has really been comprehended by the average developer. It's a bit like the saying that the telephone is the only technical equipement that truly everybody can operate -- so if you want your interface to be easily usable, make it look like a telephone. (3)
Now this would be a good example for a re-usable component: a web container component that can be used to both implement a web applictaion container and a portlet container within a web application. (4)
This is why the "crossContext" attribute in Tomcat must be set to run portals that use this approach. (5)
Of course, this remains a nuicance because the velocity classes have to be deployed as many times as there are WARs that use Velocity. This requires both memory and startup time, but it can only be fixed by the velocity project. (6)