main website home
  • About this blog

    This blog features updates, opinions, and technical notes from Caucho engineers about Caucho products, the enterprise Java industry, and PHP. Caucho Technology is the creator of the Resin Application Server and the Quercus PHP in Java engine. A leader in Java performance since 1998, Caucho is a Sun JavaEE licensee with over 9000 customers worldwide.
  • Tags

    ajaxworld bam candi cdi cloud cluster comet configuration deploy devoxx eclipse ejb embedded flash flex google app engine hessian hmtp ioc java ee 6 javaone javazone jms messaging newsletter nyjug osgi php pomegranate quercus resin resin 4.0 REST servlet sfjug silicon valley code camp spring testing training tssjs watchdog webbeans web profile websockets wordpress
  • Meta

    • Register
    • Log in
    • Entries RSS
    • Comments RSS
    • WordPress.org
« App Server Cloud Architectures
Resin Eclipse plugin snapshot »

pomegranate draft

This is a very rough draft of a pomegranate specification. It should contain the main details needed for an early implementation.

Overview

Pomegranate containers provide a classloader-based module system for maven-based libraries stored as jar files in a repository. Servlet containers can use pomegranate as an extension to the WEB-INF/lib with the following benefits:

  • Shared .jar files in a common repository, simplifying management and reducing .war sizes
  • Library dependency resolution, including the ability to handle sub-module incompatibilities
  • Familiar Maven pom.xml files, to take advantage of current development practices.
  • Optional integration with Servlet web-app containers
  • Optional integration with Java CanDI (JSR-299) managers

Copyright

Copyright 2009 Caucho Technology, all rights reserved.

[This is a placeholder until I find an appropriate license. We want to let anyone implement Pomegranate, and enforce compatibility with a TCK.]

Terms

  • “container” - the framework implementing pomegranate
  • “jar” - the byte stream representing a list of files
  • “module” - a classloader comprising of a jar contents, a parent, and a set of module imports, i.e. the deployment of a jar as a classloader in the container
  • “module identifier” - the <org,name,version> tuple
  • “base module” - the pair <org,name> identifying a module/jar without specifying the version
  • “repository” - the container’s set of jars
  • “visibility” - the classes and resources loadable from a Java class/classloader

module visibility

The visible classes and resources for a module are those resources loadable by the Class.forName, or methods on the module’s ClassLoader.

A pomegranate module’s classloader adds a set of imported modules to the usual JDK class lookup. The imports are visible to the module.

Visibility is not transitive. If module “gray” imports module “blue” and module “blue” imports module “green”, the classes from “green” are not visible to module “gray”. Because visibility is not transitive, two imported modules may themselves import different versions of the same library without conflicting.

Like the standard JDK classloader, parent classloaders have priority. The search order for classes and resources is as follows:

  1. parent
  2. imported modules (sorted by module id)
  3. the module’s jar contents

This order follows the JDKs order because the classloaders need to avoid ClassCastExceptions.

All pomegranate modules in a cloud share the same parent classloader. For example, in a Servlet web-app, the modules parents will all be the parent classloader of the web-app’s classloader, i.e. all modules and the web-app will be peers in the classloader cloud.

Modules are uniquely identified by their <org,name,version> tuple, i.e. the module identifier.

If id(m1) == id(m2), then m1 == m2

In other words, the container will never have two classloader/modules with the same identifier at the same time.

jar repository

The container’s jar repository is a set of jars, each identified by the module-identifier and containing a Maven pom.xml to declare its direct dependencies.

Pomegranate does not specify the structure of the jar repository or the name of the jar files. Containers are free to implement the repository as they wish. For example, a container might pick up all jars in a ${home}/project-jars directory, or it might store the jars in a database or in a .git repository.

Pomegranate only requires that each jar be uniquely identified by the module id, and have a pom.xml in a META-INF/maven directory to uniquely identify it.

module identifiers

Modules and jars are uniquely identified by their module identifier tuple: <org,name,version>.

  • org - the owning organization for the module in Java package syntax, e.g. “com.caucho”
  • name - the module’s name given by the organization, e.g. “resin”.
  • version - the module’s 4-part version, e.g. “4.0.1.snap”

module versions

Module versions follow Maven conventions. Each version has 4 parts:

  • integer major version
  • integer minor version
  • integer micro version
  • optional string qualifier

Versions are ordered.

Version Ranges for Dependencies

Module imports specify a range of allowed versions. In general, the highest available version in the repository that satisfies all the dependency constraints is used in resolution.

Pomegranate follows Maven syntax for declaring dependencies:

  • “[min,max]” - all versions between min and max inclusive
  • “[min," - versions greater or equal to min
  • "(min," - versions strictly grater than min
  • ",max]” - versions less than or equal to max
  • “,max)” - versions strictly less than max
  • “[version]” - exactly the named version
  • no version - match any version in the jar repository

