apache > lenya
 

Modules

Introduction

Modules are packages providing a certain set of resources or functionality.

Examples:

  • a resource type (e.g., docbook module)
  • a repository implementation (e.g., jdbc module)
  • a collection of XSLTs (e.g., content2svg module)

Some modules are included in the Lenya distribution:

  • lucene - search functionality
  • sitetree - manage documents in a tree-like structure
  • jcr - store content in a JCR repository (experimental)
  • xhtml - XHTML-based resource type
  • links - resource type to manage link lists
  • lenyadoc - adds the lenyadoc:// protocol

Implementing a module

All resources of a module are located in a single directory. The following is an example directory structure, all files are optional and depend on the nature of the module.

mymodule/
  config/                    configuration files
    module.xml               module descriptor
    cocoon-xconf/            patches for cocoon.xconf
      components.xconf     
  usecases/
    mymodule.jx              usecase view
  resources/
    images/                  image files
    css/                     CSS files
    schemas/                 XML schemas (RNG, XSD, ...)
    samples/                 Samples (in case of resource type modules)
  java/
    src/                     Java source files
    test/                    Java test classes
    lib/                     Java libraries
  xslt/                      XSLT stylesheets
  sitemap.xmap               main module sitemap

To add a module to your Lenya installation, declare it in local.build.properties:

modules.root.dirs=src/modules:src/webapp/lenya/pubs/default/modules:/home/john/modules/mymodule

When the module is deployed, the following steps are executed:

  • the module files are copied to context://lenya/modules
  • Java sources are compiled, libraries are installed
  • cocoon.xconf is patched

The Module Descriptor File

Each module must be described using a module descriptor XML file module.xml, located in the config directory of the module. The descriptor is validated when the module is deployed, so be careful! :)

A typical module descriptor looks like this:

<module xmlns="http://apache.org/lenya/module/1.0">
  <id>org.myproject.lenya.modules.myeditor</id>
  <depends module="org.apache.lenya.modules.usecase"/>
  <depends module="org.apache.lenya.modules.webdav"/>
  <export package="org.myproject.lenya.modules.myeditor.api"/>
  <package>org.myproject.lenya.modules</package>
  <version>0.1-dev</version>
  <name>My Own Editor</name>
  <lenya-version>2.0-dev</lenya-version>
  <description>
    This is my own editor.
    For more information, visit http://myproject.org/editor.
  </description>
</module>

The id must start with the package of the module. It is not allowed to deploy two modules with the same ID.

If your module uses code from other modules, you have to add a <depends> for each of these modules.

The <export package="..."/> statement(s) declare public packages, which means that these packages are accessible from other modules. You should aim for long-term stability of all interfaces and classes in exported packages, since they represent the Java API of your module.

Implementing Usecases in Modules

Declaring the Usecase

For example, imagine you implement a newsletter module, containing a send usecase. To declare usecases, add a patch file for cocoon.xconf, for instance newsletter/config/cocoon-xconf/usecases.xconf:

<xconf xpath="/cocoon/usecases"
       unless="/cocoon/usecases/component-instance[@name = 'newsletter.send']">
  <component-instance name="newsletter.send"
                      logger="lenya.usecases.newsletter.send"
                      class="org.myproject.lenya.newsletter.usecases.Send">
    <view template="modules/newsletter/usecases/send.jx" menu="false"/>
  </component-instance>
</xconf>

As you can see in the view declaration, the JX templates are typically stored in the <module>/usecases directory. The Java source files go into the <module>/java/src directory, they are compiled automatically by the Lenya build process.

Calling the Module Sitemap

The following URL syntax is used to make a call to a module sitemap:

cocoon://modules/<module>/**

The module sitemap is located at newsletter/sitemap.xmap. To request the latest newsletter and display it on the confirmation screen using the CInclude approach (for more information, see documentation about the usecase framework), you could for instance use the URI cocoon://modules/newsletter/latestNewsletter.xml:

<page:page
  xmlns:jx="http://apache.org/cocoon/templates/jx/1.0"
  xmlns:page="http://apache.org/cocoon/lenya/cms-page/1.0"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
  xmlns:cinclude="http://apache.org/cocoon/include/1.0"
  >
  
  <page:body>
    <h1><i18n:text>Send Newsletter</i18n:text></h1>
    <form>
      ...
      <jx:import uri="templates/messages.jx"/>
      
      <cinclude:includexml>
        <cinclude:src>cocoon://modules/newsletter/latestNewsletter.xml</cinclude:src>
      </cinclude:includexml>
      
      <input name="submit" type="submit" value="Send Newsletter"/>
    </form>
  </page:body>
</page:page>

Another useful option is to use a module URL as the usecase view, e.g. to export some XML. Note that the attribute uri is used instead of template. The usecase class DummyUsecase can be used because no Java code shall be executed.

<xconf xpath="/cocoon/usecases"
       unless="/cocoon/usecases/component-instance[@name = 'newsletter.download']">
  <component-instance name="newsletter.send"
                      logger="lenya.usecases.newsletter.send"
                      class="org.apache.lenya.cms.usecase.DummyUsecase">
    <view uri="cocoon://modules/newsletter/downloadNewsletter.xml"/>
  </component-instance>
</xconf>

Adding Menu Items

A module can provide menu items which are added to the publication menu. To insert the menu items of a module, you have to add the module declaration to publication.xml:

<publication>
  ...
  <module name="newsletter"/>
  ...
</publication>

If there is a menus.xmap sitemap in the module's root directory, a request of the form <area>.xml is sent into this sitemap. The matching pipeline could look like this:

<map:match pattern="**">
  <map:generate type="serverpages" src="config/menu.xsp"/>
  <map:serialize type="xml"/>
</map:match>

The server page <module>/config/menu.xsp delivers a menu XML which includes the items to be inserted:

<xsp:page ...>
  <menu>
    <menus>
      <menu i18n:attr="name" name="File">
        <block admin="false">
          <item uc:usecase="newsletter.send" href="?">
            <i18n:text>Send Newsletter</i18n:text>
          </item>
        </block>
      </menu>
    </menus>
  </menu>
</xsp:page>