Creating your own Rest Plugin ( extending REST API )

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

To create your own REST plugin you must create a new class that implements RestPlugin interface

public interface RestPlugin extends Plugin {
    
    Object executePlugin(Map<String, String> parameters, InputStream is);
    
}

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. See the sample below: 

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 java.util.Map;

import net.xeoh.plugins.base.annotations.PluginImplementation;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Sample REST plugin
 * 
 */
@PluginImplementation
public class TestRestPlugin implements RestPlugin {
    private static Logger log = LoggerFactory.getLogger(TestRestPlugin.class);

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

sb.append("}"); 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.integration.cifs.device.PathUtils;
import com.openkm.module.DocumentModule;
import com.openkm.module.ModuleManager;
import net.xeoh.plugins.base.annotations.PluginImplementation;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;

/**
 * Sample rest plugin
 */
@PluginImplementation
public class TestGetDocumentRestPlugin implements RestPlugin {
	private static Logger log = LoggerFactory.getLogger(TestGetDocumentRestPlugin.class);

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

		StreamingOutput stream = new StreamingOutput() {
			@Override
			public void write(OutputStream os) throws IOException, WebApplicationException {
				IOUtils.copy(isContent, os);
				IOUtils.closeQuietly(isContent);
				IOUtils.closeQuietly(os);
			}
		};

		Response.ResponseBuilder response = Response.ok(stream);
		response.type(mimeType); // because the method produces MediaType.APPLICATION_OCTET_STREAM can be set any document mime type

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

		return response;
	}
} 

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 Strings:

package com.openkm.ws.rest.util;

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

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "languages")
public class LanguageList {
    @XmlElement(name = "language", required = true)
    List<String> languages = new ArrayList<String>();
    
    public List<String> getList() {
        return languages;
    }
}

Return a list of Mails:

package com.openkm.ws.rest.util;

import com.openkm.bean.Mail;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;

@XmlRootElement(name = "mails")
public class MailList {
    @XmlElement(name = "mail", required = true)
    List<Mail> mails = new ArrayList<Mail>();
    
    public List<Mail> getList() {
        return mails;
    }
}

Complex object:

package com.openkm.bean;

import javax.xml.bind.annotation.XmlRootElement;

/**
 * Record
 */
@XmlRootElement(name = "record")
public class Record {
    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;
    
    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 String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        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(", uuid=").append(uuid);
        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("}");
        return sb.toString();
    }
}