Guides and Tutorials

How to create an empty bundle

Updated: February 24, 2017 Page Information Edit on GitHub

This tutorial remains very interesting as it explains many basics of a Nuxeo module and is IDE agnostic. Yet the common way of initialising a plugin project for Nuxeo Platform is now to use Nuxeo IDE which provides many wizards.

This recipe describes the steps to create the bare structure of a Nuxeo add-on project (aka a bundle).

This is the very first recipe of this cookbook and it will be the basis for the development of new bundles, of new features, even of new UI elements all along this cookbook. All the other recipes will assume that this recipe has been done.

General Remarks

  • This recipe is not specific to a system or an IDE. You will have to adapt it to your needs. The sole obligation is to use Maven in a console. But, even this part, with experience, could be fitted to your IDE habits if you have any.
  • For any remark about this recipe or about this cookbook, don't hesitate to leave us a comment on this page.

This recipe is composed of the major steps below:

What you need

Tool Version
Java jdk 1.6
Maven 2.2.1
Packaged Nuxeo DM distribution 5.4.1-Tomcat

Create the basic project skeleton using Maven

To create a basic folder structure, we use Maven and its default archetype. There is no required location to create your project. To create your project structure, follow the steps below.

  1. In a console, type: mvn archetype:generate. The available archetypes are listed.
  2. Check that in the logs displayed, you have the two lines below:

    [INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
    Choose archetype:
    [...]
    104: remote -> maven-archetype-quickstart (An archetype which contains a sample Maven project.)
    [...]
    

    If not, you may have the wrong version of Maven.

    Warning

    As the number of archetype is based on archetype contributions, the reference is not automatically "104". But the default proposition should still be "org.apache.maven.archetypes:maven-archetype-quickstart" whatever the version is.

  3. You are prompted a series of choices to create your project. Accept the default propositions (by pressing Enter) except for the groupId and artifactIdof your project which must be:

    groupId org.nuxeo.cookbook
    artifactId bareproject
  4. Confirm the defined settings. The logs indicate that the build was successful.

Here is an example of the log you should have for the whole project creation (some lines have been skipped using "[…]"):

[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [archetype:generate] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] Preparing archetype:generate
[INFO] No goals needed for project - skipping
[INFO] [archetype:generate {execution: default-cli}]
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: remote -> docbkx-quickstart-archetype (-)
2: remote -> multi (-)
[...]
103: remote -> maven-archetype-profiles (-)
104: remote -> maven-archetype-quickstart (An archetype which contains a sample Maven project.)
105: remote -> maven-archetype-site (An archetype which contains a sample Maven site which demonstrates some of the supported document types like
    APT, XDoc, and FML and demonstrates how to i18n your site. This archetype can be layered
    upon an existing Maven project.)
[...]
385: remote -> javg-minimal-archetype (-)
Choose a number: 104:
Choose version:
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
Choose a number: 6:
Define value for property 'groupId': : org.nuxeo.cookbook
Define value for property 'artifactId': : bareproject
Define value for property 'version': 1.0-SNAPSHOT:
Define value for property 'package': org.nuxeo.cookbook:
Confirm properties configuration:
groupId: org.nuxeo.cookbook
artifactId: bareproject
version: 1.0-SNAPSHOT
package: org.nuxeo.cookbook
Y:
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: org.nuxeo.cookbook
[INFO] Parameter: packageName, Value: org.nuxeo.cookbook
[INFO] Parameter: package, Value: org.nuxeo.cookbook
[INFO] Parameter: artifactId, Value: bareproject
[INFO] Parameter: basedir, Value: /home/user1/Workspaces/CookbookTest
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] project created from Old (1.x) Archetype in dir: /home/user1/Workspaces/CookbookTest/bareproject
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13 minutes 51 seconds
[INFO] Finished at: Thu Apr 21 10:51:01 CEST 2011
[INFO] Final Memory: 14M/213M
[INFO] ------------------------------------------------------------------------

Complete the folder structure

After you completed the project creation, you get this folder structure:

bareproject
|
`-- src
    |-- main
    |   `-- java
    |       `-- org
    |           `-- nuxeo
    |               `-- cookbook
    `-- test
        `-- java
            `-- org
                `-- nuxeo
                    `-- cookbook

