1 package org
.argeo
.cms
.integration
;
3 import java
.io
.IOException
;
6 import javax
.jcr
.NodeIterator
;
7 import javax
.jcr
.Property
;
8 import javax
.jcr
.PropertyIterator
;
9 import javax
.jcr
.PropertyType
;
10 import javax
.jcr
.Repository
;
11 import javax
.jcr
.RepositoryException
;
12 import javax
.jcr
.Session
;
13 import javax
.jcr
.Value
;
14 import javax
.jcr
.nodetype
.NodeType
;
15 import javax
.servlet
.ServletException
;
16 import javax
.servlet
.http
.HttpServlet
;
17 import javax
.servlet
.http
.HttpServletRequest
;
18 import javax
.servlet
.http
.HttpServletResponse
;
20 import org
.apache
.commons
.io
.FilenameUtils
;
21 import org
.apache
.commons
.io
.IOUtils
;
22 import org
.apache
.commons
.logging
.Log
;
23 import org
.apache
.commons
.logging
.LogFactory
;
24 import org
.apache
.jackrabbit
.api
.JackrabbitNode
;
25 import org
.apache
.jackrabbit
.api
.JackrabbitValue
;
26 import org
.argeo
.jcr
.JcrUtils
;
28 import com
.fasterxml
.jackson
.core
.JsonGenerator
;
29 import com
.fasterxml
.jackson
.databind
.ObjectMapper
;
31 /** Access a JCR repository via web services. */
32 public class JcrServlet
extends HttpServlet
{
33 private static final long serialVersionUID
= 6536175260540484539L;
34 private final static Log log
= LogFactory
.getLog(JcrServlet
.class);
36 private final static String PARAM_VERBOSE
= "verbose";
37 private final static String PARAM_DEPTH
= "depth";
39 public final static String JCR_NODES
= "jcr:nodes";
40 public final static String JCR_PATH
= "jcr:path";
41 public final static String JCR_NAME
= "jcr:name";
44 private Repository repository
;
45 private Integer maxDepth
= 8;
47 private ObjectMapper objectMapper
= new ObjectMapper();
50 protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
51 if (log
.isTraceEnabled())
52 log
.trace("Data service: " + req
.getPathInfo());
54 String dataWorkspace
= getWorkspace(req
);
55 String jcrPath
= getJcrPath(req
);
57 boolean verbose
= req
.getParameter(PARAM_VERBOSE
) != null && !req
.getParameter(PARAM_VERBOSE
).equals("false");
59 if (req
.getParameter(PARAM_DEPTH
) != null) {
60 depth
= Integer
.parseInt(req
.getParameter(PARAM_DEPTH
));
62 throw new RuntimeException("Depth " + depth
+ " is higher than maximum " + maxDepth
);
65 Session session
= null;
68 session
= openJcrSession(req
, resp
, repository
, dataWorkspace
);
69 if (!session
.itemExists(jcrPath
))
70 throw new RuntimeException("JCR node " + jcrPath
+ " does not exist");
71 Node node
= session
.getNode(jcrPath
);
72 if (node
.isNodeType(NodeType
.NT_FILE
)) {
73 resp
.setContentType("application/octet-stream");
74 resp
.addHeader("Content-Disposition", "attachment; filename='" + node
.getName() + "'");
75 IOUtils
.copy(JcrUtils
.getFileAsStream(node
), resp
.getOutputStream());
78 resp
.setContentType("application/json");
79 JsonGenerator jsonGenerator
= objectMapper
.getFactory().createGenerator(resp
.getWriter());
80 jsonGenerator
.writeStartObject();
81 writeNodeProperties(node
, jsonGenerator
, verbose
);
82 writeNodeChildren(node
, jsonGenerator
, depth
, verbose
);
83 jsonGenerator
.writeEndObject();
84 jsonGenerator
.flush();
86 } catch (Exception e
) {
87 new CmsExceptionsChain(e
).writeAsJson(objectMapper
, resp
);
89 JcrUtils
.logoutQuietly(session
);
94 protected void doPost(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
95 if (log
.isTraceEnabled())
96 log
.trace("Data service: " + req
.getPathInfo());
98 String dataWorkspace
= getWorkspace(req
);
99 String jcrPath
= getJcrPath(req
);
101 Session session
= null;
104 session
= openJcrSession(req
, resp
, repository
, dataWorkspace
);
105 if (!session
.itemExists(jcrPath
)) {
106 String parentPath
= FilenameUtils
.getFullPathNoEndSeparator(jcrPath
);
107 String fileName
= FilenameUtils
.getName(jcrPath
);
108 Node folderNode
= JcrUtils
.mkfolders(session
, parentPath
);
109 byte[] bytes
= IOUtils
.toByteArray(req
.getInputStream());
110 JcrUtils
.copyBytesAsFile(folderNode
, fileName
, bytes
);
112 Node node
= session
.getNode(jcrPath
);
113 if (!node
.isNodeType(NodeType
.NT_FILE
))
114 throw new IllegalArgumentException("Node " + jcrPath
+ " exists but is not a file");
115 byte[] bytes
= IOUtils
.toByteArray(req
.getInputStream());
116 JcrUtils
.copyBytesAsFile(node
.getParent(), node
.getName(), bytes
);
118 } catch (Exception e
) {
119 new CmsExceptionsChain(e
).writeAsJson(objectMapper
, resp
);
121 JcrUtils
.logoutQuietly(session
);
125 protected Session
openJcrSession(HttpServletRequest req
, HttpServletResponse resp
, Repository repository
,
126 String workspace
) throws RepositoryException
{
127 return workspace
!= null ? repository
.login(workspace
) : repository
.login();
133 * @return the workspace to use, or <code>null</code> if default should be used.
135 protected String
getWorkspace(HttpServletRequest req
) {
139 protected String
getJcrPath(HttpServletRequest req
) {
140 return req
.getPathInfo();
143 protected void writeNodeProperties(Node node
, JsonGenerator jsonGenerator
, boolean verbose
)
144 throws RepositoryException
, IOException
{
145 String jcrPath
= node
.getPath();
146 jsonGenerator
.writeStringField(JcrServlet
.JCR_NAME
, node
.getName());
147 jsonGenerator
.writeStringField(JcrServlet
.JCR_PATH
, jcrPath
);
149 PropertyIterator pit
= node
.getProperties();
150 properties
: while (pit
.hasNext()) {
151 Property property
= pit
.nextProperty();
154 if (property
.getName().equals("jcr:primaryType") || property
.getName().equals("jcr:mixinTypes")
155 || property
.getName().equals("jcr:created") || property
.getName().equals("jcr:createdBy")
156 || property
.getName().equals("jcr:lastModified")
157 || property
.getName().equals("jcr:lastModifiedBy")) {
158 continue properties
;// skip
162 if (property
.getType() == PropertyType
.BINARY
) {
163 if (!(node
instanceof JackrabbitNode
)) {
164 continue properties
;// skip
168 if (!property
.isMultiple()) {
169 jsonGenerator
.writeFieldName(property
.getName());
170 writePropertyValue(property
.getType(), property
.getValue(), jsonGenerator
);
172 jsonGenerator
.writeFieldName(property
.getName());
173 jsonGenerator
.writeStartArray();
174 Value
[] values
= property
.getValues();
175 for (Value value
: values
) {
176 writePropertyValue(property
.getType(), value
, jsonGenerator
);
178 jsonGenerator
.writeEndArray();
184 jsonGenerator
.writeStringField("jcr:identifier", node
.getIdentifier());
188 protected void writePropertyValue(int type
, Value value
, JsonGenerator jsonGenerator
)
189 throws RepositoryException
, IOException
{
190 if (type
== PropertyType
.DOUBLE
)
191 jsonGenerator
.writeNumber(value
.getDouble());
192 else if (type
== PropertyType
.LONG
)
193 jsonGenerator
.writeNumber(value
.getLong());
194 else if (type
== PropertyType
.BINARY
) {
195 if (value
instanceof JackrabbitValue
) {
196 String contentIdentity
= ((JackrabbitValue
) value
).getContentIdentity();
197 jsonGenerator
.writeString("SHA256:" + contentIdentity
);
199 jsonGenerator
.writeNull();
202 jsonGenerator
.writeString(value
.getString());
205 protected void writeNodeChildren(Node node
, JsonGenerator jsonGenerator
, int depth
, boolean verbose
)
206 throws RepositoryException
, IOException
{
207 if (!node
.hasNodes())
211 NodeIterator nit
= node
.getNodes();
212 jsonGenerator
.writeFieldName(JcrServlet
.JCR_NODES
);
213 jsonGenerator
.writeStartArray();
214 children
: while (nit
.hasNext()) {
215 Node child
= nit
.nextNode();
217 if (child
.getName().startsWith("rep:")) {
218 continue children
;// skip Jackrabbit auth metadata
221 jsonGenerator
.writeStartObject();
222 writeNodeProperties(child
, jsonGenerator
, verbose
);
223 writeNodeChildren(child
, jsonGenerator
, depth
- 1, verbose
);
224 jsonGenerator
.writeEndObject();
226 jsonGenerator
.writeEndArray();
229 public void setRepository(Repository repository
) {
230 this.repository
= repository
;
233 public void setMaxDepth(Integer maxDepth
) {
234 this.maxDepth
= maxDepth
;