Nuxeo / DAM / PAM / ECM specialistsContact

Nuxeo

An open POST endpoint for Nuxeo integrations

Receive a completion callback from an external archiving system using Nuxeo WebEngine, JAX-RS and a custom JSON payload.

Talk to a Maretha Consultant

Tell us what you're struggling with, and we'll tell you how we can help you.

Talk to us
External archiving systemRestore completes
POST/site/syncstatus/update
NuxeoProcess the payload
The external system calls Nuxeo when the restore job has finished.
In this article

Whether you're using Nuxeo as an ECM or DAM in your organization, there's almost always a need to integrate with external systems.

Sometimes, those systems need to call a publicly accessible endpoint on your Nuxeo server-like to notify you when a job is done or to push data back into the platform.

Let me show you how easy it is to create such an endpoint using Nuxeo WebEngine with JAX-RS.

Use Case

In this example, I was working on an integration with an external archiving system. We submit restore requests from Nuxeo, and when the restore is completed on their end, their system needs to call an open URL on the Nuxeo side to let us know that the job is finished.

The endpoint must support:

  • Unauthenticated access
  • A POST request
  • A custom JSON payload

Objective

We want to add an open, unauthenticated endpoint that accepts a POST request like this:

Code
POST http://localhost:8080/nuxeo/site/syncstatus/update

With the payload:

JSON
{
  "jobId": "3da5bea3-4a09-3180-8c72-ad290b80f08c",
  "eventClass": "JOB",
  "status": "COMPLETE"
}

Step 1: Contribute the Open URL

Nuxeo supports JAX-RS via WebEngine, which is exactly what we'll use to expose this API: ๐Ÿ“˜ https://doc.nuxeo.com/nxdoc/webengine-jax-rs/

In your XML contribution, register the open URL:

XML
<extension point="openUrl"
    target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService">
  <openUrl name="syncstatus">
    <grantPattern>${org.nuxeo.ecm.contextPath}/site/syncstatus</grantPattern>
  </openUrl>
</extension>

Step 2: Create the Endpoint Class

Create a new class extending ModuleRoot, mapped to /syncstatus. ๐Ÿ“˜ Reference: ModuleRoot.java

Java
@Path("/syncstatus")
@Produces("text/html;charset=UTF-8")
@WebObject(type = "syncstatus")
/**
 * Open endpoint to receive callback
 */
public class SyncStatus extends ModuleRoot {

    public static final Log log = LogFactory.getLog(SyncStatus.class);

    @Context
    private HttpServletRequest request;

    @POST
    @Path("update")
    @Produces(MediaType.APPLICATION_JSON)
    public Response syncJob(JobPayload request) {
        return syncJob(request.getJobId(), request.getEventClass());
    }
}

Step 3: Create the Payload Class

Java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class JobPayload {

    private String jobId;
    private String eventClass;
    private String status;

    public JobPayload() {}

    public String getJobId() { return jobId; }
    public void setJobId(String jobId) { this.jobId = jobId; }

    public String getEventClass() { return eventClass; }
    public void setEventClass(String eventClass) { this.eventClass = eventClass; }

    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
}

Step 4: Register the Custom JSON Reader

Java
@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class JobPayloadReader implements MessageBodyReader<JobPayload> {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return JobPayload.class.isAssignableFrom(type)
            && MediaType.APPLICATION_JSON_TYPE.isCompatible(mediaType);
    }

    @Override
    public JobPayload readFrom(Class<JobPayload> type, Type genericType,
                               Annotation[] annotations, MediaType mediaType,
                               MultivaluedMap<String, String> httpHeaders,
                               InputStream entityStream) throws IOException {

        return objectMapper.readValue(entityStream, JobPayload.class);
    }
}

Result

That's it! You now have an open Nuxeo endpoint that accepts POST requests with a custom JSON payload.

Access considerations

  • Only use unauthenticated endpoints when absolutely necessary.
  • If the endpoint should be private, simply don't contribute it via openUrl.

Test the endpoint

cURL
curl -X POST http://localhost:8080/nuxeo/site/syncstatus/update \
  -H "Content-Type: application/json" \
  -d '{
    "jobId": "3da5bea3-4a09-3180-8c72-ad290b80f08c",
    "eventClass": "JOB",
    "status": "COMPLETE"
  }'

Let me know if you've built similar integrations or want to explore more advanced patterns like token auth or validation.

Work with Maretha

Working through a content platform challenge?

Bring your platform, constraints and questions. Let us help you work through the approach.

Discuss your project โ†—

โ† All insights