jst j2ee
flexible project concepts
 
Introduction
 

The following document provides an overview of the direction that the J2EE Tools team is going with the design and implementation of the Flexible Project Structure support and API. The document will discuss the following:

 
  • the metamodel used to understand project structures,

  • the XML format used to store the model within each project,

  • a high level overview of the important components in the API,

  • how the modules are prepared for deployment, and

  • how modules are announced to the server.

 

The design of the flexible project structure as enumerated by this document does not meet some of the initial suggestions due to constraints within the base Eclipse platform. For instance, only one server target is supported at a time (but users may switch the server target easily and recompile). Also, the resources which compose a module must be wholly contained under a single project, but of course modules may reference modules in other projects.

Project Structural Model
 

The EMF structural model allows the tooling to understand various layouts for projects. Each logical module contained in the project is represented by a WorkbenchModule element. The WorkbenchModule defines information about the type of module, the resources consumed by the module from the project, and the dependent modules. The WorkbenchModule is abstract, and as modeled, does not necessarily correspond to only J2EE artifacts.

 

Figure 1: The Module Structural Metamodel.

 

The WorkbenchModule defines its deployedName, which is the name that will be used when the deployable form of the module is constructed. For a web application, the deployedName might be “MyWebApplication.war”. The WorkbenchModule may be uniquely referenced by a URI (stored on the WorkbenchModule as the “handle”). The fully qualified URI to any module must begin with the module protocol (“module:”), specify a subprotocol (“resource|classpath”) and then a path to the referenced module. A WorkbenchModule with the deplyedName “MyWebApplication.war” defined in a project named “MyWebModulesProject” would be referenced by the following URI: “module:/resource/MyWebModulesProject/MyWebApplication.war”.

 

Each WorkbenchModule defines a ModuleType. The ModuleType defines a moduleTypeId, which indicates the specific kind of module. The Web Tools Platform uses the moduleTypeId to determine how to handle the WorkbenchModule. The ModuleType may also define the runtime-paths of special metadata resources which are important to the WorkbenchModule. “Metadata” refers to resources which explain the details of the specific modules. An example of such a file would be the “WEB-INF/web.xml” deployment descriptor for Web Applications.

 

Each WorkbenchModule defines a list of WorkbenchModuleResources. Each WorkbenchModuleResource provides “sourcePath” and the corresponding “deployedPath”. The “sourcePath” can reference either a file or folder, but must reference a resource in the same project. The “deployedPath” will specify a location relative to the deployed structure of the WorbenchModule.

 

Each WorkbenchModule defines a list of DependentModules. Each DependentModule provides a handle which must resolve to another WorkbenchModule, a deployedPath that defines where the constructed module will be placed within the context of the deployed WorkbenchModule, and a dependencyType that can be either “consume” or “use” to indicate how the contents of that DependentModule should be absorbed by the WorkbenchModule.

XML Format for WorkbenchModules
 

The EMF model is stored as an XML model on disk. A full schema of this format will follow. The example below defines two modules in a single project. The first module (named: “MyWebModule.war”) defines two resources – each containers – that should be included in the deployed module. Three dependent modules are referenced, demonstrating the various ways that module URIs may be used. The first reference points to a local module, defined in the same project. The second reference (“module:/resource/AnotherProject/MyWebModuleLib2.jar”) uses a fully qualified URI that points to a module in another project. The last reference points to a module which is already in a deployable form and available on the classpath (“module:/classpath/AnotherTeamsUtilities.jar”). The second module defined by the XML sample (“MyWebModuleLib.jar”) defines a WorkbenchModule of the type “jst.utility”, which would be a simple Java library that could be used as a Utility Jar in a *.ear module or a Web Library in a *.war module.

 
 

<project-modules id="moduleCoreId">
    <wb-module
            deploy-name="MyWebModule.war">
        <wb-module
            module-type-id="jst.web" />
        <wb-resource
            deploy-path="WEB-INF/"
            source- path="/MyModulesProject/MyWebModule/WebContent/WEB-INF" />
        <wb-resource
            deploy-path="jsps/"
            source- path="/MyModulesProject/MyWebModule/WebContent/myjsps" />
        <dependent-module
            deploy-path="WEB-INF/lib"
            handle="MyWebModuleLib.jar" />
        <dependent-module
            deploy-path="WEB-INF/lib"
            handle="module:/resource/AnotherProject/MyWebModuleLib2.jar" />
        <dependent-module
            deploy-path="WEB-INF/lib"
            handle="module:/classpath/AnotherTeamsUtilities.jar" />
    </wb-module>
    <wb-module
            deploy-name="MyWebModuleLib.jar">
        <wb-module
            module-type-id="jst.utility" />
        <wb-resource
            deploy-path="/"
            source-path="/MyModulesProject/lib-src"/>
    </wb-module>
