Creating your own REST plugin (extending the REST API)
The REST plugin feature helps you extend 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 "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 in the package com.openkm.plugin.rest because the application plugin system will try to load it from there.
Do not omit 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
Method | Type | Description |
---|---|---|
executePlugin | String |
The method executes a REST plugin and returns 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 that returns a file
The sample below is used in combination with the 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 executePlugin returns an Object. This object will be processed - marshalled - by OpenKM. Basic types like String, Integer, etc. can be used directly, but for complex objects you need to use annotations on classes.
Marshalling and unmarshalling strategies:
- The result of the executed method is always a String. Internally the method converts complex objects to JSON.
- The result of the executed method 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 client side you must convert the JSON string to an 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 these classes already have the annotation.
Use the annotation (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 a low-level API, you should always ask OpenKM IT staff before using it in production to understand the benefits and restrictions of this kind of implementation.
The next sample is a project that uses a low-level API implementation for quick upload and setting metadata at the same time. The sample comes with a plugin implementation and a client plugin test.
If you have a large upload process and are looking for better performance, this is a good approach.
- Download the sample project sample-rest.zip.