Tuesday, January 17, 2006

Using Maven 2, Part 5: Payoff at Last

****************************************************************
Maven 2 Notes, Part 5: Portlet Plugins
****************************************************************

----------------------------------------------------------------
Section 1: Portlet Plugin Use Cases
----------------------------------------------------------------

* My overarching goal in these notes has been to develop a method
for making Grid Portals easier to assemble from remote repositories.
This really amounts to downloading pre-compiled WAR files and putting them
into a Tomcat container.

* That said, we have the following basic problem to solve: a particular
Grid Portlet WAR file may require other steps to occur:
o The portlet depends on another portlet (ie proxy manager is required for job submission).
o The portlet depends on various jars being placed into Tomcat's common/lib or shared/lib.
o The portlet depends upon particular modifications and patches being applied to the container.
o The portlet depends upon additional services, which may need to be installed at the same time.

* To be clear, I use "dependency" in a looser sense than normally in Maven: I just mean that
a particular portlet won't work at _runtime_ without some other pieces. I don't necessarily
mean that the dependent portlet requires the other code for _compilation_.

* An example of the third case is a portlet that depends on an extension to the GridSphere or
Jetspeed2 authentication mechanism.

* Examples of the fourth case include portlets that work with Sakai collaboration services. I single these
out because they can be installed also with Maven, so it will be an "intermediate" use case that
we will tackle in the future.

----------------------------------------------------------------
Section 2: Use Case #1: Downloading Dependent Portlets
----------------------------------------------------------------
* We assume the following: Portlet B depends upon Portlet A also being installed.
Both portlets are compiled as WAR files and are available from a remote repository. We
download the POM file for Portlet B.

* Maven basically gives us two ways of going this:
o Dependencies
o Inheritance

* These are not exclusive and in fact it may be good to combine them.

* Let's build this up. We will do the following steps:
1. Make a proxymanager-portlet.war portlet and deploy it to a remote repository.
2. Make a gridftp-portlet.war portlet and deploy it to a remote repository.
3. Make a generic-grid-portlet.war and deploy it also to the repo.
4. Finally, make a pom.xml for gridftp-portlet-download that inherits from generic-grid-portlet.

Note step 3 creates a war, but we are really only interested in deploying its pom.xml file to the
repo.