To fit to the classical structure of a Nuxeo add-on project, you need to create new folders in src/main and src/test using your favorite means. At the end, you need to get a folder structure as shown below.

bareproject
|
`-- src
    |-- main
    |   |-- java
    |   |   `-- org
    |   |       `-- nuxeo
    |   |           `-- cookbook
    |   `-- resources
    |       |-- META-INF
    |       |-- OSGI-INF
    |       |-- schemas
    |       `-- web
    |           `-- nuxeo.war
    `-- test
        |-- java
        |   `-- org
        |       `-- nuxeo
        |           `-- cookbook
        `-- resources
            `-- META-INF

The .war ending of nuxeo.war may be deceiptive, but it is actually a folder and not a file.

Adapt or create files

Adapt the pom.xml file

We need to customize the pom.xml file provided by the archetype at the root folder of the project.

  1. Change the parent entry.

    <parent>
        <groupId>org.nuxeo</groupId>
        <artifactId>nuxeo-addons-parent</artifactId>
        <version>6.0</version>
    </parent>
    
  2. In the dependencies, delete the JUnit entry.

  3. Add repositories.

    <repositories>
        <repository>
            <id>public</id>
            <url>http://maven.nuxeo.org/public</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
        <repository>
            <id>snapshots</id>
            <url>http://maven.nuxeo.org/public-snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>
    

Your "pom.xml" file should at the end to look like this:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.nuxeo.cookbook</groupId>
    <artifactId>bareproject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>bareproject</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <parent>
        <groupId>org.nuxeo</groupId>
        <artifactId>nuxeo-addons-parent</artifactId>
        <version>6.0</version>
    </parent>

    <!-- nuxeo repos have copies of everything needed -->
    <repositories>
        <repository>
            <id>public</id>
            <url>http://maven.nuxeo.org/public</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
        <repository>
            <id>snapshots</id>
            <url>http://maven.nuxeo.org/public-snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>

    <dependencies>

    </dependencies>
</project>

Create a "deployment-fragment.xml" file

In order to deploy your Nuxeo add-on project in the Nuxeo server, you need to add a new file called "deployment-fragment.xml" in the "/src/main/resources/OSGI-INF" folder. This file tells the deployment mechanism which files must be copied and where. This file is not mandatory at this stage, but it is needed to have your bundle displayed in the log at start up.

For now, the content of the file "deployment-fragment.xml" should be:

<?xml version="1.0"?>
<fragment version="1">
<!-- will contains some stuff -->
  <install>
<!-- useful later -->
  </install>
</fragment>

The content of this file will be completed in a coming recipe.

Remark:

  • The given version 1 into the fragment item is important because before Nuxeo Runtime 5.4.2, the bundle dependency management was managed into the MANIFEST.MF. You have from 5.4.2 version of Nuxeo Runtime new items (require, required-by)
  • If you want your bundle deployed after all other bundles/contributions, you can add a all
  • If you have this message "Please update the deployment-fragment.xml in myBundle.jar to use new dependency management", this is because you didn't specify the fragment version (and maybe let dependency informations into the manifest file.
  • the deployment-fragment.xml file is not required since 5.4.2 if you have no dependency information to transmit to the runtime or pre-deployment actions to execute.

Create a "MANIFEST.MF" file

As Nuxeo add-ons are OSGi modules, you need to create a "MANIFEST.MF" file in the "/src/main/resources/META-INF" folder.

Here are the minimal properties the "MANIFEST.MF" file must hold:

Manifest-Version: 1.0
Bundle-ManifestVersion: 1
Bundle-Name: cookbook-basic-bundle
Bundle-SymbolicName: org.nuxeo.cookbook.basic;singleton:=true
Bundle-Version: 0.0.1
Bundle-Vendor: Nuxeo

Some of the values above are mandatory, some should be changed to adapt to your needs. The following properties are more the less "constants" and their values must be always the same:

Manifest-Version: 1.0
Bundle-ManifestVersion: 1

The other properties should be customized to your needs. The two principals are:

Bundle-Name: cookbook-basic-bundle
Bundle-SymbolicName: org.nuxeo.cookbook.basic;singleton:=true
  • "Bundle-Name" corresponds to the human-readable name of the bundle;
  • "Bundle-SymbolicName" is the reference computed by the OSGi container and looked-up by the other bundles. This declaration is immediately followed, on the same line, by ";singleton:=true" which declares to the OSGi container that the bundle can't cohabit with an other version of the bundle at runtime. The semi-colon is of course mandatory.

The other properties are:

  • "Bundle-Version": This property is all on your responsibility. The Nuxeo convention is three digits separated by a dot such as "0.0.1";
  • "Bundle-Vendor": This is the name of the add-on owner.

Although not used in this recipe, there is one more property you should know of: "Nuxeo-Component:". It contains a list of files used to define various elements of your component. Its use is detailed in Writing a Bundle Manifest.

Formatting

The trickiest and most important part of a "MANIFEST.MF" file is its formatting. One mistake and the OSGi context can't be correctly started, leading to unexpected issues and an unreachable bundle. Here are the three formatting rules to respect:

  1. Each property name:
    • begins at the first character of the line;
    • ends with a colon without space between the name of the property and the colon itself.
  2. Each value:
    • must be preceded by a space;
    • ends with a "end of line" with eventually a comma before it.
  3. There MUST be an EMPTY LINE at the END OF THE FILE.

Create files for the tests

"log4j.properties"

As the tests will run in a sandbox, it could be useful to define a file named "log4j.properties" file. It must be placed in the "/src/test/resources" folder.

Here is the content of such a file:

log4j.rootLogger=WARN, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%C{1}] %m%n

To make the log more or less verbose, just change the first value of the "log4j.rootlogger" property. In this example, the level is "WARN". If you want more details, downgrade it to "DEBUG". You will have more entries displayed in the console about Nuxeo classes involved in the running tests.

"MANIFEST.MF"

Create a new "MANIFEST.MF" file, in the "/src/test/resources/META-INF" folder this time. The content of this file should be:

Manifest-Version: 1.0
Bundle-ManifestVersion: 1
Bundle-Name: cookbook-basic-bundle-test
Bundle-SymbolicName: org.nuxeo.cookbook.basic.test;singleton:=true
Bundle-Version: 0.0.1
Bundle-Vendor: Nuxeo

The most important difference between this content and the one declared in the "/src/main/resources/META-INF/MANIFEST.MF" file, is the value of the "Bundle-SymbolicName:" property. Those two have to be different to avoid bundle Symbolic Name collision.

Install and check the deployment of your bundle

  1. Build your bundle, using the following Command Line Interface (CLI):

    $ mvn install
    

    In the "/target" folder of your project, you get a JAR file whose name if formed like that: artifactId-1.0-SNAPSHOT.jar.

  2. Copy your brand new jar into the sub-folder "nxserver/plugins/" of your nuxeo application's root folder:

    • under Windows, assuming that the nuxeo-distribution is installed at the location "C:\Nuxeo\", copy the jar in "C:\Nuxeo\nxserver\plugins\";
    • under Linux, assuming that the nuxeo-distribution is installed at the location "/opt/nuxeo", copy the jar in "/opt/nuxeo/nxserver/plugins".
  3. Start your server using the "./nuxeoctl console" command.

    You can check the dedicated Start and stop page of the technical documentation for more information about the different ways to start your server).

  4. Check that your bundle is correctly deployed: check if its SymbolicName (as configured in the "/src/main/resources/META-INF") appears in the logs. The logs are displayed:
    • in the console if you started your server using the "./nuxeoctl console"
    • in the file "server.log" located in the "log" folder of your Nuxeo server root folder. This name is found in the list of the bundles deployed by Nuxeo in the very first lines of the logs, just after the line ended by " Preprocessing order:".

In the following example, the name of your bundle could be found at the line n°8 of the following print (some lines of the logs have been skip using "[…]"):

2011-04-18 10:37:02,384 INFO  [org.nuxeo.runtime.deployment.preprocessor.DeploymentPreprocessor] Preprocessing order:
    org.nuxeo.ecm.webengine.core
    org.nuxeo.ecm.platform.ui
    org.nuxeo.ecm.platform.types.core
    org.nuxeo.ecm.platform.uidgen.core
    [...]
    org.nuxeo.ecm.platform.oauth
    org.nuxeo.cookbook.book
    org.nuxeo.ecm.platform.syndication
    org.nuxeo.ecm.platform.audit.ws
    [...]
    org.nuxeo.ecm.relations.jena

Now you've got a bundle ready for customization. You can propose your contribution to configuration and use it to improve your Nuxeo instance.


Related How-Tos
3 months ago Solen Guitter Remove references to 5.8, update links to 6.0 to use 7.10
7 months ago GitHub Fix link to FT version
9 months ago Manon Lumeau 63 | emove wiki markup
9 months ago Manon Lumeau 62 | remove wiki markup
9 months ago Manon Lumeau 61 | remove deleted page
2 years ago Florent Guillaume 57 | better parent in pom
2 years ago Florent Guillaume 58 | Migration of unmigrated content due to installation of a new plugin
2 years ago Florent Guillaume 59 | Migration of unmigrated content due to installation of a new plugin
2 years ago Florent Guillaume 60 | Migration of unmigrated content due to installation of a new plugin
2 years ago Florent Guillaume 56 | fix link
3 years ago Manon Lumeau 53
3 years ago Manon Lumeau 54
3 years ago Manon Lumeau 55 | Migration of unmigrated content due to installation of a new plugin
3 years ago Manon Lumeau 52
3 years ago Alain Escaffre 49
3 years ago Alain Escaffre 50 | Migration of unmigrated content due to installation of a new plugin
3 years ago Alain Escaffre 51 | Migration of unmigrated content due to installation of a new plugin
3 years ago Alain Escaffre 48
3 years ago Alain Escaffre 47
4 years ago Solen Guitter 42
4 years ago Solen Guitter 43
4 years ago Solen Guitter 44 | Migration of unmigrated content due to installation of a new plugin
4 years ago Solen Guitter 45 | Migration of unmigrated content due to installation of a new plugin
4 years ago Solen Guitter 46 | Migration of unmigrated content due to installation of a new plugin
4 years ago Solen Guitter 41
5 years ago Benjamin Jalon 37
5 years ago Benjamin Jalon 38 | Migrated to Confluence 4.0
5 years ago Benjamin Jalon 39 | Migration of unmigrated content due to installation of a new plugin
5 years ago Benjamin Jalon 40 | Migration of unmigrated content due to installation of a new plugin
6 years ago Ronan Le Gall 36 | the maven command interactions
6 years ago Wojciech Sulejman 35 | changed from "bundles" to "plugins" which is the recommended location for custom plugins
6 years ago Solen Guitter 34
6 years ago Solen Guitter 33
6 years ago Solen Guitter 32
6 years ago Solen Guitter 31
6 years ago Solen Guitter 30
6 years ago Solen Guitter 29
6 years ago Solen Guitter 28
6 years ago Solen Guitter 27
6 years ago Solen Guitter 26
6 years ago Solen Guitter 25
6 years ago Solen Guitter 24
6 years ago Ronan Le Gall 23
6 years ago Ronan Le Gall 22
6 years ago Solen Guitter 21 | reorganized steps
6 years ago Ronan Le Gall 20
6 years ago Ronan Le Gall 19
6 years ago Ronan Le Gall 18
6 years ago Ronan Le Gall 17
6 years ago Ronan Le Gall 16
6 years ago Ronan Le Gall 15
6 years ago Ronan Le Gall 14
6 years ago Ronan Le Gall 13
6 years ago Solen Guitter 12
6 years ago Ronan Le Gall 11
6 years ago Ronan Le Gall 10
6 years ago Ronan Le Gall 9
6 years ago Ronan Le Gall 8
6 years ago Benjamin Jalon 7
6 years ago Ronan Le Gall 6
6 years ago Ronan Le Gall 5
6 years ago Ronan Le Gall 4
6 years ago Ronan Le Gall 3
6 years ago Ronan Le Gall 2
6 years ago Ronan Le Gall 1
History: Created by Ronan Le Gall