Creating your own Rest Plugin ( extending REST API )

The Rest Plugin feature helps you to extending the REST API.

You can create your own Form Validator

Conditions:

  • The new Rest class must implement the "RestPlugin" interface.
  • The new Rest class must be declared under the package "com.openkm.plugin.rest".
  • The new Rest class must be annotated with "@PluginImplementation".
  • The new Rest class must extend of "BasePlugin".

Rest interface:

package com.openkm.plugin.rest;

import net.xeoh.plugins.base.Plugin;

import java.io.InputStream;
import java.util.Map;

/**
 * RestPlugin
 */
public interface RestPlugin extends Plugin {

    Object executePlugin(Map<String, String> parameters, InputStream is) throws Exception;

}

The new class must be located into the package com.openkm.plugin.rest because the application plugins system will try to load it from there.

Do not miss the tag @PluginImplementation otherwise the application plugin system won't be able to use the class.

More information at Register a new plugin.

Method description

MethodTypeDescription
executePlugin String

The method executes a REST plugin an return an object.

Example of REST plugin implementation

package com.openkm.plugin.rest;

import com.openkm.plugin.BasePlugin;
import net.xeoh.plugins.base.annotations.PluginImplementation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.Map;

/**
 * Sample rest plugin
 */
@PluginImplementation
public class TestRestPlugin extends BasePlugin implements RestPlugin {

    private static Logger log = LoggerFactory.getLogger(TestRestPlugin.class);

    @Override
    public Object executePlugin(Map<String, String> parameters, InputStream is) throws Exception {
        log.debug("executePlugin({})", parameters);
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("className=").append(this.getClass().getCanonicalName());
        for (String key : parameters.keySet()) {
            sb.append(", " + key + "=").append(parameters.get(key));
        }
        return sb.toString();
    }
}

Example of REST plugin implementation what returns file

The sample below goes in combination of REST webservice "/rest/plugin/executeGetPluginReturnFile" method.

package com.openkm.plugin.rest;

import com.openkm.bean.Document;
import com.openkm.module.db.DbDocumentModule;
import com.openkm.plugin.BasePlugin;
import com.openkm.util.PathUtils;
import net.xeoh.plugins.base.annotations.PluginImplementation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.io.InputStream;
import java.util.Map;

/**
 * Sample rest plugin
 */
@PluginImplementation
public class TestGetDocumentRestPlugin extends BasePlugin implements RestPlugin {

    private static Logger log = LoggerFactory.getLogger(TestGetDocumentRestPlugin.class);

    @Autowired
    private DbDocumentModule dbDocumentModule;

    @Autowired
    private PathUtils pathUtils;

    @Override
    public Object executePlugin(Map<String, String> parameters, InputStream is) throws Exception {
        log.debug("executePlugin({})", parameters);
        String docId = parameters.get("docId");
        boolean inline = parameters.containsKey("inline");
        Document doc = dbDocumentModule.getProperties(null, docId);
        String mimeType = doc.getMimeType();
        String fileName = pathUtils.getName(doc.getPath());
        InputStream isContent = dbDocumentModule.getContent(null, docId, false);

        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.add("Content-Type", mimeType);

        // inline true when you want to embedded the content into
        if (inline) {
            responseHeaders.add("Content-disposition", "inline; filename=\"" + fileName + "\"");
        } else {
            responseHeaders.add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        }

        responseHeaders.setContentLength(doc.getActualVersion().getSize());
        InputStreamResource inputStreamResource = new InputStreamResource(isContent);
        log.debug("TestGetDocumentRestPlugin: [BINARY]");
        return new ResponseEntity<>(inputStreamResource, responseHeaders, HttpStatus.OK);
    }
}

Objects declaration

The method executionPlugin returns an Object. This object will be processed - marshall - by OpenKM. Basic types like String, Integer, etc. can be directly used, but for complex objects you need to use annotation on classes.

Marshall and unmarshall strategies:

  • The result of the executed method is always an String. Internally the method converts the complex objects to JSON.
  • The result of the executed mehod is a complex Object.

String scenario

From the plugin side - server side - you must convert the Object to JSON:

return new Gson().toJson(someObject)

From the cliend side you must convert JSON String to Object:

// value contains the returned String value by the REST call
SomeObject someObject = new Gson().fromJson(value, SomeObject.class);

Complex Object scenario

You can use existing OpenKM classes like Document, Folder, Record, Mail among others, because theses clases have already the annotation.

Use the anotation ( in the sample below the object will be marshalled with name "mail" ):

@XmlRootElement(name = "mail")

Example of complex classes:

Return a list of Activities:

package com.openkm.ws.rest.util;

import com.openkm.db.bean.Activity;

import java.util.ArrayList;
import java.util.List;

public class ActivityList {

    List<Activity> activities = new ArrayList<>();

    private int totalElements;

    public List<Activity> getActivities() {
        return activities;
    }

    public void setActivities(List<Activity> activities) {
        this.activities = activities;
    }

    public int getTotalElements() {
        return totalElements;
    }

    public void setTotalElements(int totalElements) {
        this.totalElements = totalElements;
    }
}

Complex object:

package com.openkm.bean;

public class Record extends Parent {

    private static final long serialVersionUID = 1L;
    public static final String TYPE = "okm:record";
    public static final String NAME = "okm:name";

    private boolean locked;
    private LockInfo lockInfo;
    private String title;
    private boolean hasLinks;

    public boolean isLocked() {
        return locked;
    }

    public void setLocked(boolean locked) {
        this.locked = locked;
    }

    public LockInfo getLockInfo() {
        return lockInfo;
    }

    public void setLockInfo(LockInfo lockInfo) {
        this.lockInfo = lockInfo;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean hasLinks() {
        return hasLinks;
    }

    public void setHasLinks(boolean hasLinks) {
        this.hasLinks = hasLinks;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("uuid=").append(uuid);
        sb.append(", parent=").append(parent);
        sb.append(", path=").append(path);
        sb.append(", permissions=").append(permissions);
        sb.append(", created=").append(created == null ? null : created.getTime());
        sb.append(", subscribed=").append(subscribed);
        sb.append(", subscriptors=").append(subscriptors);
        sb.append(", keywords=").append(keywords);
        sb.append(", categories=").append(categories);
        sb.append(", notes=").append(notes);
        sb.append(", locked=").append(locked);
        sb.append(", lockInfo=").append(lockInfo);
        sb.append(", title=").append(title);
        sb.append(", nodeClass=").append(getNodeClass());
        sb.append(", nodeClassChildren=").append(getNodeClassChildren());
        sb.append(", dispositionCurrentStage=").append(getDispositionCurrentStage());
        sb.append("}");
        return sb.toString();
    }
}

Advanced REST sample

Because this sample uses low-level API, you should always ask OpenKM IT staff before using it on production for understanding the benefits and restrictions of this kind of implementation.

The next sample is a project that uses low-level API implementation for quick upload and set metadata at the same time. A sample comes with plugin implementation and client plugin test.

If you have a huge uploading process and are looking for performance this is a good approach.