Note also we need to distinguish between #2 and #4. #2 is done by the developer of the portlet, while
#4 is done by the downloader of the portlet. Note also that only Step 4 requires the plugin that
we developed in the previous sections of this report. However, we will inherit it through a generic
parent (#3).

* Here is the pom.xml for the straw proxymanager-portlet:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>xportlets.proxymanager</groupId>
<artifactId>proxymanager-portlet</artifactId>
<packaging>war</packaging>
<version>2.0</version>
<name>Maven Webapp Archetype</name>
<url>http://maven.apache.org</url>
<build>
<finalName>proxymanager-portlet</finalName>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<id>Test</id>
<url>file:///tmp/Test/</url>
</repository>
</distributionManagement>
</project>

The main thing to note is that we are using our own "internal" (not to be confused with "local") repository
to stow our proxymanager-portlet.war.

We deploy this in the usual way:

[shell-prompt> mvn deploy

* Do the same for the straw gridftp-portlet. It should have a very similar pom.xml.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>xportlets.gridftp</groupId>
<artifactId>gridftp-portlet</artifactId>
<packaging>war</packaging>
<version>2.0</version>
<name>Maven Webapp Archetype</name>
<url>http://maven.apache.org</url>
<build>
<finalName>gridftp-portlet</finalName>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<id>Test</id>
<url>file:///tmp/Test/</url>
</repository>
</distributionManagement>
</project>

* Here is the pom.xml for the the generic-grid-portlet:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>ogce.portlets</groupId>
<artifactId>GenericGridPortlet</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<name>Maven Webapp Archetype</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>blah</id>
<url>file:///tmp/Test/</url>
</repository>
</repositories>
<build>
<finalName>generic-grid-portlet</finalName>
<plugins>
<plugin>
<artifactId>PortletAppPlugin</artifactId>
<groupId>test.plugin</groupId>
<executions>
<execution>
<phase>process-resources</phase>
<configuration>
<destinationDir>
/tmp/
</destinationDir>
</configuration>
<goals>
<goal>portlet-war-deploy</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Note this is a war! -->
<dependency>
<groupId>xportlets.proxymanager</groupId>
<artifactId>proxymanager-portlet</artifactId>
<version>2.0</version>
<type>war</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>

<distributionManagement>
<repository>
<id>Test</id>
<url>file:///tmp/Test/</url>
</repository>
</distributionManagement>

</project>


Note again that we specify the internal repository for both reading (top) and writing (bottom). These don't
really need to be the same. Also, we specify the proxymanager-portlet.war file as being a dependency. Again,
this "dependency" is not for compilation and classpath, but just to force the download of this war into the
local ($HOME/.m2) repository.

We also deploy this in the usual way:

[shell-prompt> cd $YOUR_TESTS/GenericGridPortlet
[shell-prompt> mvn deploy

By deploying this portlet, we stick its pom.xml in the repository. Keep reading.

* Now, finally, we can create our distribution POM. It is shown below. Note that it inherits
several things from the GenericGridPortlet parent: the dependency on proxymanager-portlet.war,
the PortletAppPlugin, and so on.

* On the other hand, we do need to specify the internal repository to bootstrap everything.

* Note also that we ONLY have to distribute the following pom.xml file. It will download
anything it needs from the specified repositories, including the GenericGridPortlet pom.xml.


<project>
<parent>
<artifactId>GenericGridPortlet</artifactId>
<groupId>ogce.portlets</groupId>
<version>1.0</version>
</parent>
<!-- Must specify this to bootstrap things. -->
<repositories>
<repository>
<id>blah</id>
<url>file:///tmp/Test/</url>
</repository>
</repositories>
<modelVersion>4.0.0</modelVersion>
<groupId>xportlets.gridftp</groupId>
<artifactId>gridftp-downloader</artifactId>
<packaging>war</packaging>
<version>2.0</version>
<name>Maven Webapp Archetype</name>
<url>http://maven.apache.org</url>
<build>
<finalName>gridftp-downloader</finalName>
</build>
<dependencies>
<dependency>
<groupId>xportlets.gridftp</groupId>
<artifactId>gridftp-portlet</artifactId>
<version>2.0</version>
<type>war</type>
</dependency>
</dependencies>
</project>


* If you run this portlet, the following output is generated:

[gateway@gridfarm002 gridftp-downloader]$ mvn process-resources
[INFO] Scanning for projects...
Downloading: file:///tmp/Test//ogce/portlets/GenericGridPortlet/1.0/GenericGridPortlet-1.0.pom
1K downloaded
[INFO] ----------------------------------------------------------------------------
[INFO] Building Maven Webapp Archetype
[INFO] task-segment: [process-resources]
[INFO] ----------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [PortletAppPlugin:portlet-war-deploy {execution: default}]
[INFO] Hollow World!
Sample Param: 2.0
junit
/home/gateway/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar
/tmp/junit-3.8.1.jar
121070
xportlets.proxymanager
/home/gateway/.m2/repository/xportlets/proxymanager/proxymanager-portlet/2.0/proxymanager-portlet-2.0.war
/tmp/proxymanager-portlet-2.0.war
112097
xportlets.gridftp
/home/gateway/.m2/repository/xportlets/gridftp/gridftp-portlet/2.0/gridftp-portlet-2.0.war
/tmp/gridftp-portlet-2.0.war
3386
[INFO] ----------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ----------------------------------------------------------------------------
[INFO] Total time: 1 second
[INFO] Finished at: Tue Jan 17 14:01:29 EST 2006
[INFO] Final Memory: 2M/4M
[INFO] ----------------------------------------------------------------------------


* This output is somewhat obscure, but it basically means that everything worked correctly: the
gridftp-downloader project pulled down the GenericGridPortlet's pom.xml file. It then downloaded
gridftp-portlet.war, as was specified explicitly in its dependencies. It also downloaded the
proxymanager-portlet.war file, which is a dependency that it inherits from GenericGridPortlet.
Finally, it ran the PortletAppPlugin (also inherited from GenericGridPorltet).

* So in summary, this can be used to download portlet WAR files and their supporting WARs.

----------------------------------------------------------------
Section 3: Use Case #2: External jar dependencies
----------------------------------------------------------------
* It is often the case that our portlets must depend on jars that do not get included in the
WAR bundle because they will be placed in Tomcat's common/lib/ or shared/lib/ directory. In the
OGCE project, the COG Jars needed to work with Grid resources are the main example.

* This is actually a more straightforward application of Maven. Our gridftp-downloader's pom.xml POM
shown above actually does not need to change at all. The GenericGridPortlet's pom.xml may contain
all of the CoG jar dependencies, or it may itself inherit this from GrandfatherCOG, yet another pom.xml.

* The actual steps needed to download the copy the COG jars from the repo to the Tomcat /shared/lib
directory can be easily based on our PortletAppPlugin code.

----------------------------------------------------------------
Section 4: Use Cases #3 and #4: Answer unclear, ask again later.
----------------------------------------------------------------
* These require a bit more thought, so I'll describe them in follow up posts.

No comments: