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:
- Step 1: Create the basic project skeleton using Maven
- Step 2: Complete the folder structure
- Step 3: Adapt or create Files
- Step 4: Install and check the deployment of your Bundle
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.
- In a console, type:
mvn archetype:generate
. The available archetypes are listed. 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.
WarningAs 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.
You are prompted a series of choices to create your project. Accept the default propositions (by pressing Enter) except for the
groupId
andartifactId
of your project which must be:**groupId**
org.nuxeo.cookbook
**artifactId**
bareproject
- 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.
Change the parent entry.
<parent> <groupId>org.nuxeo.ecm.platform</groupId> <artifactId>nuxeo-features-parent</artifactId> <version>5.4.1</version> </parent>
In the dependencies, delete the JUnit entry.
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!