1 package org
.argeo
.server
.jcr
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.util
.ArrayList
;
6 import java
.util
.Calendar
;
8 import java
.util
.StringTokenizer
;
10 import javax
.activation
.MimetypesFileTypeMap
;
11 import javax
.jcr
.Node
;
12 import javax
.jcr
.Property
;
13 import javax
.jcr
.PropertyIterator
;
14 import javax
.jcr
.Repository
;
15 import javax
.jcr
.RepositoryException
;
16 import javax
.jcr
.Session
;
17 import javax
.jcr
.SimpleCredentials
;
18 import javax
.jcr
.Value
;
19 import javax
.jcr
.version
.Version
;
20 import javax
.jcr
.version
.VersionHistory
;
21 import javax
.jcr
.version
.VersionIterator
;
23 import org
.apache
.commons
.io
.FilenameUtils
;
24 import org
.apache
.commons
.logging
.Log
;
25 import org
.apache
.commons
.logging
.LogFactory
;
26 import org
.argeo
.ArgeoException
;
27 import org
.springframework
.beans
.factory
.DisposableBean
;
28 import org
.springframework
.beans
.factory
.InitializingBean
;
29 import org
.springframework
.core
.io
.Resource
;
31 public class JcrResourceAdapter
implements InitializingBean
, DisposableBean
{
32 private final static Log log
= LogFactory
.getLog(JcrResourceAdapter
.class);
34 private Repository repository
;
36 private String username
;
37 private String password
;
39 private Session session
;
41 private Boolean versioning
= true;
42 private String defaultEncoding
= "UTF-8";
44 // private String restoreBase = "/.restore";
46 public void mkdirs(String path
) {
48 StringTokenizer st
= new StringTokenizer(path
, "/");
49 StringBuffer current
= new StringBuffer("/");
50 Node currentNode
= session().getRootNode();
51 while (st
.hasMoreTokens()) {
52 String part
= st
.nextToken();
53 current
.append(part
).append('/');
54 if (!session().itemExists(current
.toString())) {
55 currentNode
= currentNode
.addNode(part
, "nt:folder");
57 currentNode
.addMixin("mix:versionable");
58 if (log
.isTraceEnabled())
59 log
.debug("Added folder " + part
+ " as " + current
);
61 currentNode
= (Node
) session().getItem(current
.toString());
65 } catch (RepositoryException e
) {
66 throw new ArgeoException("Cannot mkdirs " + path
, e
);
70 public void create(String path
, Resource file
, String mimeType
) {
72 create(path
, file
.getInputStream(), mimeType
);
73 } catch (IOException e
) {
74 throw new ArgeoException("Cannot read " + file
, e
);
78 public void create(String path
, InputStream in
, String mimeType
) {
80 if (session().itemExists(path
)) {
81 throw new ArgeoException("Node " + path
+ " already exists.");
84 int index
= path
.lastIndexOf('/');
85 String parentPath
= path
.substring(0, index
);
86 if (parentPath
.equals(""))
88 String fileName
= path
.substring(index
+ 1);
89 if (!session().itemExists(parentPath
))
90 throw new ArgeoException("Parent folder of node " + path
91 + " does not exist: " + parentPath
);
93 Node folderNode
= (Node
) session().getItem(parentPath
);
94 Node fileNode
= folderNode
.addNode(fileName
, "nt:file");
96 Node contentNode
= fileNode
.addNode("jcr:content", "nt:resource");
98 contentNode
.setProperty("jcr:mimeType", mimeType
);
99 contentNode
.setProperty("jcr:encoding", defaultEncoding
);
100 contentNode
.setProperty("jcr:data", in
);
101 Calendar lastModified
= Calendar
.getInstance();
102 // lastModified.setTimeInMillis(file.lastModified());
103 contentNode
.setProperty("jcr:lastModified", lastModified
);
104 // resNode.addMixin("mix:referenceable");
107 fileNode
.addMixin("mix:versionable");
114 if (log
.isDebugEnabled())
115 log
.debug("Created " + path
);
116 } catch (Exception e
) {
117 throw new ArgeoException("Cannot create node for " + path
, e
);
122 public void update(String path
, Resource file
) {
124 update(path
, file
.getInputStream());
125 } catch (IOException e
) {
126 throw new ArgeoException("Cannot read " + file
, e
);
130 public void update(String path
, InputStream in
) {
133 if (!session().itemExists(path
)) {
134 String type
= new MimetypesFileTypeMap()
135 .getContentType(FilenameUtils
.getName(path
));
136 create(path
, in
, type
);
140 Node fileNode
= (Node
) session().getItem(path
);
141 Node contentNode
= fileNode
.getNode("jcr:content");
143 contentNode
.setProperty("jcr:data", in
);
144 Calendar lastModified
= Calendar
.getInstance();
145 // lastModified.setTimeInMillis(file.lastModified());
146 contentNode
.setProperty("jcr:lastModified", lastModified
);
151 if (log
.isDebugEnabled())
152 log
.debug("Updated " + path
);
153 } catch (Exception e
) {
154 throw new ArgeoException("Cannot update node " + path
, e
);
158 public List
<Calendar
> listVersions(String path
) {
160 throw new ArgeoException("Versioning is not activated");
163 List
<Calendar
> versions
= new ArrayList
<Calendar
>();
164 Node fileNode
= (Node
) session().getItem(path
);
165 VersionHistory history
= fileNode
.getVersionHistory();
166 for (VersionIterator it
= history
.getAllVersions(); it
.hasNext();) {
167 Version version
= (Version
) it
.next();
168 versions
.add(version
.getCreated());
169 if (log
.isTraceEnabled()) {
175 } catch (Exception e
) {
176 throw new ArgeoException("Cannot list version of node " + path
, e
);
180 public InputStream
retrieve(String path
) {
182 Node node
= (Node
) session().getItem(path
+ "/jcr:content");
183 Property property
= node
.getProperty("jcr:data");
184 return property
.getStream();
185 } catch (Exception e
) {
186 throw new ArgeoException("Cannot retrieve " + path
, e
);
190 public synchronized InputStream
retrieve(String path
, Integer revision
) {
192 throw new ArgeoException("Versioning is not activated");
195 Node fileNode
= (Node
) session().getItem(path
);
197 // if (revision == 0) {
198 // InputStream in = fromVersion(fileNode.getBaseVersion());
199 // if (log.isDebugEnabled())
200 // log.debug("Retrieved " + path + " at base revision ");
204 VersionHistory history
= fileNode
.getVersionHistory();
206 Version version
= null;
207 for (VersionIterator it
= history
.getAllVersions(); it
.hasNext();) {
208 version
= (Version
) it
.next();
209 if (count
== revision
+ 1) {
210 InputStream in
= fromVersion(version
);
211 if (log
.isDebugEnabled())
212 log
.debug("Retrieved " + path
+ " at revision "
218 } catch (Exception e
) {
219 throw new ArgeoException("Cannot retrieve version " + revision
223 throw new ArgeoException("Version " + revision
224 + " does not exist for node " + path
);
227 protected InputStream
fromVersion(Version version
)
228 throws RepositoryException
{
229 Node frozenNode
= version
.getNode("jcr:frozenNode");
230 InputStream in
= frozenNode
.getNode("jcr:content").getProperty(
231 "jcr:data").getStream();
235 // protected InputStream restoreOrGetRevision(Node fileNode, Version
237 // Integer revision) throws RepositoryException {
238 // Node parentFolder = (Node) fileNode
239 // .getAncestor(fileNode.getDepth() - 1);
240 // String restoreFolderPath = restoreBase + parentFolder.getPath();
241 // mkdirs(restoreFolderPath);
242 // String subNodeName = fileNode.getName() + "__" + fill(revision);
243 // Node restoreFolder = (Node) session().getItem(restoreFolderPath);
244 // if (!restoreFolder.hasNode(subNodeName)) {
245 // parentFolder.restore(version, subNodeName, true);
247 // return parentFolder.getNode(subNodeName + "/jcr:content").getProperty(
248 // "jcr:data").getStream();
251 protected Session
session() {
255 public void afterPropertiesSet() throws Exception
{
256 session
= repository
.login(new SimpleCredentials(username
, password
260 public void destroy() throws Exception
{
264 public void setRepository(Repository repository
) {
265 this.repository
= repository
;
268 public void setUsername(String username
) {
269 this.username
= username
;
272 public void setPassword(String password
) {
273 this.password
= password
;
276 public void setVersioning(Boolean versioning
) {
277 this.versioning
= versioning
;
280 public void setDefaultEncoding(String defaultEncoding
) {
281 this.defaultEncoding
= defaultEncoding
;
284 /** Recursively outputs the contents of the given node. */
285 public static void debug(Node node
) throws RepositoryException
{
286 // First output the node path
287 log
.debug(node
.getPath());
288 // Skip the virtual (and large!) jcr:system subtree
289 if (node
.getName().equals("jcr:system")) {
293 // Then output the properties
294 PropertyIterator properties
= node
.getProperties();
295 while (properties
.hasNext()) {
296 Property property
= properties
.nextProperty();
297 if (property
.getDefinition().isMultiple()) {
298 // A multi-valued property, print all values
299 Value
[] values
= property
.getValues();
300 for (int i
= 0; i
< values
.length
; i
++) {
301 log
.debug(property
.getPath() + " = "
302 + values
[i
].getString());
305 // A single-valued property
306 log
.debug(property
.getPath() + " = " + property
.getString());
312 protected String
fill(Integer number
) {
314 String str
= number
.toString();
315 for (int i
= str
.length(); i
< size
; i
++) {