From 702f22eb8cbcb56c927dc21226ee22696426647b Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Tue, 3 Nov 2020 08:45:48 +0100 Subject: [PATCH] Work on ODK servlets based on JCR. --- .../OSGI-INF/odkFormListServlet.xml | 1 + .../OSGI-INF/odkFormServlet.xml | 3 +- .../OSGI-INF/odkSubmissionServlet.xml | 1 + knowledge/org.argeo.support.odk/bnd.bnd | 2 +- .../org/argeo/support/odk/OrxListType.java | 4 +- .../src/org/argeo/support/odk/odk.cnd | 2 +- .../odk/servlet/OdkFormListServlet.java | 139 +++++++++++++++--- .../support/odk/servlet/OdkFormServlet.java | 48 ++++-- .../odk/servlet/OdkServletContext.java | 1 + .../odk/servlet/OdkSubmissionServlet.java | 8 + .../src/org/argeo/entity/EntityType.java | 4 +- .../src/org/argeo/entity/JcrName.java | 26 ++++ .../src/org/argeo/entity/entity.cnd | 6 +- .../src/org/argeo/suite/RankedObject.java | 8 +- .../suite/core/SuiteMaintenanceService.java | 2 +- 15 files changed, 210 insertions(+), 45 deletions(-) create mode 100644 org.argeo.entity.api/src/org/argeo/entity/JcrName.java diff --git a/knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml b/knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml index 852423b..e290933 100644 --- a/knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml +++ b/knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml @@ -7,4 +7,5 @@ + diff --git a/knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml b/knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml index 59d2de2..7f7e4db 100644 --- a/knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml +++ b/knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml @@ -4,7 +4,8 @@ - + + diff --git a/knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml b/knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml index e9153b4..1e1bcb1 100644 --- a/knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml +++ b/knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml @@ -7,4 +7,5 @@ + diff --git a/knowledge/org.argeo.support.odk/bnd.bnd b/knowledge/org.argeo.support.odk/bnd.bnd index 619b258..0154985 100644 --- a/knowledge/org.argeo.support.odk/bnd.bnd +++ b/knowledge/org.argeo.support.odk/bnd.bnd @@ -13,5 +13,5 @@ OSGI-INF/odkSubmissionServlet.xml Import-Package:\ org.osgi.service.http.context,\ -org.argeo.naming,\ +org.argeo.api,\ * diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OrxListType.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OrxListType.java index 0b7a963..c51484c 100644 --- a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OrxListType.java +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OrxListType.java @@ -1,9 +1,9 @@ package org.argeo.support.odk; -import org.argeo.jcr.QualifiedName; +import org.argeo.entity.JcrName; /** Types related to the http://openrosa.org/xforms/xformsList namespace. */ -public enum OrxListType implements QualifiedName { +public enum OrxListType implements JcrName { xform; @Override diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/odk.cnd b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/odk.cnd index c3f43c8..0f6fcc8 100644 --- a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/odk.cnd +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/odk.cnd @@ -4,7 +4,7 @@ -[orxList:xform] +[orxList:xform] > mix:created, mix:lastModified, jcrx:csum, entity:form + h:html (odk:html) = odk:html [odk:head] diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java index 8398c7f..cc892a8 100644 --- a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java @@ -2,10 +2,21 @@ package org.argeo.support.odk.servlet; import java.io.IOException; import java.io.Writer; +import java.security.AccessControlContext; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.security.auth.Subject; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -13,7 +24,15 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; +import org.argeo.cms.auth.ServletAuthUtils; +import org.argeo.entity.EntityType; +import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.JcrxApi; import org.argeo.support.odk.OdkForm; +import org.argeo.support.odk.OrxListType; /** Lists available forms. */ public class OdkFormListServlet extends HttpServlet { @@ -22,6 +41,11 @@ public class OdkFormListServlet extends HttpServlet { private Set odkForms = Collections.synchronizedSet(new HashSet<>()); + private DateTimeFormatter versionFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd-HHmm") + .withZone(ZoneId.from(ZoneOffset.UTC)); + + private Repository repository; + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/xml; charset=utf-8"); @@ -32,27 +56,101 @@ public class OdkFormListServlet extends HttpServlet { int serverPort = req.getServerPort(); String protocol = serverPort == 443 || req.isSecure() ? "https" : "http"; + String pathInfo = req.getPathInfo(); + + Subject subject = Subject + .getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName())); + log.debug("SERVLET CONTEXT: " + subject); + + Session session = ServletAuthUtils.doAs(() -> Jcr.login(repository, NodeConstants.SYS_WORKSPACE), req); +// session = NodeUtils.openDataAdminSession(repository, NodeConstants.SYS_WORKSPACE); Writer writer = resp.getWriter(); writer.append(""); writer.append(""); - for (OdkForm form : odkForms) { - StringBuilder sb = new StringBuilder(); - sb.append(""); - sb.append("" + form.getFormId() + ""); - sb.append("" + form.getName() + ""); - sb.append("" + form.getVersion() + ""); - sb.append("" + form.getHash(null) + ""); - sb.append("" + form.getDescription() + ""); - sb.append("" + protocol + "://" + serverName - + (serverPort == 80 - || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/" + form.getFileName() - + ""); - sb.append(""); - String str = sb.toString(); - if (log.isDebugEnabled()) - log.debug(str); - writer.append(str); - } + boolean newApproach = true; + if (newApproach) + try { + +// Query query; +// if (pathInfo == null) { +//// query = session.getWorkspace().getQueryManager() +//// .createQuery("SELECT * FROM [nt:unstructured]", Query.JCR_SQL2); +// query = session.getWorkspace().getQueryManager() +// .createQuery("SELECT * FROM [" + OrxListType.xform.get() + "]", Query.JCR_SQL2); +// } else { +// query = session.getWorkspace().getQueryManager() +// .createQuery( +// "SELECT node FROM [" + OrxListType.xform.get() +// + "] AS node WHERE ISDESCENDANTNODE (node, '" + pathInfo + "')", +// Query.JCR_SQL2); +// } +// QueryResult queryResult = query.execute(); +// +// NodeIterator nit = queryResult.getNodes(); + log.debug(session.getUserID()); + log.debug(session.getWorkspace().getName()); + NodeIterator nit = session.getRootNode().getNodes(); + while (nit.hasNext()) { + log.debug(nit.nextNode()); + } + while (nit.hasNext()) { + StringBuilder sb = new StringBuilder(); + Node node = nit.nextNode(); + if (node.isNodeType(OrxListType.xform.get())) { + sb.append(""); + sb.append("" + node.getNode("h:html").getIdentifier() + ""); + sb.append("" + node.getProperty(Property.JCR_TITLE).getString() + ""); + sb.append("" + versionFormatter.format(JcrUtils.getModified(node)) + ""); + sb.append("md5:" + JcrxApi.getChecksum(node, JcrxApi.MD5) + ""); + if (node.hasProperty(Property.JCR_DESCRIPTION)) + sb.append("" + node.getProperty(Property.JCR_DESCRIPTION).getString() + ""); + sb.append("" + protocol + "://" + serverName + + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/form/" + + node.getPath() + ""); + sb.append(""); + } else if (node.isNodeType(EntityType.formSet.get())) { + sb.append(""); + sb.append("" + node.getPath() + ""); + sb.append("" + node.getProperty(Property.JCR_TITLE).getString() + ""); + sb.append("" + protocol + "://" + serverName + + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/formList" + + node.getPath() + ""); + sb.append(""); + } + String str = sb.toString(); + if (!str.equals("")) { + if (log.isDebugEnabled()) + log.debug(str); + writer.append(str); + } + } + } catch (RepositoryException e) { + e.printStackTrace(); + // TODO error message + // resp.sendError(500); + } finally { + Jcr.logout(session); + } + + boolean oldApproach = true; + if (oldApproach) + for (OdkForm form : odkForms) { + StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append("" + form.getFormId() + ""); + sb.append("" + form.getName() + ""); + sb.append("" + form.getVersion() + ""); + sb.append("" + form.getHash(null) + ""); + sb.append("" + form.getDescription() + ""); + sb.append("" + protocol + "://" + serverName + + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/form/" + + form.getFileName() + ""); + sb.append(""); + String str = sb.toString(); + if (log.isDebugEnabled()) + log.debug(str); + writer.append(str); + } writer.append(""); } @@ -63,4 +161,9 @@ public class OdkFormListServlet extends HttpServlet { public void removeForm(OdkForm odkForm) { odkForms.remove(odkForm); } + + public void setRepository(Repository repository) { + this.repository = repository; + } + } diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java index 3746da8..93f4147 100644 --- a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java @@ -7,35 +7,57 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FilenameUtils; +import org.argeo.cms.auth.ServletAuthUtils; +import org.argeo.jcr.Jcr; import org.argeo.support.odk.OdkForm; /** Retrieves a single form. */ public class OdkFormServlet extends HttpServlet { private static final long serialVersionUID = 7838305967987687370L; + private Repository repository; private Map odkForms = Collections.synchronizedMap(new HashMap<>()); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/xml; charset=utf-8"); - String path = req.getServletPath(); - String fileName = FilenameUtils.getName(path); - OdkForm form = odkForms.get(fileName); - if (form == null) - throw new IllegalArgumentException("No form named " + fileName + " was found"); - - byte[] buffer = new byte[1024]; - try (InputStream in = form.openStream(); OutputStream out = resp.getOutputStream();) { - int bytesRead; - while ((bytesRead = in.read(buffer)) != -1) - out.write(buffer, 0, bytesRead); + Session session = ServletAuthUtils.doAs(() -> Jcr.login(repository, null), req); + + String pathInfo = req.getPathInfo(); + + try { + if (session.nodeExists(pathInfo)) { + session.exportDocumentView(pathInfo + "/h:html", resp.getOutputStream(), true, false); + } else { + + String fileName = FilenameUtils.getName(pathInfo); + OdkForm form = odkForms.get(fileName); + if (form == null) + throw new IllegalArgumentException("No form named " + fileName + " was found"); + + byte[] buffer = new byte[1024]; + try (InputStream in = form.openStream(); OutputStream out = resp.getOutputStream();) { + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) + out.write(buffer, 0, bytesRead); + } + } + } catch (RepositoryException e) { + e.printStackTrace(); + // TODO error message + resp.sendError(500); + } finally { + Jcr.logout(session); } } @@ -47,4 +69,8 @@ public class OdkFormServlet extends HttpServlet { odkForms.remove(odkForm.getFileName()); } + public void setRepository(Repository repository) { + this.repository = repository; + } + } diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkServletContext.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkServletContext.java index 2c37edc..1711f2d 100644 --- a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkServletContext.java +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkServletContext.java @@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse; import org.argeo.cms.servlet.PrivateWwwAuthServletContext; +/** OSK specific authentication (with additional headers).*/ public class OdkServletContext extends PrivateWwwAuthServletContext { @Override diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java index 3a97580..e5f5a3f 100644 --- a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; +import javax.jcr.Repository; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -26,6 +27,8 @@ public class OdkSubmissionServlet extends HttpServlet { private final static String XML_SUBMISSION_FILE = "xml_submission_file"; + private Repository repository; + @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { for (Part part : req.getParts()) { @@ -58,4 +61,9 @@ public class OdkSubmissionServlet extends HttpServlet { + "Form Received!" + ""); } + + public void setRepository(Repository repository) { + this.repository = repository; + } + } diff --git a/org.argeo.entity.api/src/org/argeo/entity/EntityType.java b/org.argeo.entity.api/src/org/argeo/entity/EntityType.java index 00a7803..6548d75 100644 --- a/org.argeo.entity.api/src/org/argeo/entity/EntityType.java +++ b/org.argeo.entity.api/src/org/argeo/entity/EntityType.java @@ -1,9 +1,7 @@ package org.argeo.entity; -import org.argeo.jcr.QualifiedName; - /** Types related to entities. */ -public enum EntityType implements QualifiedName { +public enum EntityType implements JcrName { // entity entity, definition, // typology diff --git a/org.argeo.entity.api/src/org/argeo/entity/JcrName.java b/org.argeo.entity.api/src/org/argeo/entity/JcrName.java new file mode 100644 index 0000000..43057aa --- /dev/null +++ b/org.argeo.entity.api/src/org/argeo/entity/JcrName.java @@ -0,0 +1,26 @@ +package org.argeo.entity; + +/** Can be applied to {@link Enum}s in order to generate prefixed names. */ +public interface JcrName { + String name(); + + default String getPrefix() { + return null; + } + + default String getNamespace() { + return null; + } + + default String get() { + String prefix = getPrefix(); + return prefix != null ? prefix + ":" + name() : name(); + } + + default String withNamespace() { + String namespace = getNamespace(); + if (namespace == null) + throw new UnsupportedOperationException("No namespace is specified for " + getClass()); + return "{" + namespace + "}" + name(); + } +} diff --git a/org.argeo.entity.api/src/org/argeo/entity/entity.cnd b/org.argeo.entity.api/src/org/argeo/entity/entity.cnd index 618487b..78fb8cc 100644 --- a/org.argeo.entity.api/src/org/argeo/entity/entity.cnd +++ b/org.argeo.entity.api/src/org/argeo/entity/entity.cnd @@ -15,12 +15,12 @@ mixin // // ENTITY DEFINITION // -[entity:definition] > entity:composite, mix:created, mix:referenceable +[entity:definition] > entity:composite, mix:created, mix:lastModified, mix:referenceable //- entity:type (String) multiple -[entity:part] +//[entity:part] -[entity:reference] +//[entity:reference] [entity:composite] orderable diff --git a/org.argeo.suite.core/src/org/argeo/suite/RankedObject.java b/org.argeo.suite.core/src/org/argeo/suite/RankedObject.java index f0165af..bfba46e 100644 --- a/org.argeo.suite.core/src/org/argeo/suite/RankedObject.java +++ b/org.argeo.suite.core/src/org/argeo/suite/RankedObject.java @@ -76,16 +76,16 @@ public class RankedObject { RankedObject rankedObject = new RankedObject<>(object, properties); if (!map.containsKey(key)) { map.put(key, rankedObject); - if (log.isDebugEnabled()) - log.debug( + if (log.isTraceEnabled()) + log.trace( "Added " + key + " as " + object.getClass().getName() + " with rank " + rankedObject.getRank()); return rankedObject; } else { RankedObject current = map.get(key); if (current.getRank() <= rankedObject.getRank()) { map.put(key, rankedObject); - if (log.isDebugEnabled()) - log.debug("Replaced " + key + " by " + object.getClass().getName() + " with rank " + if (log.isTraceEnabled()) + log.trace("Replaced " + key + " by " + object.getClass().getName() + " with rank " + rankedObject.getRank()); return rankedObject; } else { diff --git a/org.argeo.suite.core/src/org/argeo/suite/core/SuiteMaintenanceService.java b/org.argeo.suite.core/src/org/argeo/suite/core/SuiteMaintenanceService.java index 1117c5e..44d218c 100644 --- a/org.argeo.suite.core/src/org/argeo/suite/core/SuiteMaintenanceService.java +++ b/org.argeo.suite.core/src/org/argeo/suite/core/SuiteMaintenanceService.java @@ -18,7 +18,7 @@ public class SuiteMaintenanceService extends AbstractMaintenanceService { boolean modified = false; Node rootNode = adminSession.getRootNode(); if (!rootNode.hasNode(EntityNames.TERM_BASE)) { - rootNode.addNode(EntityNames.TERM_BASE, EntityType.typologies.qualified()); + rootNode.addNode(EntityNames.TERM_BASE, EntityType.typologies.get()); modified = true; } if (modified) -- 2.30.2