Improve JCR web service
authorMathieu Baudier <mbaudier@argeo.org>
Mon, 27 Jan 2020 10:47:28 +0000 (11:47 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Mon, 27 Jan 2020 10:47:28 +0000 (11:47 +0100)
org.argeo.cms/src/org/argeo/cms/integration/CmsExceptionsChain.java
org.argeo.cms/src/org/argeo/cms/integration/JcrReadServlet.java
org.argeo.cms/src/org/argeo/cms/integration/JcrWriteServlet.java

index a659a7e9cc5c2a59fe249fca4cd4ca3bb21707df..2ee1c30dd3d4b401e01d5490466b2cb18e0e849d 100644 (file)
@@ -29,7 +29,7 @@ public class CmsExceptionsChain extends ExceptionsChain {
 
        public String toJsonString(ObjectMapper objectMapper) {
                try {
-                       return objectMapper.writeValueAsString(this);
+                       return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(this);
                } catch (JsonProcessingException e) {
                        throw new IllegalStateException("Cannot write system exceptions " + toString(), e);
                }
@@ -37,7 +37,7 @@ public class CmsExceptionsChain extends ExceptionsChain {
 
        public void writeAsJson(ObjectMapper objectMapper, Writer writer) {
                try {
-                       JsonGenerator jg = objectMapper.getFactory().createGenerator(writer);
+                       JsonGenerator jg = objectMapper.writerWithDefaultPrettyPrinter().getFactory().createGenerator(writer);
                        jg.writeObject(this);
                } catch (IOException e) {
                        throw new IllegalStateException("Cannot write system exceptions " + toString(), e);
@@ -54,27 +54,27 @@ public class CmsExceptionsChain extends ExceptionsChain {
                }
        }
 
-       public static void main(String[] args) throws Exception {
-               try {
-                       try {
-                               try {
-                                       testDeeper();
-                               } catch (Exception e) {
-                                       throw new Exception("Less deep exception", e);
-                               }
-                       } catch (Exception e) {
-                               throw new RuntimeException("Top exception", e);
-                       }
-               } catch (Exception e) {
-                       CmsExceptionsChain vjeSystemErrors = new CmsExceptionsChain(e);
-                       ObjectMapper objectMapper = new ObjectMapper();
-                       System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(vjeSystemErrors));
-                       e.printStackTrace();
-               }
-       }
-
-       static void testDeeper() throws Exception {
-               throw new IllegalStateException("Deep exception");
-       }
+//     public static void main(String[] args) throws Exception {
+//             try {
+//                     try {
+//                             try {
+//                                     testDeeper();
+//                             } catch (Exception e) {
+//                                     throw new Exception("Less deep exception", e);
+//                             }
+//                     } catch (Exception e) {
+//                             throw new RuntimeException("Top exception", e);
+//                     }
+//             } catch (Exception e) {
+//                     CmsExceptionsChain vjeSystemErrors = new CmsExceptionsChain(e);
+//                     ObjectMapper objectMapper = new ObjectMapper();
+//                     System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(vjeSystemErrors));
+//                     e.printStackTrace();
+//             }
+//     }
+//
+//     static void testDeeper() throws Exception {
+//             throw new IllegalStateException("Deep exception");
+//     }
 
 }
index 7ef1795d0e4076a9762477442fe4e58ef633f8ae..f4bcc15d42091e167e7a45fece92312e7aaf9351 100644 (file)
@@ -1,7 +1,12 @@
 package org.argeo.cms.integration;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
@@ -35,18 +40,24 @@ public class JcrReadServlet extends HttpServlet {
        private static final long serialVersionUID = 6536175260540484539L;
        private final static Log log = LogFactory.getLog(JcrReadServlet.class);
 
+       protected final static String ACCEPT_HTTP_HEADER = "Accept";
+       protected final static String CONTENT_DISPOSITION_HTTP_HEADER = "Content-Disposition";
+
+       protected final static String OCTET_STREAM_CONTENT_TYPE = "application/octet-stream";
+       protected final static String XML_CONTENT_TYPE = "application/xml";
+       protected final static String JSON_CONTENT_TYPE = "application/json";
+
        private final static String PARAM_VERBOSE = "verbose";
        private final static String PARAM_DEPTH = "depth";
 
-       public final static String JCR_NODES = "jcr:nodes";
+       protected final static String JCR_NODES = "jcr:nodes";
        // cf. javax.jcr.Property
-       public final static String JCR_PATH = "path";
-       public final static String JCR_NAME = "name";
-//     public final static String JCR_ID = "id";
+       protected final static String JCR_PATH = "path";
+       protected final static String JCR_NAME = "name";
 
-       final static String JCR_ = "jcr_";
-       final static String JCR_PREFIX = "jcr:";
-       final static String REP_PREFIX = "rep:";
+       protected final static String _JCR = "_jcr";
+       protected final static String JCR_PREFIX = "jcr:";
+       protected final static String REP_PREFIX = "rep:";
 
        private Repository repository;
        private Integer maxDepth = 8;
@@ -76,13 +87,25 @@ public class JcrReadServlet extends HttpServlet {
                        if (!session.itemExists(jcrPath))
                                throw new RuntimeException("JCR node " + jcrPath + " does not exist");
                        Node node = session.getNode(jcrPath);
-                       if (node.isNodeType(NodeType.NT_FILE)) {
-                               resp.setContentType("application/octet-stream");
-                               resp.addHeader("Content-Disposition", "attachment; filename='" + node.getName() + "'");
+
+                       List<String> acceptHeader = readAcceptHeader(req);
+                       if (!acceptHeader.isEmpty() && node.isNodeType(NodeType.NT_FILE)) {
+                               resp.setContentType(OCTET_STREAM_CONTENT_TYPE);
+                               resp.addHeader(CONTENT_DISPOSITION_HTTP_HEADER, "attachment; filename='" + node.getName() + "'");
                                IOUtils.copy(JcrUtils.getFileAsStream(node), resp.getOutputStream());
                                resp.flushBuffer();
                        } else {
-                               resp.setContentType("application/json");
+                               if (!acceptHeader.isEmpty() && acceptHeader.get(0).equals(XML_CONTENT_TYPE)) {
+                                       // TODO Use req.startAsync(); ?
+                                       resp.setContentType(XML_CONTENT_TYPE);
+                                       session.exportSystemView(node.getPath(), resp.getOutputStream(), false, depth <= 1);
+                                       return;
+                               }
+                               if (!acceptHeader.isEmpty() && !acceptHeader.contains(JSON_CONTENT_TYPE)) {
+                                       log.warn("Content type " + acceptHeader + " in Accept header is not supported. Supported: "
+                                                       + JSON_CONTENT_TYPE + " (default), " + XML_CONTENT_TYPE);
+                               }
+                               resp.setContentType(JSON_CONTENT_TYPE);
                                JsonGenerator jsonGenerator = getObjectMapper().getFactory().createGenerator(resp.getWriter());
                                jsonGenerator.writeStartObject();
                                writeNodeChildren(node, jsonGenerator, depth, verbose);
@@ -102,29 +125,50 @@ public class JcrReadServlet extends HttpServlet {
                return workspace != null ? repository.login(workspace) : repository.login();
        }
 
-       /**
-        * To be overridden.
-        * 
-        * @return the workspace to use, or <code>null</code> if default should be used.
-        */
        protected String getWorkspace(HttpServletRequest req) {
-               return null;
+               String path = req.getPathInfo();
+               try {
+                       path = URLDecoder.decode(path, StandardCharsets.UTF_8.name());
+               } catch (UnsupportedEncodingException e) {
+                       throw new IllegalArgumentException(e);
+               }
+               String[] pathTokens = path.split("/");
+               return pathTokens[1];
        }
 
        protected String getJcrPath(HttpServletRequest req) {
-               return req.getPathInfo();
+               String path = req.getPathInfo();
+               try {
+                       path = URLDecoder.decode(path, StandardCharsets.UTF_8.name());
+               } catch (UnsupportedEncodingException e) {
+                       throw new IllegalArgumentException(e);
+               }
+               String[] pathTokens = path.split("/");
+               String domain = pathTokens[1];
+               String jcrPath = path.substring(domain.length() + 1);
+               return jcrPath;
+       }
+
+       protected List<String> readAcceptHeader(HttpServletRequest req) {
+               List<String> lst = new ArrayList<>();
+               String acceptHeader = req.getHeader(ACCEPT_HTTP_HEADER);
+               if (acceptHeader == null)
+                       return lst;
+//             Enumeration<String> acceptHeader = req.getHeaders(ACCEPT_HTTP_HEADER);
+//             while (acceptHeader.hasMoreElements()) {
+               String[] arr = acceptHeader.split("\\.");
+               for (int i = 0; i < arr.length; i++) {
+                       String str = arr[i].trim();
+                       if (!"".equals(str))
+                               lst.add(str);
+               }
+//             }
+               return lst;
        }
 
        protected void writeNodeProperties(Node node, JsonGenerator jsonGenerator, boolean verbose)
                        throws RepositoryException, IOException {
                String jcrPath = node.getPath();
-//             jsonGenerator.writeStringField(JCR_NAME, node.getName());
-//             jsonGenerator.writeStringField(JCR_PATH, jcrPath);
-//             // meta data
-//             if (verbose) {
-//                     jsonGenerator.writeStringField(JCR_ID, node.getIdentifier());
-//             }
-
                Map<String, Map<String, Property>> namespaces = new TreeMap<>();
 
                PropertyIterator pit = node.getProperties();
@@ -134,7 +178,9 @@ public class JcrReadServlet extends HttpServlet {
                        final String propertyName = property.getName();
                        int columnIndex = propertyName.indexOf(':');
                        if (columnIndex > 0) {
-                               String prefix = propertyName.substring(0, columnIndex) + "_";
+                               // mark prefix with a '_' before the name of the object, according to JSON
+                               // conventions to indicate a special value
+                               String prefix = "_" + propertyName.substring(0, columnIndex);
                                String unqualifiedName = propertyName.substring(columnIndex + 1);
                                if (!namespaces.containsKey(prefix))
                                        namespaces.put(prefix, new LinkedHashMap<String, Property>());
@@ -144,28 +190,6 @@ public class JcrReadServlet extends HttpServlet {
                                continue properties;
                        }
 
-//                     String fieldName = property.getName();
-//                     switch (fieldName) {
-//                     case "jcr:title":
-//                     case "jcr:description":
-//                     case "jcr:created":
-//                     case "jcr:createdBy":
-//                     case "jcr:lastModified":
-//                     case "jcr:lastModifiedBy":
-//                             fieldName = fieldName.substring(JCR_PREFIX.length());
-//                     }
-//
-//                     if (!verbose) {
-////                           if (property.getName().equals("jcr:primaryType") || property.getName().equals("jcr:mixinTypes")
-////                                           || property.getName().equals("jcr:created") || property.getName().equals("jcr:createdBy")
-////                                           || property.getName().equals("jcr:lastModified")
-////                                           || property.getName().equals("jcr:lastModifiedBy")) {
-////                                   continue properties;// skip
-////                           }
-//                             if (fieldName.startsWith(JCR_PREFIX))
-//                                     continue properties;
-//                     }
-
                        if (property.getType() == PropertyType.BINARY) {
                                if (!(node instanceof JackrabbitNode)) {
                                        continue properties;// skip
@@ -179,10 +203,9 @@ public class JcrReadServlet extends HttpServlet {
                        Map<String, Property> map = namespaces.get(prefix);
                        jsonGenerator.writeFieldName(prefix);
                        jsonGenerator.writeStartObject();
-                       if (JCR_.equals(prefix)) {
+                       if (_JCR.equals(prefix)) {
                                jsonGenerator.writeStringField(JCR_NAME, node.getName());
                                jsonGenerator.writeStringField(JCR_PATH, jcrPath);
-//                             jsonGenerator.writeStringField(JCR_ID, node.getIdentifier());
                        }
                        properties: for (String unqualifiedName : map.keySet()) {
                                Property property = map.get(unqualifiedName);
@@ -252,24 +275,6 @@ public class JcrReadServlet extends HttpServlet {
                        writeNodeProperties(child, jsonGenerator, verbose);
                        jsonGenerator.writeEndObject();
                }
-
-               // old
-//             nit = node.getNodes();
-//             jsonGenerator.writeFieldName(JcrServlet.JCR_NODES);
-//             jsonGenerator.writeStartArray();
-//             children: while (nit.hasNext()) {
-//                     Node child = nit.nextNode();
-//
-//                     if (child.getName().startsWith(REP_PREFIX)) {
-//                             continue children;// skip Jackrabbit auth metadata
-//                     }
-//
-//                     jsonGenerator.writeStartObject();
-//                     writeNodeProperties(child, jsonGenerator, verbose);
-//                     writeNodeChildren(child, jsonGenerator, depth - 1, verbose);
-//                     jsonGenerator.writeEndObject();
-//             }
-//             jsonGenerator.writeEndArray();
        }
 
        public void setRepository(Repository repository) {
index d678ccbbe0aab06becc51cc0e4dde329105c4105..68312173443ad07b61a888d0e49fdd6f99c95963 100644 (file)
@@ -2,6 +2,7 @@ package org.argeo.cms.integration;
 
 import java.io.IOException;
 
+import javax.jcr.ImportUUIDBehavior;
 import javax.jcr.Node;
 import javax.jcr.Session;
 import javax.jcr.nodetype.NodeType;
@@ -32,10 +33,18 @@ public class JcrWriteServlet extends JcrReadServlet {
                try {
                        // authentication
                        session = openJcrSession(req, resp, getRepository(), dataWorkspace);
+
+                       if (req.getContentType() != null && req.getContentType().equals(XML_CONTENT_TYPE)) {
+//                             resp.setContentType(XML_CONTENT_TYPE);
+                               session.getWorkspace().importXML(jcrPath, req.getInputStream(),
+                                               ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING);
+                               return;
+                       }
+
                        if (!session.itemExists(jcrPath)) {
                                String parentPath = FilenameUtils.getFullPathNoEndSeparator(jcrPath);
                                String fileName = FilenameUtils.getName(jcrPath);
-                               Node folderNode = JcrUtils.mkdirs(session, parentPath);
+                               Node folderNode = JcrUtils.mkfolders(session, parentPath);
                                byte[] bytes = IOUtils.toByteArray(req.getInputStream());
                                JcrUtils.copyBytesAsFile(folderNode, fileName, bytes);
                        } else {