Guides and Tutorials

How to create an empty bundle

Updated: March 15, 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.

 

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.

|
`-- 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.ecm.platform</groupId>
        <artifactId>nuxeo-features-parent</artifactId>
        <version>5.4.1</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.ecm.platform</groupId>
        <artifactId>nuxeo-features-parent</artifactId>
        <version>5.4.1</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 this lesson.

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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.

 

As said in the beginning of this recipe, if you have unexpected errors or Nuxeo Application behavior don't forget to check the FAQ and don't forget to leave us a comment about this recipe or about the cookbook!

 


Related How-Tos
   
3 months ago Manon Lumeau fix layout on 60
4 months ago Solen Guitter NXDOC-1103: Remove link to PDF and welcome banner
10 months ago Manon Lumeau 59
3 years ago Manon Lumeau 58 | Migration of unmigrated content due to installation of a new plugin
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 56 | Migration of unmigrated content due to installation of a new plugin
3 years ago Manon Lumeau 57 | 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
6 years ago Benjamin Jalon 37
6 years ago Benjamin Jalon 38 | Migrated to Confluence 4.0
6 years ago Benjamin Jalon 39 | Migration of unmigrated content due to installation of a new plugin
6 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