</project-modules>					
					

Figure 2: Sample metamodel XML format

Constraints enforced by the current solution
 
  1. The solution will not check dependencies for modules that are contained in the same project. To get the full benefits of inter-module dependency checking, modules must be separated into different projects. We do not have the necessary flexibility in constructing and scoping classpaths on a level more granular than the project level, which would be needed to support this functionality.

  2. The solution will not allow a single module to span more than one project. Within that project, we will have fairly broad flexibility to specify which resources map to which modules. Each module within a project must have its own source folder, but a module may contain more than one source folder. It would be discouraged or completely disallowed for a single source folder to be contained by more than one module. Modules may reference dependent modules in other projects (so a Web Application may reference a Web Library outside of the project that contains the Web Application).

  3. The solution will not allow more than one server target per module (and really per-project) at a time. The ability to switch this server target (via some action or property setting) will continue to be possible. Users that need the capability to develop for multiple server targets will need to manually switch and test as necessary.

  4. Each module in a project will have its own output folder structure automatically constructed for it. The output structure will match the J2EE-spec output structure required for the module type (for J2EE modules). A new builder will handle this responsibility and work cooperatively with the Java builder to construct a deployable, on-disk representation of the module structure. The necessity for this on-disk structure to match a J2EE-compliant layout is motivated by the requirement to have in-workbench testing, so that users will not have to deal with a deployer actually constructing a deployable module and shipping it off to a server to test their code. This approach is consistent with existing Ant-based approaches and Application Servers which can run in a "debug" mode on disk. Our value-add will be greater automation and integration with the workbench -- particularly for incremental based support. The specialized module builder would not be necessary if the source was already in the appropriate J2EE-spec compliant structure. The default creation will still encourage a single module per project, which conforms to the correct J2EE structure.

  5. Modules will be described using a simple XML format, and each project will contain one .wtpmodules file that will describe all of the modules for that project. The level of tooling to help users create these files is yet to be determined for WTP M4. This would be a great area for other interested developers to suggest and provide tooling (e.g. a Wizard or Editor) to create these files from existing structures.

Interaction Diagram for Important Components
 

The follow diagram highlights the important relationships between the components in the Flexible Project API. Each project that supports a flexible project structure has a ModuleCoreNature associated with it. The ModuleCoreNature is used to manage the WTP Modules Structural Model and used to access the Artifact Edit Models. A project has exactly one Structural Model that defines all of the modules contained by the project. A project may have multiple Artifact Edit Models. Each Artifact Edit Model represents the pieces of a module that an operation or editor would want to modify, and are loaded and saved as a single unit.

 

Figure 3: Interaction diagram of the ModuleCore components

 

The complexity of accessing the models is hidden from clients by the use of an Adapter pattern. When a client needs to read or modify a deployment descriptor model, the client may adapt the ArtifactEditModel to return an instance of the ArtifactEdit adapter or even request a specific subclass of ArtifactEdit with API which is targeted to the specific type of Deployment Descriptor. Clients that need to edit or understand the structural model of the modules in the project may use a similar pattern by adapting the ModuleStructuralModel to a ModuleCore adapter, which will then have the necessary API to focus on the module structural model.

 

The ArtifactEditModel and ModuleStructuralModel are each a subclass of the EditModel object. Edit models have a life cycle that requires them to be notified when clients are using them and notified when clients have completed their tasks. The ModuleCore and ArtifactEdit adapters will wrap the specific instances of the edit models they originally adapted. At the end of the client’s usage, the edit model must still be released. Releasing the edit model allows the framework to maintain an accurate reference count so that when the edit model is no longer in use by any client, the resources that it references may be unloaded.

ArtifactEdit: Working with the Module Content Metamodel
 

The ArtifactEdit adapter pattern is used to facilitate access to the modules resources, and provide API for specific module types. Clients must adapt the ArtifactEditModel to an ArtifactEdit adapter, which will operate in the context of the ArtifactEditModel that created it. An ArtifactEditModel may be acquired from the ModuleCoreNature using the specific WorkbenchModule the client is interested in.

 
				public ArtifactEditModel getModuleEditModelForRead(WorkbenchModule wbModule, Object anAccessorKey)
			
 

