Tutorials

Getting Started with Nuxeo CLI

Updated: March 18, 2024

Here we want to add some server side Java logic that will update the contract's renegotiation date. In our example it will simply take the contract's start date and add three months to it, but you can think of integrating any custom logic in your Java class, including a connection to an external webservice or an ERP.

Prerequisites

  • A Contract document type created at the previous step
  • An IDE that integrates with Maven. We provide instructions for Eclipse IDE for Java EE Developers 4.5 (Mars) or IntelliJ IDEA
  • Java 8 with JDK
  • Maven 3.3+ (see the Installing Maven section of page Maven Usage)
  • The latest stable NodeJS version should be installed on your machine
  • npm 2.12.0+

Step 1 - Install Nuxeo CLI

From a command line prompt, type:

$ npm install -g nuxeo-cli

Nuxeo CLI lets you easily scaffold common Nuxeo components like empty projects, Packages, Automation Operations, Services, etc. This saves you time writing boilerplate code to focus on your code instead of the structure.

We'll use it to generate a new Nuxeo project and a custom operation.

Step 2 - Implement a Custom Operation

We want to create an operation that indicates the date of renegotiation of a contract. This will be done by fetching the document's start date and adding three months to it.

A custom operation is a Java class in which you can put custom business logic. Custom operations usually serve one of two purposes: to support business logic that is too complex to express via an Automation Chain; or to provide functionality that does not exist within existing operations.

Once created, the operation can be exposed in Nuxeo Studio and used just like any other operation via automation chains and automation scripts. See the Automation section for more information about operations.

Create Your Project

