Tutorials

How to create an empty bundle

Updated: March 16, 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 initializing a plugin project for Nuxeo Platform is now to use Nuxeo CLI which provides many bootstraps.

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 available in a console and Nuxeo CLI installed. 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.8
Maven 3.x
NodeJS 6.x
Nuxeo CLI latest
Nuxeo Server distribution Latest LTS

Create the Basic Project Skeleton

To create a basic folder structure, we use the Nuxeo CLI. There is no required location to create your project. To create your project structure, follow the steps below.

  1. In a console, type: mkdir cookbook && cd $_
  2. In a console, type: nuxeo bootstrap single-module.
  3. You are prompted a series of choices to create your project. Accept the default propositions and follow those:

    create Generating Single module
     info   Parameters: Parent group, Parent artifact, Parent version, Nuxeo version, Project group, Project artifact, Project version, Project description
    ? Parent Group id (use white space to cancel default value.): org.nuxeo
    ? Parent Artifact id: nuxeo-addons-parent
    ? Parent Version: 8.3
    ? Project Group id: org.nuxeo.cookbook
    ? Project Artifact id: cookbook-core
    ? Project version: 1.0-SNAPSHOT
    ? Project description: Cookbook bare project
    create pom.xml
    create src/main/resources/META-INF/MANIFEST.MF
    create src/main/java/org/nuxeo/cookbook/package-info.java
    create src/test/resources/jndi.properties
    create src/test/resources/log4j.xml
     info You can start editing code or you can continue with calling another generator (nuxeo bootstrap <generator>..)
    

Complete the Folder Structure

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

cookbook
└── src
    ├── main
    │   ├── java
    │   │   └── org
    │   │       └── nuxeo
    │   │           └── cookbook
    │   └── resources
    │       ├── META-INF
    │       └── OSGI-INF
    └── test
        ├── java
        │   └── org
        │       └── nuxeo
        │           └── cookbook
        └── resources

Created Files Description

The pom.xml File Explained

  1. Nuxeo Addon set as parent to pull all Nuxeo dependencies:

    <parent>
        <groupId>org.nuxeo</groupId>
        <artifactId>nuxeo-addons-parent</artifactId>
        <version>8.3</version>
    </parent>
    
  2. Repositories:

    Repository public contains all Nuxeo's bundle releases.

    <repository>
      <id>public</id>
      <url>http://maven.nuxeo.org/nexus/content/groups/public</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
    

    Repository public-snapshot contains all Nuxeo's bundle snapshots.

    <repository>
      <id>public-snapshot</id>
      <url>http://maven.nuxeo.org/nexus/content/groups/public-snapshot</url>
      <releases>
        <enabled>false</enabled>
      </releases>
      <snapshots>
        <updatePolicy>always</updatePolicy>
        <enabled>true</enabled>
      </snapshots>
    </repository>
    

    Repository nuxeo-studio contains Studio projects.

    <repository>
      <id>nuxeo-studio</id>
      <url>https://connect.nuxeo.com/nuxeo/site/studio/maven</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <updatePolicy>always</updatePolicy>
        <enabled>true</enabled>
      </snapshots>
    </repository>
    

The deployment-fragment.xml File

In order to deploy your Nuxeo addon project in the Nuxeo server, you need a new file called deployment-fragment.xml in the cookbook/src/main/resources/OSGI-INF folder. This file tells the deployment mechanism which files must be copied and where. This file is not mandatory, 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:

  • If you want your bundle deployed after all other bundles/contributions, you can add a all
  • The deployment-fragment.xml file is not required if you have no dependency information to transmit to the runtime or pre-deployment actions to execute.

The MANIFEST.MF File

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

Here is a sample that a MANIFEST.MF file must hold:

Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
Manifest-Version: 1.0
Bundle-Vendor: org.nuxeo.cookbook
Bundle-Version: 1.0.0
Bundle-Name: cookbook-core
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.nuxeo.cookbook.cookbook-core;singleton=true

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

Manifest-Version: 1.0
Bundle-ManifestVersion: 2

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

Bundle-Name: cookbook-core
Bundle-SymbolicName: org.nuxeo.cookbook.cookbook-core;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 "x.y.z";
  • 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.

 

Files for the Tests

The log4j.xml File

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

Here is a sample of such a file:

<root>
  <priority value="INFO" />
  <appender-ref ref="CONSOLE" />
</root>

To make the log more or less verbose, just change the first value of the priority value. In this example, the level is "INFO". 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.

Install and Check the Deployment of Your Bundle

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

    $ mvn install
    

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

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

    • under Windows, assuming that the Nuxeo Server Distribution is installed at the location "C:\Nuxeo\", copy the jar in C:\Nuxeo\nxserver\bundles\;
    • under Linux, assuming that the Nuxeo Server Distribution is installed at the location "/opt/nuxeo", copy the jar in /opt/nuxeo/nxserver/bundles.
  3. Start your server using the ./bin/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 ./bin/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:.

Now you've got a bundle ready for customization. You can start generating more stuff with the Nuxeo CLI and use it to improve your Nuxeo Server instance.

 

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

 


Related How-Tos
8 days ago Solen Guitter Update Nuxeo CLI links and remove *the* Nuxeo CLI
25 days ago Arnaud Kervern NXDOC-1116: Replace generator occurences with nuxeo-cli
3 months ago GitHub Fix review date and typos
4 months ago Arnaud Kervern Rewrite empty bundle creation documentation
4 months ago manonlumeau fix related pages panel
4 months ago Andrew Goodricke Add content-review-lts2016 to labels
4 months ago Kevin Leturc NXP-19481: Update MarkLogic page to detail how to configure range element indexes
7 months ago Manon Lumeau 64
7 months ago Manon Lumeau 63 | remove wiki markup
7 months ago Manon Lumeau 62 | remove wiki markup
7 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