Server

HOWTO: Create an Empty Bundle

Updated: November 15, 2024

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 addon 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 11
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: 9.10
    ? 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/log4j2.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>9.10</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

For this exercise, 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">
  <install>
    <!-- to be completed 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, you can add an element <require> inside the <fragment> element, and fill it with the required bundle name. The all pseudo-bundle can also be referenced, so that this bundle is deployed after all others: <require>all</require>.
  • 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 addons 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 addon 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 log4j2.xml File

As the tests will run in a sandbox, it could be useful to define a file named log4j2.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/bundles/" 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 start 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 start
    • 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