Each module type will register an ArtifactEditAdapterFactory that creates a corresponding ArtifactEdit instance or subclass. For instance, a module of type “jst.web” would register the WebAppEditAdapterFactory.

 
				IAdapterManager manager = Platform.getAdapterManager();
				manager.registerAdapters(new WebAppEditAdapterFactory(), ArtifactEditModel.class);
			
 

Web Tools Platform contributors may use SPI as above to expand the types of ArtifactEdit adapters available to clients.

 

The following line would return the specific ArtifactEdit instance:

 
				WebAppEdit util = (WebAppEdit)moduleEditModel.getAdapter(WebAppEdit.class);
			
 

The WebAppEdit class would have some of the specific module API previously found on the WebNature and WebEditModel. Type specific API would be found here:

 
				public WebApp getWebApp();
				public int getServletVersion();
			
 

Additionally, API is inherited from a common superclass:

 
				public int getJ2EEVersion();
				public createDeploymentDescriptorWithRoot();
				public EObject getRootObject();
			
 

The API that will be provided to consumers is still under development, and suggestions for the kinds of API that are required are always welcome.

Deployable Module Builder
 

The Deployable Module Builder extends the Eclipse Incremental Builder framework in order to create and maintain a deployable module output structure for flexible module projects, which are now supported by the Web Tools Platform. The builder is added to any project which contains the Module Core Nature indicating that a given project is flexible. The can construct appropriate deploy structures for multiple modules of possibly varying types within the same project. The deploy structure for each J2EE module will be the corresponding J2EE-compliant runtime structure, but the builder may be extended to handle other types of modules that are not J2EE-specific. The deploy structures created by the builder may then be used to run on a server.

 

The Deployable Module Builder is scoped to the project level, as all Eclipse builders are, however this specific builder is more concerned about the individual modules within a given project. The design of the builder focuses on these individual modules, rather than a project-level artifact. The builder uses the WorkbenchModule definitions from the Modules Structural Model to understand what structures need to be created. Each operation arranges the deployable structure according to J2EE specifications by processing each of the WorkbenchModuleResources contained in its WorkbenchModule. The deployable structure is created in the project using the format “RootProject/.deployables/module_name”. The builder also manages all module dependencies making sure dependent modules are handled appropriately. Once all WorkbenchModules for the given project have been constructed, the Java builder will then compile all source code and the *.class files will be pushed into the predefined output locations within their corresponding WorkbenchModule deployed structure. The API defined by the Server Tools component will then be used to correctly adapt a WorkbenchModule to an IModule in order to run the modules on the server.

 

Each module type (EJB, Web App, App Client, Connector) must register its own type of deployable module builder. The registration mechanism is not yet complete, but will allow for total extensibility within the framework in how deployable modules are constructed. (Note: Currently in the M3 initial implementation, the only module which contributes a deployable module builder is Web Application, which means only a Web Application project can support flexible project structures.)

 

The following screen shots show a flexible Web Module project before and after the Deployable Module Builder has run.

 

Figure 4: A flexible project before a build.

 

Figure 5: A flexible project after a build with 3 web modules defined within the project.

 

Figure 6: The individual output module J2EE compliant contents.

Server Integration
 

The contents of the “.deployables” folder are represented as IModules to the Server Tooling, which is a model format that the server can deploy. The Server API contains a ServerUtil object that listens for project changes. A project change occurs after the deploy builder creates a “.deployables” folder. After the project change, ServerUtil will query for a DeployableFactory via an extension point. Currently, Server Tools assigns a single DeployableFactory per project, which must be expanded to allow multiple modules and modules types per project. The DeployableFactory will create multiple J2EE ModuleDelegates depending on the WorkbenchModules defined for the project (see ModuleCore API). Each J2EE ModuleDelegate will set its context and metadata location based on the location of the deployable structure created for the WorkbenchModule. Next, the DeployableFactory creates an IModule (as defined by the Server Tools API) caches the IModule by the project. As already stated, single module per project approach must change to support multiple modules per project.

 

Once the Run on server action is invoked by the user, the WorkbenchModule will be adapted to an IModule, which the Server Tools understand and can deploy.

 

Figure 6: Interaction diagram of the Server Integration components

 

 

Last updated Feb 16 2004