package org.argeo.cms.integration;
import java.io.IOException;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
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.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jackrabbit.api.JackrabbitNode;
import org.apache.jackrabbit.api.JackrabbitValue;
import org.argeo.jcr.JcrUtils;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.stream.JsonWriter;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
-/** Canonical access to a JCR repository via web services. */
+/** Access a JCR repository via web services. */
public class JcrServlet extends HttpServlet {
private static final long serialVersionUID = 6536175260540484539L;
+ private final static Log log = LogFactory.getLog(JcrServlet.class);
private final static String PARAM_VERBOSE = "verbose";
private final static String PARAM_DEPTH = "depth";
- private final static String PARAM_PRETTY = "pretty";
- private final static Log log = LogFactory.getLog(JcrServlet.class);
+ public final static String JCR_NODES = "jcr:nodes";
+ public final static String JCR_PATH = "jcr:path";
+ public final static String JCR_NAME = "jcr:name";
+
private Repository repository;
private Integer maxDepth = 8;
- private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- String path = req.getPathInfo();
if (log.isTraceEnabled())
- log.trace("Data service: " + path);
- path = URLDecoder.decode(path, StandardCharsets.UTF_8.name());
- String[] pathTokens = path.split("/");
- // TODO make it more robust
+ log.trace("Data service: " + req.getPathInfo());
- String domain = pathTokens[1];
- String dataWorkspace = "vje_" + domain;
- String jcrPath = path.substring(domain.length() + 1);
+ String dataWorkspace = getWorkspace(req);
+ String jcrPath = getJcrPath(req);
boolean verbose = req.getParameter(PARAM_VERBOSE) != null && !req.getParameter(PARAM_VERBOSE).equals("false");
int depth = 1;
if (depth > maxDepth)
throw new RuntimeException("Depth " + depth + " is higher than maximum " + maxDepth);
}
- String urlBase = null;
- if (req.getParameter("html") != null)
- urlBase = req.getServletPath() + '/' + domain;
- boolean pretty = req.getParameter(PARAM_PRETTY) != null;
- resp.setContentType("application/json");
Session session = null;
try {
// authentication
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() + "'");
+ IOUtils.copy(JcrUtils.getFileAsStream(node), resp.getOutputStream());
+ resp.flushBuffer();
+ } else {
+ resp.setContentType("application/json");
+ JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(resp.getWriter());
+ jsonGenerator.writeStartObject();
+ writeNodeProperties(node, jsonGenerator, verbose);
+ writeNodeChildren(node, jsonGenerator, depth, verbose);
+ jsonGenerator.writeEndObject();
+ jsonGenerator.flush();
+ }
+ } catch (Exception e) {
+ new CmsExceptionsChain(e).writeAsJson(objectMapper, resp);
+ } finally {
+ JcrUtils.logoutQuietly(session);
+ }
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ if (log.isTraceEnabled())
+ log.trace("Data service: " + req.getPathInfo());
- JsonWriter jsonWriter;
- if (pretty)
- jsonWriter = new GsonBuilder().setPrettyPrinting().create().newJsonWriter(resp.getWriter());
- else
- jsonWriter = gson.newJsonWriter(resp.getWriter());
- jsonWriter.beginObject();
- writeNodeProperties(node, jsonWriter, verbose, urlBase);
- writeNodeChildren(node, jsonWriter, depth, verbose, urlBase);
- jsonWriter.endObject();
- jsonWriter.flush();
- } catch (RepositoryException e) {
- resp.setStatus(500);
- throw new RuntimeException("Cannot process JCR node " + jcrPath, e);
+ String dataWorkspace = getWorkspace(req);
+ String jcrPath = getJcrPath(req);
+
+ Session session = null;
+ try {
+ // authentication
+ session = openJcrSession(req, resp, repository, dataWorkspace);
+ if (!session.itemExists(jcrPath)) {
+ String parentPath = FilenameUtils.getFullPathNoEndSeparator(jcrPath);
+ String fileName = FilenameUtils.getName(jcrPath);
+ Node folderNode = JcrUtils.mkfolders(session, parentPath);
+ byte[] bytes = IOUtils.toByteArray(req.getInputStream());
+ JcrUtils.copyBytesAsFile(folderNode, fileName, bytes);
+ } else {
+ Node node = session.getNode(jcrPath);
+ if (!node.isNodeType(NodeType.NT_FILE))
+ throw new IllegalArgumentException("Node " + jcrPath + " exists but is not a file");
+ byte[] bytes = IOUtils.toByteArray(req.getInputStream());
+ JcrUtils.copyBytesAsFile(node.getParent(), node.getName(), bytes);
+ }
+ } catch (Exception e) {
+ new CmsExceptionsChain(e).writeAsJson(objectMapper, resp);
} finally {
JcrUtils.logoutQuietly(session);
}
protected Session openJcrSession(HttpServletRequest req, HttpServletResponse resp, Repository repository,
String workspace) throws RepositoryException {
- return repository.login();
+ 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;
+ }
+
+ protected String getJcrPath(HttpServletRequest req) {
+ return req.getPathInfo();
}
- protected void writeNodeProperties(Node node, JsonWriter jsonWriter, boolean verbose, String urlBase)
+ protected void writeNodeProperties(Node node, JsonGenerator jsonGenerator, boolean verbose)
throws RepositoryException, IOException {
String jcrPath = node.getPath();
- jsonWriter.name("jcr:name");
- jsonWriter.value(node.getName());
- jsonWriter.name("jcr:path");
- jsonWriter.value(jcrPath);
+ jsonGenerator.writeStringField(JcrServlet.JCR_NAME, node.getName());
+ jsonGenerator.writeStringField(JcrServlet.JCR_PATH, jcrPath);
PropertyIterator pit = node.getProperties();
properties: while (pit.hasNext()) {
Property property = pit.nextProperty();
+
if (!verbose) {
if (property.getName().equals("jcr:primaryType") || property.getName().equals("jcr:mixinTypes")
|| property.getName().equals("jcr:created") || property.getName().equals("jcr:createdBy")
}
if (!property.isMultiple()) {
- jsonWriter.name(property.getName());
- writePropertyValue(property.getType(), property.getValue(), jsonWriter);
+ jsonGenerator.writeFieldName(property.getName());
+ writePropertyValue(property.getType(), property.getValue(), jsonGenerator);
} else {
- jsonWriter.name(property.getName());
- jsonWriter.beginArray();
+ jsonGenerator.writeFieldName(property.getName());
+ jsonGenerator.writeStartArray();
Value[] values = property.getValues();
for (Value value : values) {
- writePropertyValue(property.getType(), value, jsonWriter);
+ writePropertyValue(property.getType(), value, jsonGenerator);
}
- jsonWriter.endArray();
+ jsonGenerator.writeEndArray();
}
}
// meta data
if (verbose) {
- jsonWriter.name("jcr:identifier");
- jsonWriter.value(node.getIdentifier());
- }
- if (urlBase != null) {// TODO make it browsable
- jsonWriter.name("url");
- String url = urlBase + jcrPath;
- jsonWriter.value("<a href='" + url + "?html=true'>" + url + "</a>");
+ jsonGenerator.writeStringField("jcr:identifier", node.getIdentifier());
}
}
- protected void writePropertyValue(int type, Value value, JsonWriter jsonWriter)
+ protected void writePropertyValue(int type, Value value, JsonGenerator jsonGenerator)
throws RepositoryException, IOException {
if (type == PropertyType.DOUBLE)
- jsonWriter.value(value.getDouble());
+ jsonGenerator.writeNumber(value.getDouble());
else if (type == PropertyType.LONG)
- jsonWriter.value(value.getLong());
+ jsonGenerator.writeNumber(value.getLong());
else if (type == PropertyType.BINARY) {
if (value instanceof JackrabbitValue) {
String contentIdentity = ((JackrabbitValue) value).getContentIdentity();
- jsonWriter.value("SHA256:" + contentIdentity);
+ jsonGenerator.writeString("SHA256:" + contentIdentity);
+ } else {
+ jsonGenerator.writeNull();
}
} else
- jsonWriter.value(value.getString());
+ jsonGenerator.writeString(value.getString());
}
- protected void writeNodeChildren(Node node, JsonWriter jsonWriter, int depth, boolean verbose, String urlBase)
+ protected void writeNodeChildren(Node node, JsonGenerator jsonGenerator, int depth, boolean verbose)
throws RepositoryException, IOException {
if (!node.hasNodes())
return;
if (depth <= 0)
return;
NodeIterator nit = node.getNodes();
- jsonWriter.name("jcr:nodes");
- jsonWriter.beginArray();
- while (nit.hasNext()) {
+ jsonGenerator.writeFieldName(JcrServlet.JCR_NODES);
+ jsonGenerator.writeStartArray();
+ children: while (nit.hasNext()) {
Node child = nit.nextNode();
- jsonWriter.beginObject();
- writeNodeProperties(child, jsonWriter, verbose, urlBase);
- writeNodeChildren(child, jsonWriter, depth - 1, verbose, urlBase);
- jsonWriter.endObject();
+
+ if (child.getName().startsWith("rep:")) {
+ continue children;// skip Jackrabbit auth metadata
+ }
+
+ jsonGenerator.writeStartObject();
+ writeNodeProperties(child, jsonGenerator, verbose);
+ writeNodeChildren(child, jsonGenerator, depth - 1, verbose);
+ jsonGenerator.writeEndObject();
}
- jsonWriter.endArray();
+ jsonGenerator.writeEndArray();
}
public void setRepository(Repository repository) {