For compatibility with older, the unadorned version “version” is a best-effort match for “[version,".

Maven pom.xml files

Pomegranate reads the pom.xml file in a jar's META-INF/maven directory.

Pomegranate uses two pieces of the Maven pom.xml file and ignores the rest. It uses the <groupId>, <artifactId> and <version> for the org,name, and version. And it uses the <dependencies> section for dependencies. An example might look like:

<project xmlns="http://maven.apache.org/POM/4.0.0">
<groupId>com.foo</groupId>
<artifactId>gray</artifactId>
<version>1.0.0</version>

<dependencies>
<dependency>
<groupId>com.foo</groupId>
<artifactId>red</artifactId>
<version>[2.0,</version>
</dependency>

<dependency>
<groupId>com.foo</groupId>
<artifactId>blue</artifactId>
</dependency>
</dependencies>
</project>

module resolution

Resolution is the container's process of selecting jars from the repository, and creating the import graph. It's defined as a set of constraints on allowed versions. Essentially, there are two rules: declared dependencies in the pom.xml files, and implied constraints from peer modules.

Each jar declares its imports in the pom.xml <dependencies> section. The <version> tag declares the allowed set of jar files. These imports and their version ranges form the primary constraints.

Peer modules can also create a constraint. If a module imports the same base module as a peer, the version must be the same. For example, if "gray" imports "green-1.2", "gray" imports "blue" and "blue" imports "green" with a "[1.0," range, then "blue" will also import "green-1.2". If the version conflict, the container should throw an error.

Optional: Servlet web-app integration

Servlet containers which implement pomegranate need to integrate the web-app's classloader into the pomegranate cloud, and also let servlet users to specify their dependencies in a pom.xml.

The web-app classloader is a peer to the pomegranate modules. It will have an import list just like any other module. The classloader parent of the web-app classloader is the classloader parent of all pomegranate modules in the web-app. In other words, the servlet container's classes are available to all modules and take precedence, just as in normal JDK classloading.

The web-app may contain an optional WEB-INF/pom.xml declaring the web-app's own dependencies.

[WEB-INF/lib/pom.xml has been suggested as an alternative, with the reasoning that WEB-INF/lib already declares jar dependencies. I'm currently leaving it in WEB-INF/pom.xml because WEB-INF/classes would also use pom.xml dependencies, so it seems logical to me to use a common directory.]

Optional: Java CanDI (JSR-299) integration

Pomegranate containers which also implement Java CanDI need extra structure from the JSR-299 spec to properly handle the visibility graph provided by Pomegranate. The following structure is recommended

  • Each pomegranate module/classloader has its own BeanManager associated with it.
  • The module’s BeanManager returns and resolves all Beans visible to the module.
  • No extra ordering or priority is associated with imported Beans. All Beans are equal (as defined by JSR-299).
  • Some beans may be marked @ModulePrivate and are only visible to the declaring module (e.g. the BeanManager itself!)

In the above diagram, any CanDI bean in the “gray” module could inject a BlueBean or a RedBean, but would not inject any GreenBean.

Each of the “gray”, “blue”, “red”, and “green” modules would have a distinct BeanManager. BlueBean would inject the “blue” BeanManager, and could inject a BlueBean or GreenBean, but not a RedBean or GrayBean.

Tags: pomegranate

This entry was posted on Wednesday, August 5th, 2009 at 1:30 pm and is filed under Engineering. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

3 Responses to “pomegranate draft”

  1. lehphyro Says:
    August 6th, 2009 at 7:53 am

    The webapp jars already have pom.xml file inside them, why do you need a WEB-INF/pom.xml?

  2. ferg Says:
    August 6th, 2009 at 9:59 am

    The webapp might have extra dependencies beyond the jars in WEB-INF/lib. (The WEB-INF/pom.xml is optional, by the way.)

    Consider the extreme case where WEB-INF/lib and WEB-INF/classes are empty, and all jars are loaded as pomegranate modules.

  3. horacyoliwka Says:
    November 2nd, 2009 at 1:16 pm

    This is all very neat, but I have one question left:

    I have a working maven project, with all dependencies resolved. I create a web-app jar without the dependency jars, and a set of jar’s for this project. all by maven.
    I want to deploy it on a server with resin 4.0.1
    I’ve put the set of dependency jars to resin’s project-jars/ directory.
    Then I copy the WAR to resin’s depoly directory, resin spots it and its WEB-INF/pom.xml and tries to resolve dependencies; unfortunately it cannot find any artifacts in its
    repository.

    I suppose this is because all the jars I have put there do not have
    META-INF/maven…pom.xml files packed in them. They are just normal jars
    like those downloaded by maven.
    What should I do?
    Should I somehow generate jars with pom.xml files inside? Or should I
    manually copy the foo-bar-1.0.pom files from the repository on my devel
    machine to the resin’s project-jars directory?

    Thank You for a response,

    Horacy

Leave a Reply

You must be logged in to post a comment.


Caucho Technology is proudly powered by WordPress and Quercus®
Entries (RSS) and Comments (RSS).

  • HOME |
  • CONTACT US |
  • DOCUMENTATION |
  • BLOG |
  • WIKI 4 |
  • WIKI 3 |
  • Resin: Java Application Server
Copyright (c) 1998-2012 Caucho Technology, Inc. All rights reserved.
caucho® , resin® and quercus® are registered trademarks of Caucho Technology, Inc.
resin® is a cloud optimized, java® application server that supports the java ee webprofile ®