From a command line:

  1. Create an empty folder in which to store the project:

    $ mkdir contract-mgt-project
    $ cd contract-mgt-project
    
  2. Generate a "Multi module" project structure:

    $ nuxeo bootstrap
    
  3. Fill in the following values, via the prompts provided by Nuxeo CLI:

    ? Use a parent artifact (for instance your company's BOM or the org.nuxeo.ecm.distribution:nuxeo-distribution POM)? y
    ? Parent Group id: org.nuxeo.ecm.distribution
    ? Parent Artifact id: nuxeo-distribution
    ? Parent Version: (leave the default option here)
    ? Project group id: com.bigcorp.contractmgt
    ? Project Artifact id: contract-mgt-project-parent
    ? Project version: 1.0-SNAPSHOT
    ? Project description: Contract management parent.
    

    Once complete, Nuxeo CLI will automatically start the process to create a "Single Module" project (the operation will be implemented here).

  4. Fill in the following values to generate the "Single Module" project:

    ? Project Group id: com.bigcorp.contractmgt
    ? Project Artifact id: contract-mgt-project-core
    ? Project version: 1.0-SNAPSHOT
    ? Project description: Contract management project.
    

    Now that the project is generated, it can be used in any IDE.

Import the Project Into an IDE

  • Using IntelliJ IDEA

    1. Select File / Open....

    2. Browse to your project root folder (contract-mgt-project), then the pom.xml file and click Open.

    3. Choose Open as Project.

  • Using Eclipse

    1. Generate Eclipse configuration files.

      $ mvn install
      
      # The following parameters (downloadSources, downloadJavadocs and useProjectReferences) are optional
      # For details, see Maven Eclipse Plugin documentation: https://maven.apache.org/plugins/maven-eclipse-plugin/eclipse-mojo.html
      $ mvn eclipse:clean eclipse:eclipse -DdownloadSources=true -DdownloadJavadocs=true -Declipse.useProjectReferences=true
      
      # Linux and OS X users should run the following script to make Eclipse use different build directories than Maven:
      $ curl -o- https://raw.githubusercontent.com/nuxeo/nuxeo/master/fixeclipse|bash
      # A cross-platform script is also available for Windows users:
      # curl -o- https://raw.githubusercontent.com/nuxeo/nuxeo/master/scripts/fixeclipse.py|python
      
    2. In Eclipses select File / Import / General / Existing Projects into Workspace.

    3. Browse to your project root folder (contract-mgt-project), then click Finish.

Implement Your Operation

  1. In a terminal, generate an operation code template:

    $ nuxeo bootstrap operation
    
  2. You are prompted for a few details:

    ? Operation package: com.bigcorp.contractmgt
    ? Operation class name: ContractUpdater
    ? Operation label: Contract Updater
    
  3. Update the dependencies:

    • IntelliJ IDEA In IntelliJ IDEA, click on Import Changes in the Maven's popup.

    • Eclipse

      1. In a terminal:

        $ mvn eclipse:eclipse
        
      2. In Eclipse, then right-click on the project and click Refresh (F5).

  4. Nuxeo CLI will generate a new Java class for the operation at contract-mgt-project/contract-mgt-project-core/src/main/java/com/bigcorp/contractmgt/ContractUpdater.java like so:

     package com.bigcorp.contractmgt;
    
     import org.apache.commons.lang3.StringUtils;
     import org.nuxeo.ecm.automation.core.Constants;
     import org.nuxeo.ecm.automation.core.annotations.Context;
     import org.nuxeo.ecm.automation.core.annotations.Operation;
     import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
     import org.nuxeo.ecm.automation.core.annotations.Param;
     import org.nuxeo.ecm.core.api.CoreSession;
     import org.nuxeo.ecm.core.api.DocumentModel;
     import org.nuxeo.ecm.core.api.PathRef;
    
     /**
      *
      */
     @Operation(id=ContractUpdater.ID, category=Constants.CAT_DOCUMENT, label="Contract Updater", description="Describe here what your operation does.")
     public class ContractUpdater {
         public static final String ID = "Document.ContractUpdater";
         @Context
         protected CoreSession session;
         @Param(name = "path", required = false)
         protected String path;
         @OperationMethod
         public DocumentModel run() {
             if (StringUtils.isBlank(path)) {
                 return session.getRootDocument();
             } else {
                 return session.getDocument(new PathRef(path));
             }
         }
     }
    
  5. Time to fill in the skeleton and start coding! Here is the final result of ContractUpdater.java:

    package com.bigcorp.contractmgt;
    
    import java.util.Calendar;
    import org.nuxeo.ecm.automation.core.Constants;
    import org.nuxeo.ecm.automation.core.annotations.Operation;
    import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
    import org.nuxeo.ecm.automation.core.collectors.DocumentModelCollector;
    import org.nuxeo.ecm.core.api.DocumentModel;
    import org.nuxeo.ecm.core.api.NuxeoException;
    
    /**
     *
     */
    @Operation(id = ContractUpdater.ID, category = Constants.CAT_DOCUMENT, label = "Contract Updater", description = "On a contract, sets the reminder date to three months after its start date.")
    public class ContractUpdater {
    
        public static final String ID = "Document.ContractUpdater";
        static final String CONTRACT_TYPE = "Contract";
        static final String CONTRACT_SCHEMA = "contract";
        static final String CONTRACT_START = CONTRACT_SCHEMA + ":start";
        static final String CONTRACT_REMINDER = CONTRACT_SCHEMA
                + ":reminder";
    
        @OperationMethod(collector = DocumentModelCollector.class)
        public DocumentModel run(DocumentModel input) throws NuxeoException {
            if (!(CONTRACT_TYPE.equals(input.getType()))) {
                throw new NuxeoException("Operation works only with "
                        + CONTRACT_TYPE + " document type.");
            }
    
            Calendar start = (Calendar) input.getPropertyValue(CONTRACT_START);
            Calendar reminder = (Calendar) start.clone();
            reminder.add(Calendar.MONTH, 3);
            input.setPropertyValue(CONTRACT_REMINDER, reminder.getTime());
            return input;
        }
    }
    

Update the Unit Test

Nuxeo CLI automatically created a unit test class for the Operation at contract-mgt-project/contract-mgt-project-core/src/test/java/com/bigcorp/contractmgt/TestContractUpdater.java. This test must be made to pass in order to compile and deploy your project.

  1. Create a "dummy" component to account for necessary Studio requirements, e.g. fakestudio-component.xml at contract-mgt-project/contract-mgt-project-core/src/test/resources.

  2. Paste the following into fakestudio-component.xml:

     <component name="com.nuxeo.studio.fake">
       <alias>org.nuxeo.ecm.directory.sql.storage</alias>
       <alias>org.nuxeo.runtime.started</alias>
     </component>
    
  3. Replace TestContractUpdater.java with the following code:

    package com.bigcorp.contractmgt;
    
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertNotNull;
    import static org.junit.Assert.assertNull;
    
    import java.util.Calendar;
    import java.util.GregorianCalendar;
    import javax.inject.Inject;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.nuxeo.ecm.automation.test.AutomationFeature;
    import org.nuxeo.ecm.core.api.CoreSession;
    import org.nuxeo.ecm.core.api.DocumentModel;
    import org.nuxeo.ecm.core.test.DefaultRepositoryInit;
    import org.nuxeo.ecm.core.test.annotations.Granularity;
    import org.nuxeo.ecm.core.test.annotations.RepositoryConfig;
    import org.nuxeo.runtime.test.runner.Deploy;
    import org.nuxeo.runtime.test.runner.Features;
    import org.nuxeo.runtime.test.runner.FeaturesRunner;
    import org.nuxeo.runtime.test.runner.LocalDeploy;
    
    @RunWith(FeaturesRunner.class)
    @Features(AutomationFeature.class)
    @RepositoryConfig(init = DefaultRepositoryInit.class, cleanup = Granularity.METHOD)
    
    // Be sure to replace studio.extensions.MAVEN-ARTIFACT-ID
    // with your Studio project's symbolic name.
    // You can find it in Studio:
    // Settings / Application Information / Maven Artifact id field
    @Deploy({"com.bigcorp.contractmgt.contract-mgt-project-core", "studio.extensions.MAVEN-ARTIFACT-ID"})
    @LocalDeploy({ "com.bigcorp.contractmgt.contract-mgt-project-core:fakestudio-component.xml" })
    public class TestContractUpdater {
          @Inject
          protected CoreSession session;
          @Test
          public void shouldCallTheAutomationChain() {
              // Create a contract, currently stored in memory
              DocumentModel doc = session.createDocumentModel("/default-domain", "my-test-doc", ContractUpdater.CONTRACT_TYPE);
              GregorianCalendar now = new GregorianCalendar();
              doc.setPropertyValue(ContractUpdater.CONTRACT_START, now);
    
              // At this stage, the reminder date should be empty
              assertNull(doc.getPropertyValue(ContractUpdater.CONTRACT_REMINDER));
    
              // We'll save the document in the database which will
              // trigger an event handler that sets the reminder date
              doc = session.createDocument(doc);
              session.save();
    
              // Now we'll check that the reminder date is set as expected
              int currentMonth = now.get(Calendar.MONTH);
              GregorianCalendar reminder = (GregorianCalendar) doc.getPropertyValue(ContractUpdater.CONTRACT_REMINDER);
              assertNotNull("Reminder date is not set, check your automation chain.", reminder);
              assertEquals("Reminder date is not set in three months from now", (((currentMonth + 3) > 12) ? (currentMonth - 9):(currentMonth + 3)), reminder.get(Calendar.MONTH));
          }
    }
    
  4. Replace "MAVEN-ARTIFACT-ID" in studio.extensions.MAVEN-ARTIFACT-ID with your Studio project's symbolic name.

    Note: To get the symbolic name go to Settings > Application Information in Nuxeo Studio and use the value found in the Maven Artifact id field.

If you try running the test (in Eclipse, right-click on your project and choose Run As, JUnit Test, or Run TestContractUpdater in IntelliJ IDEA), you will notice that the test fails because our Studio project is missing a few things. We need to add them to make the test pass.

Send the Operation to Studio

  1. Build a JAR file (without running the tests); from the contract-mgt-project folder run:

     $ mvn -DskipTests package
    
  2. Deploy the JAR (contract-mgt-project/contract-mgt-project-core/target/contract-mgt-project-core-1.0-SNAPSHOT.jar) in your Nuxeo server by copying it to $NuxeoServer/nxserver/bundles, then restart your server.

  3. Go to the local automation documentation at <server>/nuxeo/site/automation/doc (for example http://NUXEO_SERVER/nuxeo/site/automation/doc).

  4. In the Document category click Contract Updater, then click on the JSON definition link and copy the operation definition.

  5. In Nuxeo Studio go to Settings > Registries > Automation Operations and paste the operation definition into the "operations": [] array, for example:

     { "operations": [
      {
       "id" : "Document.ContractUpdater",
       "label" : "Contract Updater",
       "category" : "Document",
       "requires" : null,
       "description" : "On a contract, sets the reminder date to three months after its start date.",
       "url" : "Document.ContractUpdater",
       "signature" : [ "document", "document", "documents", "documents" ],
       "params" : [ ]
      }
     ] }
    

The operation is now available in Automation Chain editor, under the Document category.

Step 3 - Create Your Chain in Nuxeo Studio

Create an Automation Chain

  1. In the Studio menu Automation > Automation Chains, click on New.

  2. Call your chain ContractUpdater.

  3. Leave the Fetch > Context Document(s) operation and add the custom operation, available in Document > ContractUpdater.

  4. Click on Save.

Create an Event Handler

Now create an Event Handler in order to call your operation when a contract is created.

  1. In the Studio menu Automation > Event Handlers, click on New.

  2. Call the event handler SetReminderDate.

  3. Fill in the creation wizard:

    • Events: Select About to create.
    • Current document has one of the types: Select your document type Contract.
    • Event Handler Execution: Choose your automation chain ContractUpdater.
  4. Click on Save.

Now you can try it on your server either by running the unit tests or by testing manually.

Step 4 - Test the Code

The code can either be tested through unit tests or manually. You need to bind the Studio project first to have it deployed during the unit tests or on the server when testing manually.

Bind the Studio Project

  1. In Nuxeo Studio, under Source Control > Branch Management, release the most recent commit on your project. This will generate a version of your project that can be accessed by Maven.

  2. It is strongly recommended that you encrypt your Studio password (aka your Nuxeo Connect account password):

    1. Create a master password:

       mvn --encrypt-master-password
      

      The command will prompt you for your master password and produce an encrypted version, something like this:

       {jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ=}
      
    2. Store this password in ~/.m2/settings-security.xml like this:

       <settingsSecurity>
         <master>{jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ=}</master>
       </settingsSecurity>
      
    3. Encrypt your Studio password:

       mvn --encrypt-password
      
    4. Store the encrypted Studio password in your ~/.m2/settings.xml file as below:

       <servers>
         ....
         <server>
           <id>nuxeo-studio</id>
           <username>your_studio_username</username>
           <password>your_encrypted_studio_password</password>
         </server>
         ...
       </servers>
      

      This configures your Maven client to use authentication when accessing the Studio Maven repository.

  3. Edit the pom.xml file in contract-mgt-project/contract-mgt-project-core to declare the dependency on the Studio project you just made a release of:

     <dependencies>
       ...
       <dependency>
         <groupId>nuxeo-studio</groupId>
         <artifactId>myproject</artifactId>
         <version>0.0.1</version>
       </dependency>
       ...
     </dependencies>
    

    The value for artifactId is identical to the MAVEN-ARTIFACT-ID we referenced before. Use the version value from the release you created.

  4. Update the project dependencies:

    • IntelliJ IDEA

      • IntelliJ IDEA will detect the change automatically, click on Import Changes in the Maven popup.
    • Eclipse

      1. In a terminal:

        $ mvn eclipse:eclipse
        
      2. In Eclipse, then right-click on the project and click Refresh (F5).

Using Unit Tests

  1. Right-click on your unit test class and choose Run As, JUnit Test in Eclipse, or Run TestContractUpdater in IntelliJ IDEA. The tests should now pass.

Using unit tests is the recommended way to ensure a feature is working as expected. Unit tests are triggered automatically whenever you compile your project using Maven, and as such they help you in maintaining a high quality level.

Testing Manually

  1. Start your Nuxeo server. Update your Studio package from the Update Center.

  2. Create a new Contract with the following information:

    • Title: Maintenance Contract
    • Contract Owner: Nuxeo
    • Starting Date: Choose today's date. You should end up with something like this:

That's it! You are ready to develop on the Nuxeo Platform.

What to do Next

You should learn to package and deploy your application in a Nuxeo Platform instance.