From: Mathieu Baudier Date: Mon, 27 Jan 2020 10:47:28 +0000 (+0100) Subject: Improve JCR web service X-Git-Tag: argeo-commons-2.1.85~16 X-Git-Url: https://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=2f47a72478c876464ac31cecc138183fccfeac91 Improve JCR web service --- diff --git a/org.argeo.cms/src/org/argeo/cms/integration/CmsExceptionsChain.java b/org.argeo.cms/src/org/argeo/cms/integration/CmsExceptionsChain.java index a659a7e9c..2ee1c30dd 100644 --- a/org.argeo.cms/src/org/argeo/cms/integration/CmsExceptionsChain.java +++ b/org.argeo.cms/src/org/argeo/cms/integration/CmsExceptionsChain.java @@ -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"); +// } } diff --git a/org.argeo.cms/src/org/argeo/cms/integration/JcrReadServlet.java b/org.argeo.cms/src/org/argeo/cms/integration/JcrReadServlet.java index 7ef1795d0..f4bcc15d4 100644 --- a/org.argeo.cms/src/org/argeo/cms/integration/JcrReadServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/integration/JcrReadServlet.java @@ -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 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 null 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 readAcceptHeader(HttpServletRequest req) { + List lst = new ArrayList<>(); + String acceptHeader = req.getHeader(ACCEPT_HTTP_HEADER); + if (acceptHeader == null) + return lst; +// Enumeration 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> 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()); @@ -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 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) { diff --git a/org.argeo.cms/src/org/argeo/cms/integration/JcrWriteServlet.java b/org.argeo.cms/src/org/argeo/cms/integration/JcrWriteServlet.java index d678ccbbe..683121734 100644 --- a/org.argeo.cms/src/org/argeo/cms/integration/JcrWriteServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/integration/JcrWriteServlet.java @@ -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 {