Select Git revision
AbstractCaosDBServerResource.java
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
AbstractCaosDBServerResource.java 12.39 KiB
/*
* ** header v3.0
* This file is a part of the CaosDB Project.
*
* Copyright (C) 2018 Research Group Biomedical Physics,
* Max-Planck-Institute for Dynamics and Self-Organization Göttingen
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* ** end header
*/
package caosdb.server.resource;
import static caosdb.server.utils.Utils.isNonNullInteger;
import static java.net.URLDecoder.decode;
import caosdb.server.CaosDBException;
import caosdb.server.accessControl.Principal;
import caosdb.server.database.backend.implementation.MySQL.ConnectionException;
import caosdb.server.entity.Message;
import caosdb.server.utils.ServerMessages;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.restlet.data.Form;
import org.restlet.data.Header;
import org.restlet.data.MediaType;
import org.restlet.data.Parameter;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.resource.Delete;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.restlet.resource.Put;
import org.restlet.resource.ServerResource;
import org.restlet.util.Series;
/**
* Class is still under construction.
*
* @author Timm Fitschen
*/
public abstract class AbstractCaosDBServerResource extends ServerResource {
private final HashMap<String, String> flags = new HashMap<String, String>();
private Long timestamp = null;
private static final XMLParser xmlparser = new XMLParser();
protected String sRID = null;
protected String cRID = null;
private String[] requestedItems = null;
private ArrayList<Integer> requestedIDs = new ArrayList<Integer>();
private ArrayList<String> requestedNames = new ArrayList<String>();
private String xslScript = "webcaosdb.xsl";
public static class xmlNotWellFormedException extends Exception {
private static final long serialVersionUID = -6836378704013776849L;
}
public String getSRID() {
return this.sRID;
}
public Long getTimestamp() {
return this.timestamp;
}
public Subject getUser() {
return SecurityUtils.getSubject();
}
@Override
protected void doInit() {
if (getRequestEntity().isTransient() && !getRequestEntity().isEmpty()) {
final ReReadableRepresentation r = new ReReadableRepresentation(getRequestEntity());
getRequest().setEntity(r);
}
this.timestamp = getRequest().getDate().getTime();
this.sRID = getRequest().getAttributes().get("SRID").toString();
final Series<Header> headers = getRequest().getHeaders();
this.cRID = headers.getFirstValue("crequestid");
String specifier = (String) getRequestAttributes().get("specifier");
if (specifier != null && !specifier.equals("")) {
try {
specifier = decode(specifier, "UTF-8");
} catch (final UnsupportedEncodingException e) {
// this should never happen
e.printStackTrace();
System.exit(1);
}
this.requestedItems = specifier.split("&");
for (final String requestedItem : this.requestedItems) {
if (isNonNullInteger(requestedItem)) {
final int id = Integer.parseInt(requestedItem);
if (id > 0) {
getRequestedIDs().add(id);
}
} else if (requestedItem.equalsIgnoreCase("all")) {
getRequestedNames().clear();
getRequestedIDs().clear();
getRequestedNames().add("all");
break;
} else {
getRequestedNames().add(requestedItem);
}
}
}
// flags
final Form queryAsForm = getRequest().getResourceRef().getQueryAsForm(true);
if (queryAsForm != null) {
for (final Parameter p : queryAsForm) {
getFlags()
.put(
p.getName(),
(p.getValue() == null || p.getValue().isEmpty() ? null : p.getValue()));
}
}
}
@Override
protected void doRelease() {}
protected Element generateRootElement() {
final Element retRoot = new Element("Response");
if (getUser() != null && getUser().isAuthenticated()) {
retRoot.setAttribute("username", ((Principal) getUser().getPrincipal()).getUsername());
retRoot.setAttribute("realm", ((Principal) getUser().getPrincipal()).getRealm());
}
retRoot.setAttribute("srid", getSRID());
if (this.cRID != null) {
retRoot.setAttribute("crid", this.cRID);
}
retRoot.setAttribute("timestamp", getTimestamp().toString());
retRoot.setAttribute("baseuri", getRootRef().toString());
return retRoot;
}
@Get
public Representation httpGet() {
try {
return httpGetInChildClass();
} catch (final Throwable t) {
return handleThrowable(t);
}
}
protected abstract Representation httpGetInChildClass()
throws ConnectionException, IOException, SQLException, CaosDBException,
NoSuchAlgorithmException, Exception;
@Post
public Representation httpPost(final Representation entity) {
try {
return httpPostInChildClass(entity);
} catch (final Throwable t) {
return handleThrowable(t);
}
}
@Put
public Representation httpPut(final Representation entity) {
try {
// catch empty post entity.
if (entity == null) {
return error(ServerMessages.REQUEST_BODY_EMPTY, Status.CLIENT_ERROR_BAD_REQUEST);
} else {
return httpPutInChildClass(entity);
}
} catch (final Throwable t) {
return handleThrowable(t);
}
}
protected Representation httpPutInChildClass(final Representation entity)
throws ConnectionException, JDOMException, Exception, xmlNotWellFormedException {
getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
return null;
}
protected Representation httpDeleteInChildClass()
throws ConnectionException, SQLException, CaosDBException, IOException,
NoSuchAlgorithmException, Exception {
getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
return null;
}
@Delete
public Representation httpDelete() {
try {
return httpDeleteInChildClass();
} catch (final Throwable t) {
return handleThrowable(t);
}
}
protected Representation httpPostInChildClass(final Representation entity)
throws ConnectionException, SQLException, CaosDBException, IOException,
NoSuchAlgorithmException, xmlNotWellFormedException, JDOMException, Exception {
getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
return null;
}
public Document parseEntity(final Representation entity)
throws xmlNotWellFormedException, IOException {
return parseEntity(entity.getStream());
}
public Document parseEntity(final InputStream stream)
throws xmlNotWellFormedException, IOException {
try {
return xmlparser.parse(stream);
} catch (final JDOMException e) {
throw new xmlNotWellFormedException();
}
}
protected String getXSLScript() {
return this.xslScript;
}
protected void setXSLScript(final String s) {
this.xslScript = s;
}
protected JdomRepresentation ok(Element root) {
return ok(new Document(root));
}
protected JdomRepresentation ok(final Document doc) {
return new JdomRepresentation(doc, MediaType.TEXT_XML, " ", getReference(), getXSLScript());
}
protected JdomRepresentation error(final Message m, final Status status) {
final Document doc = new Document();
final Element root = generateRootElement();
root.addContent(m.toElement().setName("Error"));
doc.setRootElement(root);
getResponse().setStatus(status);
return new JdomRepresentation(doc, MediaType.TEXT_XML, " ", getReference(), getXSLScript());
}
protected JdomRepresentation error(final Message m) {
return error(m, Status.SERVER_ERROR_INTERNAL);
}
protected JdomRepresentation warning(final Message m) {
final Document doc = new Document();
final Element root = generateRootElement();
root.addContent(m.toElement().setName("Warning"));
doc.setRootElement(root);
return new JdomRepresentation(doc, MediaType.TEXT_XML, " ", getReference(), getXSLScript());
}
protected JdomRepresentation noWellFormedNess() {
return error(ServerMessages.REQUEST_BODY_NOT_WELLFORMED);
}
protected JdomRepresentation emptyEntity() {
return error(ServerMessages.REQUEST_BODY_EMPTY);
}
protected JdomRepresentation connectionFailed() {
return error(ServerMessages.CANNOT_CONNECT_TO_DATABASE);
}
public JdomRepresentation handleThrowable(final Throwable t) {
try {
getRequest().getAttributes().put("THROWN", t);
throw t;
} catch (final AuthenticationException e) {
getResponse().setStatus(Status.CLIENT_ERROR_FORBIDDEN);
return null;
} catch (final AuthorizationException e) {
getResponse().setStatus(Status.CLIENT_ERROR_FORBIDDEN);
return null;
} catch (final Message m) {
return error(m, Status.CLIENT_ERROR_BAD_REQUEST);
} catch (final FileUploadException e) {
return error(ServerMessages.FILE_UPLOAD_FAILED);
} catch (final ConnectionException e) {
return connectionFailed();
} catch (final xmlNotWellFormedException e) {
return noWellFormedNess();
} catch (final JDOMException e) {
return noWellFormedNess();
} catch (final Throwable e) {
getLogger().log(Level.SEVERE, "UNKNOWN ERROR", e);
return error(ServerMessages.UNKNOWN_ERROR(getSRID()));
}
}
public ArrayList<Integer> getRequestedIDs() {
return this.requestedIDs;
}
public void setReqestedIDs(final ArrayList<Integer> requestedIDs) {
this.requestedIDs = requestedIDs;
}
public ArrayList<String> getRequestedNames() {
return this.requestedNames;
}
public void setRequestedNames(final ArrayList<String> requestedNames) {
this.requestedNames = requestedNames;
}
public HashMap<String, String> getFlags() {
return this.flags;
}
protected Element generateRootElement(Element... elements) {
Element root = generateRootElement();
for (Element e : elements) {
root.addContent(e);
}
return root;
}
public static class XMLParser {
private final LinkedList<SAXBuilder> pool = new LinkedList<SAXBuilder>();
private final int max = 25;
private final int init = 5;
private final int min = 5;
private SAXBuilder getSAXBuilder() {
try {
synchronized (this.pool) {
return this.pool.removeFirst();
}
} catch (final NoSuchElementException e) {
}
preLoad(this.min);
return new SAXBuilder();
}
private void release(final SAXBuilder sb) {
synchronized (this.pool) {
if (this.pool.size() <= this.max) {
this.pool.add(sb);
}
}
}
public XMLParser() {
preLoad(this.init);
}
public Document parse(final InputStream is) throws JDOMException, IOException {
final SAXBuilder sb = getSAXBuilder();
Document ret;
try {
ret = sb.build(is);
} finally {
release(sb);
}
return ret;
}
public void preLoad(final int i) {
final Thread t =
new Thread() {
@Override
public void run() {
for (int j = 0; j < i; j++) {
final SAXBuilder sb = new SAXBuilder();
release(sb);
}
}
};
t.start();
}
}
}