diff --git a/CHANGELOG.md b/CHANGELOG.md index 6898772f8d7299037aa4806c036e9a0839e74ae3..ed30234ef57ec758fa3dee597087292951c9b182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The sever by default now only serves TLS 1.2 and 1.3, all previous versions have been disabled in the default settings. Make sure that your clients (especially the Python client) are up to date. +* Update of Restlet library to 2.4.2 ### Deprecated diff --git a/pom.xml b/pom.xml index ada3403287a657f7684ba8984c57cc80a51820a9..502b5bab4eb850fda88d5c7c80cd7a3add30a59f 100644 --- a/pom.xml +++ b/pom.xml @@ -31,6 +31,7 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.testSourceDirectory>src/test/java</project.build.testSourceDirectory> + <restlet-version>2.4.2</restlet-version> </properties> <repositories> <repository> @@ -80,12 +81,12 @@ <dependency> <groupId>org.restlet.jse</groupId> <artifactId>org.restlet</artifactId> - <version>2.3.12</version> + <version>${restlet-version}</version> </dependency> <dependency> <groupId>org.restlet.jse</groupId> <artifactId>org.restlet.ext.fileupload</artifactId> - <version>2.3.12</version> + <version>${restlet-version}</version> </dependency> <dependency> <groupId>mysql</groupId> @@ -120,7 +121,7 @@ <dependency> <groupId>org.restlet.jse</groupId> <artifactId>org.restlet.ext.jetty</artifactId> - <version>2.3.12</version> + <version>${restlet-version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> diff --git a/src/main/java/caosdb/server/converter/xml/EntityXMLConverterHelper.java b/src/main/java/caosdb/server/converter/xml/EntityXMLConverterHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..5b425e8d88e2621975783ea99efb48070ee6f72e --- /dev/null +++ b/src/main/java/caosdb/server/converter/xml/EntityXMLConverterHelper.java @@ -0,0 +1,233 @@ +package caosdb.server.converter.xml; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.ProcessingInstruction; +import org.restlet.data.MediaType; +import org.restlet.engine.converter.ConverterHelper; +import org.restlet.engine.resource.VariantInfo; +import org.restlet.representation.Representation; +import org.restlet.representation.Variant; +import org.restlet.resource.Resource; +import caosdb.server.accessControl.AuthenticationUtils; +import caosdb.server.accessControl.Principal; +import caosdb.server.accessControl.UserSources; +import caosdb.server.entity.Entity; +import caosdb.server.entity.EntityInterface; +import caosdb.server.entity.container.Container; +import caosdb.server.entity.container.TransactionContainer; +import caosdb.server.resource.JdomRepresentation; + +public class EntityXMLConverterHelper extends ConverterHelper { + + private static final VariantInfo VARIANT_TEXT_XML = new VariantInfo(MediaType.TEXT_XML); + private static final VariantInfo VARIANT_APPLICATION_XML = new VariantInfo(MediaType.APPLICATION_XML); + private static final List<Class<?>> objectClasses = Collections.singletonList(TransactionContainer.class); + private static final XMLParser xmlParser = new XMLParser(); + + @Override + public List<Class<?>> getObjectClasses(Variant source) { + return objectClasses; + } + + @Override + public List<VariantInfo> getVariants(Class<?> source) throws IOException { + List<VariantInfo> result = null; + + if (source != null && TransactionContainer.class.isAssignableFrom(source)) { + result = addVariant(result, VARIANT_TEXT_XML); + result = addVariant(result, VARIANT_APPLICATION_XML); + } + + return result; + } + + @SuppressWarnings("unchecked") + @Override + public <T> T toObject(Representation source, Class<T> target, Resource resource) + throws IOException { + Document xml; + try { + xml = xmlParser.parse(source.getStream()); + } catch (JDOMException e) { + throw new IOException(e); + } + return (T) createContainer(xml, resource.getAttribute("srid"), resource.getRequest().getDate().getTime(), SecurityUtils.getSubject()); + } + + private Container<? extends EntityInterface> createContainer(Document xml, String sRID, long timestamp, Subject owner) { + TransactionContainer result = new TransactionContainer( owner, timestamp, sRID); + for (Element e : xml.getRootElement().getChildren()) { + result.add(new Entity(e)); + } + return result; + } + + @Override + public Representation toRepresentation(Object source, Variant target, Resource resource) + throws IOException { + return toJdomRepresentation((TransactionContainer) source, resource); + } + + private Representation toJdomRepresentation(TransactionContainer container, Resource resource) { + Document doc = new Document(generateRootElement(container, resource)); + + String xsl = resource.getAttribute("xsl"); + if(xsl != null) { + addStyleSheet(doc, xsl); + } + + return new JdomRepresentation(doc, " "); + } + + /** adds the xslt processing instruction to the document. */ + private void addStyleSheet(Document document, String xslPath) { + final ProcessingInstruction pi = + new ProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"" + xslPath + "\" "); + document.getContent().add(0, pi); + } + + /** + * Creates the XML root. + * + * <p>The XML root node contains: + * + * <p> + * + * <ul> + * <li>User info as per addUserInfo + * <li>The sRID (server-side request ID) + * <li>A timestamp + * <li>The URI to this resource. + */ + protected Element generateRootElement(TransactionContainer container, Resource resource) { + final Element retRoot = new Element("Response"); + + addUserInfo(retRoot, container.getOwner()); + retRoot.setAttribute("srid", container.getRequestId()); + retRoot.setAttribute("timestamp", container.getTimestamp().toString()); + retRoot.setAttribute("baseuri", resource.getRootRef().toString()); + return retRoot; + } + + /** + * Add the user info to the Response Element. + * + * @param retRoot + * @param user + */ + private void addUserInfo(Element retRoot, Subject user) { + + if (user != null && user.isAuthenticated()) { + Element userInfo = new Element("UserInfo"); + if (!user.getPrincipal().equals(AuthenticationUtils.ANONYMOUS_USER.getPrincipal())) { + // TODO: deprecated + addNameAndRealm(retRoot, user); + + // this is the new, correct way + addNameAndRealm(userInfo, user); + } + + addRoles(userInfo, user); + retRoot.addContent(userInfo); + } + } + + /** + * Add all roles of the current user to the user info, like this: + * `<UserInfo><Roles><Role>role1</Role><Role>role2</Role>...</Roles></UserInfo>` + * + * @param userInfo + * @param user + */ + private void addRoles(Element userInfo, Subject user) { + Collection<String> roles = UserSources.resolve(user.getPrincipals()); + if (roles == null) return; + Element rs = new Element("Roles"); + for (String role : roles) { + Element r = new Element("Role"); + r.addContent(role); + rs.addContent(r); + } + userInfo.addContent(rs); + } + + /** + * Add the username and the realm of the current user to the user info (as attributes). + * + * @param userInfo + */ + private void addNameAndRealm(Element userInfo, Subject user) { + userInfo.setAttribute("username", ((Principal) user.getPrincipal()).getUsername()); + userInfo.setAttribute("realm", ((Principal) user.getPrincipal()).getRealm()); + } + + /* + ********************************************************************** + * The following function is stolen from {@link + * org.restlet.engine.converter.StatusInfoHtmlConverter} and adapted. + * + * Licensed under Apache 2.0. + * + * Author: Manuel Boillod. + * Copyright 2005-2019 Talend + * ******************************************************************** + */ + /** + * Indicates if the given variant is compatible with the media types supported by this converter. + * + * @param variant The variant. + * @return True if the given variant is compatible with the media types supported by this + * converter. + */ + protected boolean isCompatible(Variant variant) { + return (variant != null) && (VARIANT_TEXT_XML.isCompatible(variant) + || VARIANT_APPLICATION_XML.isCompatible(variant)); + } + + /* + ********************************************************************** + * The following function is stolen from {@link + * org.restlet.engine.converter.StatusInfoHtmlConverter} and adapted. + * + * Licensed under Apache 2.0. + * + * Author: Manuel Boillod. + * Copyright 2005-2019 Talend + * ******************************************************************** + */ + @Override + public float score(Object source, Variant target, Resource resource) { + float result = -1.0F; + + if (source instanceof Container && isCompatible(target)) { + result = 1.0F; + } + + return result; + } + + /* + ********************************************************************** + * The following function is stolen from {@link + * org.restlet.engine.converter.StatusInfoHtmlConverter} and adapted. + * + * Licensed under Apache 2.0. + * + * Author: Manuel Boillod. + * Copyright 2005-2019 Talend + * ******************************************************************** + */ + @Override + public <T> float score(Representation source, Class<T> target, Resource resource) { + return -1.0F; + } + +} diff --git a/src/main/java/caosdb/server/converter/xml/XMLParser.java b/src/main/java/caosdb/server/converter/xml/XMLParser.java new file mode 100644 index 0000000000000000000000000000000000000000..cd9660439f697a6ffbb4a1d67a17218ae246e426 --- /dev/null +++ b/src/main/java/caosdb/server/converter/xml/XMLParser.java @@ -0,0 +1,65 @@ +package caosdb.server.converter.xml; + +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedList; +import java.util.NoSuchElementException; +import org.jdom2.Document; +import org.jdom2.JDOMException; +import org.jdom2.input.SAXBuilder; + +public 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(); + } + } diff --git a/src/main/java/caosdb/server/entity/container/DeleteContainer.java b/src/main/java/caosdb/server/entity/container/DeleteContainer.java index c219ee5e4399caeee857006f995748435d023963..907e6930fb40e1288d4928e9352b8f758067e09b 100644 --- a/src/main/java/caosdb/server/entity/container/DeleteContainer.java +++ b/src/main/java/caosdb/server/entity/container/DeleteContainer.java @@ -22,9 +22,9 @@ */ package caosdb.server.entity.container; -import caosdb.server.entity.DeleteEntity; -import java.util.HashMap; +import java.util.Map; import org.apache.shiro.subject.Subject; +import caosdb.server.entity.DeleteEntity; public class DeleteContainer extends EntityByIdContainer { @@ -34,7 +34,7 @@ public class DeleteContainer extends EntityByIdContainer { final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { super(user, timestamp, srid, flags); } diff --git a/src/main/java/caosdb/server/entity/container/EntityByIdContainer.java b/src/main/java/caosdb/server/entity/container/EntityByIdContainer.java index 1bfda2024c4ff11375ad619d64946e1036c714b0..cbcb93e983a538ee9fdbd9990041a160d838e0a4 100644 --- a/src/main/java/caosdb/server/entity/container/EntityByIdContainer.java +++ b/src/main/java/caosdb/server/entity/container/EntityByIdContainer.java @@ -22,7 +22,7 @@ */ package caosdb.server.entity.container; -import java.util.HashMap; +import java.util.Map; import org.apache.shiro.subject.Subject; public abstract class EntityByIdContainer extends TransactionContainer { @@ -32,7 +32,7 @@ public abstract class EntityByIdContainer extends TransactionContainer { final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { super(user, timestamp, srid, flags); } diff --git a/src/main/java/caosdb/server/entity/container/InsertContainer.java b/src/main/java/caosdb/server/entity/container/InsertContainer.java index 6810e053cc9234bb587ea35f2399e7ceaa04e408..2d7bd2a266a235b802a305b8e973615817d7f4c5 100644 --- a/src/main/java/caosdb/server/entity/container/InsertContainer.java +++ b/src/main/java/caosdb/server/entity/container/InsertContainer.java @@ -22,10 +22,10 @@ */ package caosdb.server.entity.container; -import caosdb.server.entity.InsertEntity; -import java.util.HashMap; +import java.util.Map; import org.apache.shiro.subject.Subject; import org.jdom2.Element; +import caosdb.server.entity.InsertEntity; public class InsertContainer extends WritableContainer { @@ -33,7 +33,7 @@ public class InsertContainer extends WritableContainer { final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { super(user, timestamp, srid, flags); } diff --git a/src/main/java/caosdb/server/entity/container/RetrieveContainer.java b/src/main/java/caosdb/server/entity/container/RetrieveContainer.java index 9b4f7364d4ecdc63da1be8f1ead292545801a078..04e67d1d05b2a63451eee923617266a35fe1ead1 100644 --- a/src/main/java/caosdb/server/entity/container/RetrieveContainer.java +++ b/src/main/java/caosdb/server/entity/container/RetrieveContainer.java @@ -22,9 +22,9 @@ */ package caosdb.server.entity.container; -import caosdb.server.entity.RetrieveEntity; -import java.util.HashMap; +import java.util.Map; import org.apache.shiro.subject.Subject; +import caosdb.server.entity.RetrieveEntity; public class RetrieveContainer extends EntityByIdContainer { @@ -34,7 +34,7 @@ public class RetrieveContainer extends EntityByIdContainer { final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { super(user, timestamp, srid, flags); } diff --git a/src/main/java/caosdb/server/entity/container/TransactionContainer.java b/src/main/java/caosdb/server/entity/container/TransactionContainer.java index ea4cc02e7d16bda586ff3c700e3f2fb237b15426..311437591b09aeb350b5064271d3023188f3dd90 100644 --- a/src/main/java/caosdb/server/entity/container/TransactionContainer.java +++ b/src/main/java/caosdb/server/entity/container/TransactionContainer.java @@ -22,6 +22,12 @@ */ package caosdb.server.entity.container; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.apache.shiro.subject.Subject; +import org.jdom2.Element; import caosdb.server.CaosDBServer; import caosdb.server.database.misc.TransactionBenchmark; import caosdb.server.entity.Entity; @@ -30,11 +36,6 @@ import caosdb.server.entity.FileProperties; import caosdb.server.entity.xml.ToElementable; import caosdb.server.jobs.JobTarget; import caosdb.server.utils.EntityStatus; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import org.apache.shiro.subject.Subject; -import org.jdom2.Element; public class TransactionContainer extends Container<Entity> implements ToElementable, JobTarget { @@ -57,7 +58,7 @@ public class TransactionContainer extends Container<Entity> implements ToElement final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { this(user, timestamp, srid); setFlags(flags); } @@ -88,9 +89,9 @@ public class TransactionContainer extends Container<Entity> implements ToElement this.messages.add(m); } - private HashMap<String, String> flags = null; + private Map<String, String> flags = null; - public HashMap<String, String> getFlags() { + public Map<String, String> getFlags() { return this.flags; } @@ -174,7 +175,7 @@ public class TransactionContainer extends Container<Entity> implements ToElement return null; } - public void setFlags(final HashMap<String, String> flags) { + public void setFlags(final Map<String, String> flags) { this.flags = flags; } diff --git a/src/main/java/caosdb/server/entity/container/UpdateContainer.java b/src/main/java/caosdb/server/entity/container/UpdateContainer.java index 843e70cf8131428cfb4086c6ee0b6f30c32797cf..f09159a2f62441532934bbe6e20eb22f61001f05 100644 --- a/src/main/java/caosdb/server/entity/container/UpdateContainer.java +++ b/src/main/java/caosdb/server/entity/container/UpdateContainer.java @@ -22,10 +22,10 @@ */ package caosdb.server.entity.container; -import caosdb.server.entity.UpdateEntity; -import java.util.HashMap; +import java.util.Map; import org.apache.shiro.subject.Subject; import org.jdom2.Element; +import caosdb.server.entity.UpdateEntity; public class UpdateContainer extends WritableContainer { @@ -35,7 +35,7 @@ public class UpdateContainer extends WritableContainer { final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { super(user, timestamp, srid, flags); } diff --git a/src/main/java/caosdb/server/entity/container/WritableContainer.java b/src/main/java/caosdb/server/entity/container/WritableContainer.java index 3000aa5a943ec767b5de889899e62ccae311fa8d..623bea6c3d1896905cd74a31d5c906c1e560d045 100644 --- a/src/main/java/caosdb/server/entity/container/WritableContainer.java +++ b/src/main/java/caosdb/server/entity/container/WritableContainer.java @@ -22,10 +22,10 @@ */ package caosdb.server.entity.container; -import caosdb.server.entity.FileProperties; -import java.util.HashMap; +import java.util.Map; import org.apache.shiro.subject.Subject; import org.jdom2.Element; +import caosdb.server.entity.FileProperties; public abstract class WritableContainer extends TransactionContainer { private static final long serialVersionUID = -4097777313518959519L; @@ -34,7 +34,7 @@ public abstract class WritableContainer extends TransactionContainer { final Subject user, final Long timestamp, final String srid, - final HashMap<String, String> flags) { + final Map<String, String> flags) { super(user, timestamp, srid, flags); } diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java index ea4e65f0a60d72cb5da6cb03b2cec44848dbc3c6..95f9b0147f7b0bf4cfcf88029ce52a650af856ca 100644 --- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java +++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java @@ -25,46 +25,16 @@ 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.AuthenticationUtils; -import caosdb.server.accessControl.Principal; -import caosdb.server.accessControl.UserSources; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.entity.Message; -import caosdb.server.utils.ServerMessages; -import caosdb.server.utils.WebinterfaceUtils; -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.Collection; import java.util.HashMap; -import java.util.LinkedList; -import java.util.NoSuchElementException; -import java.util.logging.Level; -import org.apache.commons.fileupload.FileUploadException; +import java.util.Map; 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.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; +import caosdb.server.utils.WebinterfaceUtils; /** * Class is still under construction. @@ -73,11 +43,8 @@ import org.restlet.util.Series; */ public abstract class AbstractCaosDBServerResource extends ServerResource { - private final HashMap<String, String> flags = new HashMap<String, String>(); + private final Map<String, String> flags = new HashMap<String, String>(); private Long timestamp = null; - private static final XMLParser xmlparser = new XMLParser(); - protected String sRID = null; // Server side request ID - private String cRID = null; // Client side request ID private String[] requestedItems = null; private ArrayList<Integer> requestedIDs = new ArrayList<Integer>(); private ArrayList<String> requestedNames = new ArrayList<String>(); @@ -88,19 +55,6 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { return this.utils; } - public static class xmlNotWellFormedException extends Exception { - private static final long serialVersionUID = -6836378704013776849L; - } - - /** - * Returns the (probably unique) server request ID. - * - * @return The server request ID. - */ - public String getSRID() { - return this.sRID; - } - public Long getTimestamp() { return this.timestamp; } @@ -116,6 +70,8 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { */ @Override protected void doInit() { + + // wrap the request entity into an entity with proper logging if (getRequestEntity().isTransient() && !getRequestEntity().isEmpty()) { final ReReadableRepresentation r = new ReReadableRepresentation(getRequestEntity()); getRequest().setEntity(r); @@ -124,12 +80,8 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { this.utils = WebinterfaceUtils.getInstance(getHostRef()); this.timestamp = getRequest().getDate().getTime(); - - this.sRID = getRequest().getAttributes().get("SRID").toString(); - - final Series<Header> headers = getRequest().getHeaders(); - - this.setCRID(headers.getFirstValue("crequestid")); + + this.setAttribute("xsl", getXSLScript()); String specifier = (String) getRequestAttributes().get("specifier"); if (specifier != null && !specifier.equals("")) { @@ -174,260 +126,12 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { @Override protected void doRelease() {} - /** - * Creates the XML root. - * - * <p>The XML root node contains: - * - * <p> - * - * <ul> - * <li>User info as per addUserInfo - * <li>The sRID (server-side request ID) - * <li>A timestamp - * <li>The URI to this resource. - */ - protected Element generateRootElement() { - final Element retRoot = new Element("Response"); - - addUserInfo(retRoot, getUser()); - retRoot.setAttribute("srid", getSRID()); - if (this.getCRID() != null) { - retRoot.setAttribute("crid", this.getCRID()); - } - retRoot.setAttribute("timestamp", getTimestamp().toString()); - retRoot.setAttribute("baseuri", getRootRef().toString()); - return retRoot; - } - - /** - * Add the user info to the Response Element. - * - * @param retRoot - * @param user - */ - private void addUserInfo(Element retRoot, Subject user) { - - if (user != null && user.isAuthenticated()) { - Element userInfo = new Element("UserInfo"); - if (!user.getPrincipal().equals(AuthenticationUtils.ANONYMOUS_USER.getPrincipal())) { - // TODO: deprecated - addNameAndRealm(retRoot, user); - - // this is the new, correct way - addNameAndRealm(userInfo, user); - } - - addRoles(userInfo, user); - retRoot.addContent(userInfo); - } - } - - /** - * Add all roles of the current user to the user info, like this: - * `<UserInfo><Roles><Role>role1</Role><Role>role2</Role>...</Roles></UserInfo>` - * - * @param userInfo - * @param user - */ - private void addRoles(Element userInfo, Subject user) { - Collection<String> roles = UserSources.resolve(user.getPrincipals()); - if (roles == null) return; - Element rs = new Element("Roles"); - for (String role : roles) { - Element r = new Element("Role"); - r.addContent(role); - rs.addContent(r); - } - userInfo.addContent(rs); - } - - /** - * Add the username and the realm of the current user to the user info (as attributes). - * - * @param userInfo - */ - private void addNameAndRealm(Element userInfo, Subject user) { - userInfo.setAttribute("username", ((Principal) user.getPrincipal()).getUsername()); - userInfo.setAttribute("realm", ((Principal) user.getPrincipal()).getRealm()); - } - - @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.utils.getWebinterfaceURI("webcaosdb.xsl"); } - /** Wrap an element for an "OK" response. */ - protected JdomRepresentation ok(Element root) { - return ok(new Document(root)); - } - - /** - * Return a JDomRepresentation of the document and leave the {@link Response}'s {@link Status} - * where it is - which is usually 200 - OK when this method happens to be called. - */ - protected JdomRepresentation ok(final Document doc) { - return new JdomRepresentation(doc, " ", getXSLScript()); - } - - /** - * Return a Representation containing the error message and set the {@link Response}'s {@link - * Status}. - * - * @param m - the error message. - * @param status - the response status - * @return A Representation of the error. - */ - protected Representation 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); - return error(new JdomRepresentation(doc, " ", getXSLScript()), status); - } - - protected Representation error(Representation entity, Status status) { - getResponse().setStatus(status); - return entity; - } - - protected Representation error(final Message m) { - return error(m, Status.SERVER_ERROR_INTERNAL); - } - - protected Representation error(final Status status) { - return error((Representation) null, status); - } - - /** - * Return a Representation containing the warning message but leave the {@link Response}'s {@link - * Status} where it is - which is usually 200 - OK when this method happens to be called. - * - * @param m - the warning message. - * @return A Representation of the warning. - */ - 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, " ", getXSLScript()); - } - - protected Representation noWellFormedNess() { - return error(ServerMessages.REQUEST_BODY_NOT_WELLFORMED); - } - - protected Representation emptyEntity() { - return error(ServerMessages.REQUEST_BODY_EMPTY); - } - - protected Representation connectionFailed() { - return error(ServerMessages.CANNOT_CONNECT_TO_DATABASE); - } - - public Representation 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())); - } + protected String getSRID() { + return getRequest().getAttributes().get("SRID").toString(); } public ArrayList<Integer> getRequestedIDs() { @@ -442,83 +146,8 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { this.requestedNames = requestedNames; } - public HashMap<String, String> getFlags() { + public Map<String, String> getFlags() { return this.flags; } - - protected Element generateRootElement(Element... elements) { - Element root = generateRootElement(); - for (Element e : elements) { - root.addContent(e); - } - return root; - } - - /** - * Returns the client request ID, which can be set by the client. - * - * @return The cRID. - */ - public String getCRID() { - return cRID; - } - - public void setCRID(String cRID) { - this.cRID = cRID; - } - - 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(); - } - } + } diff --git a/src/main/java/caosdb/server/resource/AuthenticationResource.java b/src/main/java/caosdb/server/resource/AuthenticationResource.java index 116439074544f57990ab32ad99d69c20cc5cabe3..4da792192494185f23b60512052cf16edeaff53a 100644 --- a/src/main/java/caosdb/server/resource/AuthenticationResource.java +++ b/src/main/java/caosdb/server/resource/AuthenticationResource.java @@ -22,13 +22,6 @@ */ package caosdb.server.resource; -import caosdb.server.CaosDBException; -import caosdb.server.accessControl.AuthenticationUtils; -import caosdb.server.accessControl.RealmUsernamePasswordToken; -import caosdb.server.accessControl.UserSources; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.entity.xml.ToElementable; -import caosdb.server.utils.ServerMessages; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; @@ -38,48 +31,39 @@ import org.apache.shiro.authc.AccountException; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.CredentialsException; import org.apache.shiro.subject.Subject; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; import org.restlet.data.Form; import org.restlet.data.Reference; import org.restlet.data.Status; import org.restlet.representation.Representation; +import org.restlet.resource.Delete; +import org.restlet.resource.Post; +import caosdb.server.CaosDBException; +import caosdb.server.accessControl.AuthenticationUtils; +import caosdb.server.accessControl.RealmUsernamePasswordToken; +import caosdb.server.accessControl.UserSources; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.entity.Message; +import caosdb.server.utils.ServerMessages; public class AuthenticationResource extends AbstractCaosDBServerResource { - /** Returns single "Login" element. XSLT will create a form. */ - @Override - protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { - final Document doc = new Document(); - final Element rootElem = generateRootElement(); - doc.setRootElement(rootElem); - - final Element loginElem = new Element("Login"); - rootElem.addContent(loginElem); - - return ok(doc); - } - - @Override - protected Representation httpDeleteInChildClass() + @Delete + public Message logout() throws ConnectionException, SQLException, CaosDBException, IOException, NoSuchAlgorithmException, Exception { final Subject user = SecurityUtils.getSubject(); if (user.isAuthenticated()) { user.logout(); getResponse().getCookieSettings().addAll(AuthenticationUtils.getLogoutCookies()); - return success(ServerMessages.LOGOUT_INFO); + return ServerMessages.LOGOUT_INFO; } + getResponse().setStatus(Status.CLIENT_ERROR_FORBIDDEN); return null; } - @Override - protected Representation httpPostInChildClass(final Representation entity) - throws ConnectionException, JDOMException, Exception, xmlNotWellFormedException { + @Post + public Message login(final Representation entity) { getUser().logout(); @@ -106,7 +90,7 @@ public class AuthenticationResource extends AbstractCaosDBServerResource { if (referrerRef != null && !"logout".equals(referrerRef.getLastSegment())) { getResponse().redirectSeeOther(referrerRef); } - return success(null); + return null; } catch (final CredentialsException e) { getLogger().log(Level.INFO, "LOGIN_FAILED", e); @@ -121,16 +105,4 @@ public class AuthenticationResource extends AbstractCaosDBServerResource { return null; } - private JdomRepresentation success(final ToElementable msg) { - - final Document doc = new Document(); - final Element rootElem = generateRootElement(); - doc.setRootElement(rootElem); - - if (msg != null) { - msg.addToElement(rootElem); - } - - return ok(doc); - } } diff --git a/src/main/java/caosdb/server/resource/DefaultResource.java b/src/main/java/caosdb/server/resource/DefaultResource.java index e26171f04fa24cea607288c2ee37ba713d47234f..a199f986a05ca20230fac5ced639236d13933cac 100644 --- a/src/main/java/caosdb/server/resource/DefaultResource.java +++ b/src/main/java/caosdb/server/resource/DefaultResource.java @@ -22,16 +22,16 @@ */ package caosdb.server.resource; -import caosdb.server.CaosDBException; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; import org.jdom2.Document; import org.jdom2.Element; import org.restlet.representation.Representation; +import caosdb.server.CaosDBException; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -public class DefaultResource extends AbstractCaosDBServerResource { +public class DefaultResource extends XMLServerResource { private Element responseBody = null; diff --git a/src/main/java/caosdb/server/resource/EntityOwnerResource.java b/src/main/java/caosdb/server/resource/EntityOwnerResource.java index 5841eeb541be111720379008c5144ef82c6b9a01..d4e58f1ce50e02d93f465e39a2b84e9b61307f73 100644 --- a/src/main/java/caosdb/server/resource/EntityOwnerResource.java +++ b/src/main/java/caosdb/server/resource/EntityOwnerResource.java @@ -22,20 +22,8 @@ */ package caosdb.server.resource; -import caosdb.server.entity.container.RetrieveContainer; import caosdb.server.resource.transaction.EntityResource; -import caosdb.server.resource.transaction.handlers.RequestHandler; -import caosdb.server.resource.transaction.handlers.RetriveOwnerRequestHandler; public class EntityOwnerResource extends EntityResource { - public EntityOwnerResource() { - // only retrieval is allowed - super(true, false, false, false); - } - - @Override - protected RequestHandler<RetrieveContainer> getGetRequestHandler() { - return new RetriveOwnerRequestHandler(); - } } diff --git a/src/main/java/caosdb/server/resource/FileSystemResource.java b/src/main/java/caosdb/server/resource/FileSystemResource.java index 37f0e9ffda3f81b84c6001c5521ba5c8451b473e..8b78d617674605fa88cb192b70c4b487fd97b68b 100644 --- a/src/main/java/caosdb/server/resource/FileSystemResource.java +++ b/src/main/java/caosdb/server/resource/FileSystemResource.java @@ -24,7 +24,17 @@ package caosdb.server.resource; import static caosdb.server.FileSystem.getFromFileSystem; import static java.net.URLDecoder.decode; - +import java.io.File; +import java.io.IOException; +import org.jdom2.Attribute; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.restlet.data.Disposition; +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.representation.FileRepresentation; +import org.restlet.representation.Representation; import caosdb.server.database.backend.implementation.MySQL.ConnectionException; import caosdb.server.database.exceptions.EntityDoesNotExistException; import caosdb.server.database.misc.TransactionBenchmark; @@ -39,17 +49,6 @@ import caosdb.server.transaction.RetrieveSparseEntityByPath; import caosdb.server.transaction.Transaction; import caosdb.server.utils.FileUtils; import caosdb.server.utils.ServerMessages; -import java.io.File; -import java.io.IOException; -import org.jdom2.Attribute; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; -import org.restlet.data.Disposition; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.FileRepresentation; -import org.restlet.representation.Representation; /** * Download files via GET method from the file system directly without making the detour through the @@ -57,7 +56,7 @@ import org.restlet.representation.Representation; * * @author Timm Fitschen */ -public class FileSystemResource extends AbstractCaosDBServerResource { +public class FileSystemResource extends XMLServerResource { public static Message ORPHANED_FILE_WARNING = new Message( diff --git a/src/main/java/caosdb/server/resource/InfoResource.java b/src/main/java/caosdb/server/resource/InfoResource.java index 924d66980e5da27ec998fd361b95a8f0769e4229..d706e9c66593325f0f25bd61aa106b322ff1ae3e 100644 --- a/src/main/java/caosdb/server/resource/InfoResource.java +++ b/src/main/java/caosdb/server/resource/InfoResource.java @@ -22,17 +22,17 @@ */ package caosdb.server.resource; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.database.misc.TransactionBenchmark; -import caosdb.server.utils.FlagInfo; -import caosdb.server.utils.Info; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.restlet.representation.Representation; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.database.misc.TransactionBenchmark; +import caosdb.server.utils.FlagInfo; +import caosdb.server.utils.Info; /** This class represents the information retrieved by /Info requests (only GET) to CaosDB. */ -public class InfoResource extends AbstractCaosDBServerResource { +public class InfoResource extends XMLServerResource { /** * The response to the HTTP GET request is generated here. diff --git a/src/main/java/caosdb/server/resource/JdomRepresentation.java b/src/main/java/caosdb/server/resource/JdomRepresentation.java index 04fbdac3fdd13f9e5c4f025cb2364386deac71f0..984bcc71c9ba4c008956aba816a2b919ca030061 100644 --- a/src/main/java/caosdb/server/resource/JdomRepresentation.java +++ b/src/main/java/caosdb/server/resource/JdomRepresentation.java @@ -22,17 +22,13 @@ */ package caosdb.server.resource; -import caosdb.server.CaosDBServer; -import caosdb.server.ServerProperties; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import org.jdom2.Document; -import org.jdom2.ProcessingInstruction; import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.restlet.data.MediaType; -import org.restlet.data.Reference; import org.restlet.representation.WriterRepresentation; /** @@ -64,27 +60,13 @@ public class JdomRepresentation extends WriterRepresentation { * @param indent Usually a number of whitespaces for better human readbility. * @param xslPath A String containing a meaningful path to a xslt file. */ - public JdomRepresentation(final Document document, final String indent, final String xslPath) { + public JdomRepresentation(final Document document, final String indent) { super(MediaType.TEXT_XML); this.indent = indent; this.document = document; - if (xslPath != null && document != null) { - addStyleSheet(document, xslPath); - } - } - - public static final String getWebUIRef(final Reference reference) { - return reference.getHostIdentifier() - + CaosDBServer.getServerProperty(ServerProperties.KEY_CONTEXT_ROOT) - + "/webinterface/"; } - /** adds the xslt processing instruction to the document. */ - protected final void addStyleSheet(Document document, String xslPath) { - final ProcessingInstruction pi = - new ProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"" + xslPath + "\" "); - document.getContent().add(0, pi); - } + @Override public void write(final Writer writer) throws IOException { diff --git a/src/main/java/caosdb/server/resource/LogoutResource.java b/src/main/java/caosdb/server/resource/LogoutResource.java index 79489413d163d38c669f2cb7f04c5d437b2f11c5..6c734d45a58889e7910d7fa85f5bd434ebaaf848 100644 --- a/src/main/java/caosdb/server/resource/LogoutResource.java +++ b/src/main/java/caosdb/server/resource/LogoutResource.java @@ -22,14 +22,14 @@ */ package caosdb.server.resource; -import caosdb.server.CaosDBException; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; import org.restlet.representation.Representation; +import caosdb.server.CaosDBException; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -public class LogoutResource extends AuthenticationResource { +public class LogoutResource extends XMLServerResource { @Override protected Representation httpGetInChildClass() diff --git a/src/main/java/caosdb/server/resource/PermissionRulesResource.java b/src/main/java/caosdb/server/resource/PermissionRulesResource.java index 1610ba372def0b758b76d15d6d839b825a9d96c6..bcb6539aa0a1fb6c80df012b54474c55d5f6d7b2 100644 --- a/src/main/java/caosdb/server/resource/PermissionRulesResource.java +++ b/src/main/java/caosdb/server/resource/PermissionRulesResource.java @@ -22,14 +22,6 @@ */ package caosdb.server.resource; -import caosdb.server.CaosDBException; -import caosdb.server.accessControl.ACMPermissions; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.entity.Message; -import caosdb.server.permissions.PermissionRule; -import caosdb.server.transaction.RetrievePermissionRulesTransaction; -import caosdb.server.transaction.UpdatePermissionRulesTransaction; -import caosdb.server.utils.ServerMessages; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; @@ -39,8 +31,16 @@ import org.jdom2.Element; import org.restlet.data.Status; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; +import caosdb.server.CaosDBException; +import caosdb.server.accessControl.ACMPermissions; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.entity.Message; +import caosdb.server.permissions.PermissionRule; +import caosdb.server.transaction.RetrievePermissionRulesTransaction; +import caosdb.server.transaction.UpdatePermissionRulesTransaction; +import caosdb.server.utils.ServerMessages; -public class PermissionRulesResource extends AbstractCaosDBServerResource { +public class PermissionRulesResource extends XMLServerResource { @Override protected Representation httpGetInChildClass() diff --git a/src/main/java/caosdb/server/resource/RolesResource.java b/src/main/java/caosdb/server/resource/RolesResource.java index ef9b30f5b831454fac392317e2c57833657c62d6..fc7d092e9310f9860f6b99f5a256f2187030f225 100644 --- a/src/main/java/caosdb/server/resource/RolesResource.java +++ b/src/main/java/caosdb/server/resource/RolesResource.java @@ -22,16 +22,6 @@ */ package caosdb.server.resource; -import caosdb.server.CaosDBException; -import caosdb.server.accessControl.ACMPermissions; -import caosdb.server.accessControl.Role; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.entity.Message; -import caosdb.server.transaction.DeleteRoleTransaction; -import caosdb.server.transaction.InsertRoleTransaction; -import caosdb.server.transaction.RetrieveRoleTransaction; -import caosdb.server.transaction.UpdateRoleTransaction; -import caosdb.server.utils.ServerMessages; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; @@ -42,8 +32,18 @@ import org.restlet.data.Form; import org.restlet.data.Status; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; +import caosdb.server.CaosDBException; +import caosdb.server.accessControl.ACMPermissions; +import caosdb.server.accessControl.Role; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.entity.Message; +import caosdb.server.transaction.DeleteRoleTransaction; +import caosdb.server.transaction.InsertRoleTransaction; +import caosdb.server.transaction.RetrieveRoleTransaction; +import caosdb.server.transaction.UpdateRoleTransaction; +import caosdb.server.utils.ServerMessages; -public class RolesResource extends AbstractCaosDBServerResource { +public class RolesResource extends XMLServerResource { @Override protected Representation httpGetInChildClass() diff --git a/src/main/java/caosdb/server/resource/ScriptingResource.java b/src/main/java/caosdb/server/resource/ScriptingResource.java index b23a9593918ead75a6b38216838995e09ac67bc7..45e13f2bf2223569c9e4efa182c29152d691d479 100644 --- a/src/main/java/caosdb/server/resource/ScriptingResource.java +++ b/src/main/java/caosdb/server/resource/ScriptingResource.java @@ -24,18 +24,6 @@ package caosdb.server.resource; -import caosdb.server.FileSystem; -import caosdb.server.accessControl.Principal; -import caosdb.server.accessControl.SessionToken; -import caosdb.server.accessControl.UserSources; -import caosdb.server.entity.FileProperties; -import caosdb.server.entity.Message; -import caosdb.server.scripting.CallerSerializer; -import caosdb.server.scripting.ServerSideScriptingCaller; -import caosdb.server.utils.Serializer; -import caosdb.server.utils.ServerMessages; -import caosdb.server.utils.Utils; -import com.ibm.icu.text.Collator; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -57,8 +45,20 @@ import org.restlet.data.Status; import org.restlet.engine.header.ContentType; import org.restlet.ext.fileupload.RestletFileUpload; import org.restlet.representation.Representation; +import com.ibm.icu.text.Collator; +import caosdb.server.FileSystem; +import caosdb.server.accessControl.Principal; +import caosdb.server.accessControl.SessionToken; +import caosdb.server.accessControl.UserSources; +import caosdb.server.entity.FileProperties; +import caosdb.server.entity.Message; +import caosdb.server.scripting.CallerSerializer; +import caosdb.server.scripting.ServerSideScriptingCaller; +import caosdb.server.utils.Serializer; +import caosdb.server.utils.ServerMessages; +import caosdb.server.utils.Utils; -public class ScriptingResource extends AbstractCaosDBServerResource { +public class ScriptingResource extends XMLServerResource { private ServerSideScriptingCaller caller; private Collection<FileProperties> deleteFiles = new LinkedList<>(); diff --git a/src/main/java/caosdb/server/resource/ServerLogsResource.java b/src/main/java/caosdb/server/resource/ServerLogsResource.java index b63de6e0f7fd0d95dd315db47fe6a6c88833f648..b9e72b6314c1e2d2c7f2f3be577fd46ad5614e40 100644 --- a/src/main/java/caosdb/server/resource/ServerLogsResource.java +++ b/src/main/java/caosdb/server/resource/ServerLogsResource.java @@ -22,9 +22,6 @@ */ package caosdb.server.resource; -import caosdb.server.CaosDBException; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.transaction.RetrieveLogRecordTransaction; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; @@ -36,8 +33,11 @@ import org.restlet.data.Form; import org.restlet.data.MediaType; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; +import caosdb.server.CaosDBException; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.transaction.RetrieveLogRecordTransaction; -public class ServerLogsResource extends AbstractCaosDBServerResource { +public class ServerLogsResource extends XMLServerResource { @Override protected Representation httpGetInChildClass() diff --git a/src/main/java/caosdb/server/resource/ServerPropertiesResource.java b/src/main/java/caosdb/server/resource/ServerPropertiesResource.java index 347919d9c7d0f604734790f2ea09b5e2bac54042..d69a066838d0bf73339c5c3f389d3e35388dbb10 100644 --- a/src/main/java/caosdb/server/resource/ServerPropertiesResource.java +++ b/src/main/java/caosdb/server/resource/ServerPropertiesResource.java @@ -1,14 +1,14 @@ package caosdb.server.resource; -import caosdb.server.CaosDBServer; -import caosdb.server.ServerPropertiesSerializer; -import caosdb.server.accessControl.ACMPermissions; import org.restlet.data.Form; import org.restlet.data.Parameter; import org.restlet.data.Status; import org.restlet.representation.Representation; +import caosdb.server.CaosDBServer; +import caosdb.server.ServerPropertiesSerializer; +import caosdb.server.accessControl.ACMPermissions; -public class ServerPropertiesResource extends AbstractCaosDBServerResource { +public class ServerPropertiesResource extends XMLServerResource { @Override protected void doInit() { diff --git a/src/main/java/caosdb/server/resource/SharedFileResource.java b/src/main/java/caosdb/server/resource/SharedFileResource.java index ffdce05baab5098d03080567ee815d286476f281..bded689b60ac34372a69d1a0414f75a9718650b6 100644 --- a/src/main/java/caosdb/server/resource/SharedFileResource.java +++ b/src/main/java/caosdb/server/resource/SharedFileResource.java @@ -25,11 +25,6 @@ package caosdb.server.resource; import static java.net.URLDecoder.decode; - -import caosdb.server.FileSystem; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.utils.FileUtils; -import caosdb.server.utils.ServerMessages; import java.io.File; import java.io.IOException; import org.jdom2.JDOMException; @@ -38,13 +33,17 @@ import org.restlet.data.MediaType; import org.restlet.data.Status; import org.restlet.representation.FileRepresentation; import org.restlet.representation.Representation; +import caosdb.server.FileSystem; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.utils.FileUtils; +import caosdb.server.utils.ServerMessages; /** * Download temporary files via GET method only. * * @author Daniel Hornung */ -public class SharedFileResource extends AbstractCaosDBServerResource { +public class SharedFileResource extends XMLServerResource { /** * Download a File from the tempfiles folder. Only one File per Request. diff --git a/src/main/java/caosdb/server/resource/ThumbnailsResource.java b/src/main/java/caosdb/server/resource/ThumbnailsResource.java index e08378c5a4e766f8f84d9d383207e461868bc422..cde3b9f00fee6f0706cba7a7720868fb6e0f89f6 100644 --- a/src/main/java/caosdb/server/resource/ThumbnailsResource.java +++ b/src/main/java/caosdb/server/resource/ThumbnailsResource.java @@ -23,9 +23,6 @@ package caosdb.server.resource; import static caosdb.server.FileSystem.getFromFileSystem; - -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.utils.ServerMessages; import java.io.File; import java.io.IOException; import org.jdom2.JDOMException; @@ -34,8 +31,10 @@ import org.restlet.data.MediaType; import org.restlet.data.Status; import org.restlet.representation.FileRepresentation; import org.restlet.representation.Representation; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.utils.ServerMessages; -public class ThumbnailsResource extends AbstractCaosDBServerResource { +public class ThumbnailsResource extends XMLServerResource { /** * Download a File from the CaosDBFileSystem. Only one File per Request. diff --git a/src/main/java/caosdb/server/resource/UserResource.java b/src/main/java/caosdb/server/resource/UserResource.java index b77ff8a49035d2be930a3f957e743ed2f0dae067..f84ff0c330f28a6c152b14906cd07e2d7f5fd6ae 100644 --- a/src/main/java/caosdb/server/resource/UserResource.java +++ b/src/main/java/caosdb/server/resource/UserResource.java @@ -22,6 +22,15 @@ */ package caosdb.server.resource; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.sql.SQLException; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.restlet.data.Form; +import org.restlet.data.Status; +import org.restlet.representation.Representation; import caosdb.server.CaosDBException; import caosdb.server.CaosDBServer; import caosdb.server.ServerProperties; @@ -35,22 +44,13 @@ import caosdb.server.transaction.InsertUserTransaction; import caosdb.server.transaction.RetrieveUserTransaction; import caosdb.server.transaction.UpdateUserTransaction; import caosdb.server.utils.ServerMessages; -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.sql.SQLException; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; -import org.restlet.data.Form; -import org.restlet.data.Status; -import org.restlet.representation.Representation; /** * This class handles requests for Users. * * @author Timm Fitschen */ -public class UserResource extends AbstractCaosDBServerResource { +public class UserResource extends XMLServerResource { @Override protected Representation httpGetInChildClass() diff --git a/src/main/java/caosdb/server/resource/UserRolesResource.java b/src/main/java/caosdb/server/resource/UserRolesResource.java index 27e147157675982f036b88981b1dce221abae754..0b129062ff509c0cf91a628902dc059bff494a5c 100644 --- a/src/main/java/caosdb/server/resource/UserRolesResource.java +++ b/src/main/java/caosdb/server/resource/UserRolesResource.java @@ -22,14 +22,6 @@ */ package caosdb.server.resource; -import caosdb.server.CaosDBException; -import caosdb.server.accessControl.ACMPermissions; -import caosdb.server.accessControl.UserSources; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import caosdb.server.entity.Message; -import caosdb.server.transaction.RetrieveUserRolesTransaction; -import caosdb.server.transaction.UpdateUserRolesTransaction; -import caosdb.server.utils.ServerMessages; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; @@ -40,8 +32,16 @@ import org.jdom2.Element; import org.jdom2.JDOMException; import org.restlet.data.Status; import org.restlet.representation.Representation; +import caosdb.server.CaosDBException; +import caosdb.server.accessControl.ACMPermissions; +import caosdb.server.accessControl.UserSources; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.entity.Message; +import caosdb.server.transaction.RetrieveUserRolesTransaction; +import caosdb.server.transaction.UpdateUserRolesTransaction; +import caosdb.server.utils.ServerMessages; -public class UserRolesResource extends AbstractCaosDBServerResource { +public class UserRolesResource extends XMLServerResource { @Override protected Representation httpGetInChildClass() diff --git a/src/main/java/caosdb/server/resource/XMLServerResource.java b/src/main/java/caosdb/server/resource/XMLServerResource.java new file mode 100644 index 0000000000000000000000000000000000000000..b0668268fffa4bc80d400fed318309a3bf50aa93 --- /dev/null +++ b/src/main/java/caosdb/server/resource/XMLServerResource.java @@ -0,0 +1,475 @@ +/* + * ** 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 + * Copyright (C) 2019 IndiScale GmbH + * + * 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 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.Collection; +import java.util.HashMap; +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.restlet.data.Form; +import org.restlet.data.Header; +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.util.Series; +import caosdb.server.CaosDBException; +import caosdb.server.accessControl.AuthenticationUtils; +import caosdb.server.accessControl.Principal; +import caosdb.server.accessControl.UserSources; +import caosdb.server.converter.xml.XMLParser; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import caosdb.server.entity.Message; +import caosdb.server.utils.ServerMessages; +import caosdb.server.utils.WebinterfaceUtils; + +/** + * Class is still under construction. + * + * @author Timm Fitschen + */ +public abstract class XMLServerResource extends AbstractCaosDBServerResource { + + 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; // Server side request ID + private String cRID = null; // Client side request ID + private String[] requestedItems = null; + private ArrayList<Integer> requestedIDs = new ArrayList<Integer>(); + private ArrayList<String> requestedNames = new ArrayList<String>(); + private WebinterfaceUtils utils; + + /** Return the {@link WebinterfaceUtils} instance for this resource. */ + @Override + public WebinterfaceUtils getUtils() { + return this.utils; + } + + public static class xmlNotWellFormedException extends Exception { + private static final long serialVersionUID = -6836378704013776849L; + } + + /** + * Returns the (probably unique) server request ID. + * + * @return The server request ID. + */ + @Override + public String getSRID() { + return this.sRID; + } + + @Override + public Long getTimestamp() { + return this.timestamp; + } + + @Override + public Subject getUser() { + Subject ret = SecurityUtils.getSubject(); + return ret; + } + + /** + * Setup method, called by {@link org.restlet.resource.Resource#init(org.restlet.Context, + * org.restlet.Request, org.restlet.Response)}. + */ + @Override + protected void doInit() { + if (getRequestEntity().isTransient() && !getRequestEntity().isEmpty()) { + final ReReadableRepresentation r = new ReReadableRepresentation(getRequestEntity()); + getRequest().setEntity(r); + } + + this.utils = WebinterfaceUtils.getInstance(getHostRef()); + + this.timestamp = getRequest().getDate().getTime(); + + this.sRID = getRequest().getAttributes().get("SRID").toString(); + + final Series<Header> headers = getRequest().getHeaders(); + + this.setCRID(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() {} + + /** + * Creates the XML root. + * + * <p>The XML root node contains: + * + * <p> + * + * <ul> + * <li>User info as per addUserInfo + * <li>The sRID (server-side request ID) + * <li>A timestamp + * <li>The URI to this resource. + */ + protected Element generateRootElement() { + final Element retRoot = new Element("Response"); + + addUserInfo(retRoot, getUser()); + retRoot.setAttribute("srid", getSRID()); + if (this.getCRID() != null) { + retRoot.setAttribute("crid", this.getCRID()); + } + retRoot.setAttribute("timestamp", getTimestamp().toString()); + retRoot.setAttribute("baseuri", getRootRef().toString()); + return retRoot; + } + + /** + * Add the user info to the Response Element. + * + * @param retRoot + * @param user + */ + private void addUserInfo(Element retRoot, Subject user) { + + if (user != null && user.isAuthenticated()) { + Element userInfo = new Element("UserInfo"); + if (!user.getPrincipal().equals(AuthenticationUtils.ANONYMOUS_USER.getPrincipal())) { + // TODO: deprecated + addNameAndRealm(retRoot, user); + + // this is the new, correct way + addNameAndRealm(userInfo, user); + } + + addRoles(userInfo, user); + retRoot.addContent(userInfo); + } + } + + /** + * Add all roles of the current user to the user info, like this: + * `<UserInfo><Roles><Role>role1</Role><Role>role2</Role>...</Roles></UserInfo>` + * + * @param userInfo + * @param user + */ + private void addRoles(Element userInfo, Subject user) { + Collection<String> roles = UserSources.resolve(user.getPrincipals()); + if (roles == null) return; + Element rs = new Element("Roles"); + for (String role : roles) { + Element r = new Element("Role"); + r.addContent(role); + rs.addContent(r); + } + userInfo.addContent(rs); + } + + /** + * Add the username and the realm of the current user to the user info (as attributes). + * + * @param userInfo + */ + private void addNameAndRealm(Element userInfo, Subject user) { + userInfo.setAttribute("username", ((Principal) user.getPrincipal()).getUsername()); + userInfo.setAttribute("realm", ((Principal) user.getPrincipal()).getRealm()); + } + + @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(); + } + } + + @Override + protected String getXSLScript() { + return this.utils.getWebinterfaceURI("webcaosdb.xsl"); + } + + /** Wrap an element for an "OK" response. */ + protected JdomRepresentation ok(Element root) { + return ok(new Document(root)); + } + + /** + * Return a JDomRepresentation of the document and leave the {@link Response}'s {@link Status} + * where it is - which is usually 200 - OK when this method happens to be called. + */ + protected JdomRepresentation ok(final Document doc) { + return new JdomRepresentation(doc, " "); + } + + /** + * Return a Representation containing the error message and set the {@link Response}'s {@link + * Status}. + * + * @param m - the error message. + * @param status - the response status + * @return A Representation of the error. + */ + protected Representation 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); + return error(new JdomRepresentation(doc, " "), status); + } + + protected Representation error(Representation entity, Status status) { + getResponse().setStatus(status); + return entity; + } + + protected Representation error(final Message m) { + return error(m, Status.SERVER_ERROR_INTERNAL); + } + + protected Representation error(final Status status) { + return error((Representation) null, status); + } + + /** + * Return a Representation containing the warning message but leave the {@link Response}'s {@link + * Status} where it is - which is usually 200 - OK when this method happens to be called. + * + * @param m - the warning message. + * @return A Representation of the warning. + */ + 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, " "); + } + + protected Representation noWellFormedNess() { + return error(ServerMessages.REQUEST_BODY_NOT_WELLFORMED); + } + + protected Representation emptyEntity() { + return error(ServerMessages.REQUEST_BODY_EMPTY); + } + + protected Representation connectionFailed() { + return error(ServerMessages.CANNOT_CONNECT_TO_DATABASE); + } + + public Representation 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())); + } + } + + @Override + public ArrayList<Integer> getRequestedIDs() { + return this.requestedIDs; + } + + @Override + public ArrayList<String> getRequestedNames() { + return this.requestedNames; + } + + @Override + public void setRequestedNames(final ArrayList<String> requestedNames) { + this.requestedNames = requestedNames; + } + + @Override + 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; + } + + /** + * Returns the client request ID, which can be set by the client. + * + * @return The cRID. + */ + public String getCRID() { + return cRID; + } + + public void setCRID(String cRID) { + this.cRID = cRID; + } + +} diff --git a/src/main/java/caosdb/server/resource/transaction/EntityResource.java b/src/main/java/caosdb/server/resource/transaction/EntityResource.java index 0c853096c1af71dcacf2ef775c46f4dceecb1673..367450fa053583df2a19b18b59d36bffbe465bbc 100644 --- a/src/main/java/caosdb/server/resource/transaction/EntityResource.java +++ b/src/main/java/caosdb/server/resource/transaction/EntityResource.java @@ -22,11 +22,21 @@ */ package caosdb.server.resource.transaction; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.sql.SQLException; +import org.jdom2.JDOMException; +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.resource.Get; +import org.restlet.resource.Post; +import org.restlet.resource.Put; import caosdb.server.CaosDBException; import caosdb.server.database.backend.implementation.MySQL.ConnectionException; import caosdb.server.entity.container.DeleteContainer; import caosdb.server.entity.container.InsertContainer; import caosdb.server.entity.container.RetrieveContainer; +import caosdb.server.entity.container.TransactionContainer; import caosdb.server.entity.container.UpdateContainer; import caosdb.server.resource.AbstractCaosDBServerResource; import caosdb.server.resource.transaction.handlers.FileUploadHandler; @@ -38,15 +48,6 @@ import caosdb.server.transaction.Delete; import caosdb.server.transaction.Insert; import caosdb.server.transaction.Retrieve; import caosdb.server.transaction.Update; -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.sql.SQLException; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; public class EntityResource extends AbstractCaosDBServerResource { @@ -93,8 +94,8 @@ public class EntityResource extends AbstractCaosDBServerResource { } } - @Override - protected final Representation httpGetInChildClass() + @Get + protected final TransactionContainer httpGetInChildClass() throws ConnectionException, IOException, SQLException, CaosDBException, NoSuchAlgorithmException, Exception { @@ -105,7 +106,6 @@ public class EntityResource extends AbstractCaosDBServerResource { } final RetrieveContainer entityContainer = new RetrieveContainer(getUser(), getTimestamp(), getSRID(), getFlags()); - final Document doc = new Document(); getGetRequestHandler().handle(this, entityContainer); @@ -116,14 +116,11 @@ public class EntityResource extends AbstractCaosDBServerResource { entityContainer .getTransactionBenchmark() .addMeasurement(getClass().getSimpleName() + ".httpGetInChildClass", t2 - t1); - final Element rootElem = generateRootElement(); - entityContainer.addToElement(rootElem); - doc.setRootElement(rootElem); - return ok(doc); + return entityContainer; } - @Override - protected final Representation httpDeleteInChildClass() throws Exception { + @org.restlet.resource.Delete + protected final TransactionContainer httpDeleteInChildClass() throws Exception { if (!this.delete) { getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); @@ -131,23 +128,18 @@ public class EntityResource extends AbstractCaosDBServerResource { } final DeleteContainer entityContainer = new DeleteContainer(getUser(), getTimestamp(), getSRID(), getFlags()); - final Document doc = new Document(); getDeleteRequestHandler().handle(this, entityContainer); final Delete delete = new Delete(entityContainer); delete.execute(); - final Element rootElem = generateRootElement(); - entityContainer.addToElement(rootElem); - doc.setRootElement(rootElem); - - return ok(doc); + return entityContainer; } - @Override - protected final Representation httpPostInChildClass(final Representation entity) - throws xmlNotWellFormedException, Exception { + @Post + protected final TransactionContainer httpPostInChildClass(final TransactionContainer entities) + throws Exception { if (!this.post) { getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); @@ -156,23 +148,18 @@ public class EntityResource extends AbstractCaosDBServerResource { final InsertContainer entityContainer = new InsertContainer(getUser(), getTimestamp(), getSRID(), getFlags()); - final Document doc = new Document(); getPostRequestHandler().handle(this, entityContainer); final Insert insert = new Insert(entityContainer); insert.execute(); - final Element rootElem = generateRootElement(); - entityContainer.addToElement(rootElem); - doc.setRootElement(rootElem); - - return ok(doc); + return entityContainer; } - @Override - protected final Representation httpPutInChildClass(final Representation entity) - throws ConnectionException, JDOMException, Exception, xmlNotWellFormedException { + @Put + protected final TransactionContainer httpPutInChildClass(final TransactionContainer entities) + throws ConnectionException, JDOMException, Exception { if (!this.put) { getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); @@ -181,17 +168,12 @@ public class EntityResource extends AbstractCaosDBServerResource { final UpdateContainer entityContainer = new UpdateContainer(getUser(), getTimestamp(), getSRID(), getFlags()); - final Document doc = new Document(); getPutRequestHandler().handle(this, entityContainer); final Update update = new Update(entityContainer); update.execute(); - final Element rootElem = generateRootElement(); - entityContainer.addToElement(rootElem); - doc.setRootElement(rootElem); - - return ok(doc); + return entityContainer; } } diff --git a/src/main/java/caosdb/server/resource/transaction/handlers/SimpleWriteHandler.java b/src/main/java/caosdb/server/resource/transaction/handlers/SimpleWriteHandler.java index 71afcc91321f8d9853cd18ae3ca12022501416ff..66afef752797d841bf498d088094703f26f82273 100644 --- a/src/main/java/caosdb/server/resource/transaction/handlers/SimpleWriteHandler.java +++ b/src/main/java/caosdb/server/resource/transaction/handlers/SimpleWriteHandler.java @@ -22,10 +22,10 @@ */ package caosdb.server.resource.transaction.handlers; -import caosdb.server.entity.container.WritableContainer; -import caosdb.server.resource.transaction.EntityResource; import java.util.List; import org.jdom2.Element; +import caosdb.server.entity.container.WritableContainer; +import caosdb.server.resource.transaction.EntityResource; public class SimpleWriteHandler<T extends WritableContainer> extends RequestHandler<T> { diff --git a/src/test/java/caosdb/server/permissions/EntityACLTest.java b/src/test/java/caosdb/server/permissions/EntityACLTest.java index 13ebb6279546606c8e796672db09a7e99ffea588..53b15fe8d6e2754f8469843bccf30de49fdbfb2f 100644 --- a/src/test/java/caosdb/server/permissions/EntityACLTest.java +++ b/src/test/java/caosdb/server/permissions/EntityACLTest.java @@ -23,11 +23,6 @@ package caosdb.server.permissions; import static org.junit.Assert.assertNotNull; - -import caosdb.server.CaosDBServer; -import caosdb.server.resource.AbstractCaosDBServerResource; -import caosdb.server.resource.AbstractCaosDBServerResource.XMLParser; -import caosdb.server.utils.Utils; import java.io.IOException; import java.util.BitSet; import java.util.LinkedList; @@ -36,6 +31,9 @@ import org.jdom2.JDOMException; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import caosdb.server.CaosDBServer; +import caosdb.server.converter.xml.XMLParser; +import caosdb.server.utils.Utils; public class EntityACLTest { @@ -206,7 +204,7 @@ public class EntityACLTest { return false; } - public static AbstractCaosDBServerResource.XMLParser parser = new XMLParser(); + public static XMLParser parser = new XMLParser(); public Element stringToJdom(final String s) throws JDOMException, IOException { return parser.parse(Utils.String2InputStream(s)).getRootElement(); diff --git a/src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java b/src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java index 81baabd4797842f1cbbc61f990d6ea580553de85..1ae724bbbd4df42360c7bf2f0d3df83b87f15aa4 100644 --- a/src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java +++ b/src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java @@ -2,13 +2,6 @@ package caosdb.server.resource; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; - -import caosdb.server.CaosDBException; -import caosdb.server.CaosDBServer; -import caosdb.server.ServerProperties; -import caosdb.server.accessControl.AnonymousAuthenticationToken; -import caosdb.server.accessControl.AnonymousRealm; -import caosdb.server.database.backend.implementation.MySQL.ConnectionException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -24,6 +17,12 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.restlet.data.Reference; import org.restlet.representation.Representation; +import caosdb.server.CaosDBException; +import caosdb.server.CaosDBServer; +import caosdb.server.ServerProperties; +import caosdb.server.accessControl.AnonymousAuthenticationToken; +import caosdb.server.accessControl.AnonymousRealm; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; public class TestAbstractCaosDBServerResource { @@ -38,8 +37,8 @@ public class TestAbstractCaosDBServerResource { public void testReponseRootElement() throws IOException { final Subject user = new DelegatingSubject(new DefaultSecurityManager(new AnonymousRealm())); user.login(AnonymousAuthenticationToken.getInstance()); - AbstractCaosDBServerResource s = - new AbstractCaosDBServerResource() { + XMLServerResource s = + new XMLServerResource() { @Override protected Representation httpGetInChildClass()