1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.jcr.wrapper;
35
36 import info.magnolia.context.MgnlContext;
37 import info.magnolia.jcr.decoration.ContentDecorator;
38 import info.magnolia.jcr.decoration.ContentDecoratorPropertyWrapper;
39 import info.magnolia.jcr.decoration.ContentDecoratorSessionWrapper;
40 import info.magnolia.jcr.decoration.ContentDecoratorWorkspaceWrapper;
41 import info.magnolia.jcr.util.NodeTypes;
42 import info.magnolia.jcr.util.NodeTypes.LastModified;
43 import info.magnolia.repository.RepositoryConstants;
44
45 import java.io.InputStream;
46 import java.math.BigDecimal;
47 import java.util.ArrayList;
48 import java.util.Calendar;
49 import java.util.List;
50 import java.util.Map;
51
52 import javax.jcr.AccessDeniedException;
53 import javax.jcr.Binary;
54 import javax.jcr.InvalidItemStateException;
55 import javax.jcr.ItemExistsException;
56 import javax.jcr.NoSuchWorkspaceException;
57 import javax.jcr.Node;
58 import javax.jcr.NodeIterator;
59 import javax.jcr.PathNotFoundException;
60 import javax.jcr.Property;
61 import javax.jcr.ReferentialIntegrityException;
62 import javax.jcr.RepositoryException;
63 import javax.jcr.Session;
64 import javax.jcr.Value;
65 import javax.jcr.ValueFormatException;
66 import javax.jcr.Workspace;
67 import javax.jcr.lock.LockException;
68 import javax.jcr.nodetype.ConstraintViolationException;
69 import javax.jcr.nodetype.NoSuchNodeTypeException;
70 import javax.jcr.version.VersionException;
71
72 import org.apache.commons.lang.ArrayUtils;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76
77
78
79 public class MgnlPropertySettingContentDecorator extends PropertyAndChildWrappingContentDecorator implements ContentDecorator {
80
81 private static final Logger log = LoggerFactory.getLogger(MgnlPropertySettingContentDecorator.class);
82
83
84
85
86
87 private static Map<String, String> parentNodeMappings = ArrayUtils.toMap(new String[][] {
88 { RepositoryConstants.WEBSITE, NodeTypes.Page.NAME },
89 { RepositoryConstants.USERS, NodeTypes.User.NAME } });
90
91 protected boolean isSysSessionDirty;
92
93
94
95
96
97 final class ChangeLastUpdateDateOp extends MgnlContext.RepositoryOp {
98 private final String workspaceName;
99 private final String userName;
100 private final String destAbsPath;
101 private final Calendar updateDate;
102 private final boolean recursiveDown;
103
104 ChangeLastUpdateDateOp(String workspaceName, String userName, String destAbsPath, Calendar updateDate, boolean recursiveDown) {
105 this.workspaceName = workspaceName;
106 this.userName = userName;
107 this.destAbsPath = destAbsPath;
108 this.updateDate = updateDate;
109 this.recursiveDown = recursiveDown;
110 }
111
112 @Override
113 public void doExec() throws RepositoryException {
114 Session sysSession = MgnlContext.getJCRSession(workspaceName);
115
116
117 if (!sysSession.itemExists(destAbsPath)) {
118
119 if (log.isDebugEnabled()) {
120 log.warn("Can't update mgnl:lastModified. Path {}:{} doesn't exist anymore.", workspaceName, destAbsPath);
121 }
122 return;
123 }
124 Node node = resolveModifiedNode(workspaceName, destAbsPath, sysSession);
125
126
127 if (node == null) {
128 node = sysSession.getNode(destAbsPath);
129 }
130
131 if (node.isNodeType(NodeTypes.MetaData.NAME)) {
132
133 return;
134 }
135
136
137 if (node.getDepth() == 0) {
138 return;
139 }
140
141
142 if (node instanceof DelegateNodeWrapper) {
143 node = ((DelegateNodeWrapper) node).deepUnwrap(MgnlPropertySettingNodeWrapper.class);
144 }
145
146 log.debug("LUD on {} from {}:{}", node.getPath(), node.getSession().toString(), Thread.currentThread().getName());
147 if (node.isNodeType(LastModified.NAME)) {
148 LastModified.update(node, userName, updateDate);
149 }
150
151 if (recursiveDown) {
152
153 List<NodeIterator> iters = new ArrayList<NodeIterator>();
154 iters.add(node.getNodes());
155 while (!iters.isEmpty()) {
156 List<NodeIterator> tmp = updateChildren(iters, updateDate);
157 iters.clear();
158 iters.addAll(tmp);
159 }
160 }
161
162
163 isSysSessionDirty = true;
164 return;
165 }
166
167 private Node resolveModifiedNode(final String workspaceName, final String destAbsPath, Session sysSession) throws RepositoryException {
168 String parentNodeType = parentNodeMappings.get(workspaceName);
169 Node node = sysSession.getNode(destAbsPath);
170 if (parentNodeType == null) {
171 return node;
172 }
173 while (node != null && !parentNodeType.equals(node.getPrimaryNodeType().getName()) && node.getDepth() > 0) {
174 node = node.getParent();
175 }
176 return node;
177 }
178
179 private List<NodeIterator> updateChildren(List<NodeIterator> iters, Calendar updateDate) {
180 List<NodeIterator> tmp = new ArrayList<NodeIterator>();
181 for (NodeIterator iter : iters) {
182 while (iter.hasNext()) {
183 Node node = iter.nextNode();
184 try {
185 if (skipTypeInWorkspace(workspaceName, node.getPrimaryNodeType().getName())) {
186
187 continue;
188 }
189 if (node.isNodeType(NodeTypes.MetaData.NAME)) {
190
191 continue;
192 }
193 if (node.isNodeType(LastModified.NAME)) {
194 LastModified.update(node, userName, updateDate);
195 }
196 tmp.add(node.getNodes());
197 } catch (RepositoryException e) {
198 log.error("Failed to update last modified date of " + node + " with " + e.getMessage(), e);
199 }
200 }
201 }
202 return tmp;
203 }
204
205 private boolean skipTypeInWorkspace(String workspaceName, String name) {
206 if (RepositoryConstants.USER_ROLES.equals(workspaceName)) {
207 return !(NodeTypes.Role.NAME.equals(name) || NodeTypes.Folder.NAME.equals(name));
208 }
209 if (RepositoryConstants.WEBSITE.equals(workspaceName)) {
210 return !NodeTypes.Page.NAME.equals(name);
211 }
212 return false;
213 }
214 }
215
216
217
218
219 public class LastUpdatePropertyWrapper extends ContentDecoratorPropertyWrapper<MgnlPropertySettingContentDecorator> implements Property {
220
221 public LastUpdatePropertyWrapper(Property property, MgnlPropertySettingContentDecorator contentDecorator) {
222 super(property, contentDecorator);
223 }
224
225 @Override
226 public void setValue(BigDecimal value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
227 super.setValue(value);
228 this.updateLastModifiedProperty();
229 }
230
231 @Override
232 public void setValue(Binary value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
233 super.setValue(value);
234 this.updateLastModifiedProperty();
235 }
236
237 @Override
238 public void setValue(boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
239 super.setValue(value);
240 this.updateLastModifiedProperty();
241 }
242
243 @Override
244 public void setValue(Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
245 super.setValue(value);
246 this.updateLastModifiedProperty();
247 }
248
249 @Override
250 public void setValue(double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
251 super.setValue(value);
252 this.updateLastModifiedProperty();
253 }
254
255 @Override
256 public void setValue(InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
257 super.setValue(value);
258 this.updateLastModifiedProperty();
259 }
260
261 @Override
262 public void setValue(long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
263 super.setValue(value);
264 this.updateLastModifiedProperty();
265 }
266
267 @Override
268 public void setValue(Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
269 super.setValue(value);
270 this.updateLastModifiedProperty();
271 }
272
273 @Override
274 public void setValue(String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
275 super.setValue(value);
276 this.updateLastModifiedProperty();
277 }
278
279 @Override
280 public void setValue(String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
281 super.setValue(values);
282 this.updateLastModifiedProperty();
283 }
284
285 @Override
286 public void setValue(Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
287 super.setValue(value);
288 this.updateLastModifiedProperty();
289 }
290
291 @Override
292 public void setValue(Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
293 super.setValue(values);
294 this.updateLastModifiedProperty();
295 }
296
297 @Override
298 public void remove() throws VersionException, LockException, ConstraintViolationException, AccessDeniedException, RepositoryException {
299 Node parent = this.getParent();
300 String propertyName = this.getName();
301 super.remove();
302 if (shouldIgnoreUpdate(propertyName)) {
303 return;
304 }
305 updateLastModified(parent.getSession(), parent.getPath());
306 }
307
308 private void updateLastModifiedProperty() throws RepositoryException {
309 MgnlPropertySettingContentDecorator.this.updateLastModifiedProperty(getSession().getWorkspace().getName(), this.getName(), this.getParent().getPath());
310 }
311 }
312
313
314
315
316
317
318 public class LastUpdateWorkspaceWrapper extends MgnlPropertySettingWorkspaceWrapper {
319
320 protected LastUpdateWorkspaceWrapper(final Workspace workspace, final ContentDecorator contentDecorator) {
321 super(workspace, contentDecorator);
322 }
323 }
324
325
326
327
328 public class MgnlPropertySettingWorkspaceWrapper extends ContentDecoratorWorkspaceWrapper implements Workspace {
329
330 protected MgnlPropertySettingWorkspaceWrapper(final Workspace workspace, final ContentDecorator contentDecorator) {
331 super(workspace, contentDecorator);
332 }
333
334 @Override
335 public void move(String srcAbsPath, String destAbsPath) throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
336 super.move(srcAbsPath, destAbsPath);
337 updateLastModified(super.getWrappedWorkspace().getSession(), destAbsPath, true);
338 }
339
340 @Override
341 public void copy(String srcAbsPath, String destAbsPath) throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
342 super.copy(srcAbsPath, destAbsPath);
343 final String workspaceName = super.getWrappedWorkspace().getName();
344 setCreatedDate(workspaceName, destAbsPath, true);
345 }
346
347 @Override
348 public void copy(String srcWorkspace, String srcAbsPath, String destAbsPath) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
349 super.copy(srcWorkspace, srcAbsPath, destAbsPath);
350 final String workspaceName = super.getWrappedWorkspace().getName();
351 setCreatedDate(workspaceName, destAbsPath, true);
352 }
353
354 @Override
355 public void clone(String srcWorkspace, String srcAbsPath, String destAbsPath, boolean removeExisting) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
356 super.clone(srcWorkspace, srcAbsPath, destAbsPath, removeExisting);
357
358 }
359
360 }
361
362
363
364
365
366
367 public class LastUpdateSessionWrapper extends MgnlPropertySettingSessionWrapper {
368 public LastUpdateSessionWrapper(final Session session, final MgnlPropertySettingContentDecorator contentDecorator) {
369 super(session, contentDecorator);
370 }
371 }
372
373
374
375
376 public class MgnlPropertySettingSessionWrapper extends ContentDecoratorSessionWrapper<MgnlPropertySettingContentDecorator> implements Session {
377
378 public MgnlPropertySettingSessionWrapper(Session session, MgnlPropertySettingContentDecorator contentDecorator) {
379 super(session, contentDecorator);
380 }
381
382 @Override
383 public void move(String srcAbsPath, String destAbsPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException {
384 super.move(srcAbsPath, destAbsPath);
385
386 updateLastModified(super.getWrappedSession(), srcAbsPath, true);
387 }
388
389 @Override
390 public void save() throws AccessDeniedException, ItemExistsException, ReferentialIntegrityException, ConstraintViolationException, InvalidItemStateException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException {
391 String workspaceName = wrapped.getWorkspace().getName();
392 try {
393 log.debug("saving session: " + wrapped.toString() + "::" + workspaceName + "::sys:" + MgnlContext.isSystemInstance() + "::" + Thread.currentThread().getName());
394 super.save();
395 if (MgnlContext.isSystemInstance()) {
396 isSysSessionDirty = false;
397 }
398 } catch (InvalidItemStateException e) {
399 log.error("Failed to update LUD for session: " + wrapped.toString() + "::" + workspaceName, e);
400 throw e;
401 }
402 if (isSysSessionDirty) {
403 Session sysSession = MgnlContext.getSystemContext().getJCRSession(workspaceName);
404 if (sysSession instanceof DelegateSessionWrapper) {
405 sysSession = ((DelegateSessionWrapper) sysSession).deepUnwrap(LastUpdateSessionWrapper.class);
406 }
407 if (!wrapped.toString().equals(sysSession.toString())) {
408 log.debug("Forcing saving of : " + sysSession.toString() + "::" + workspaceName);
409 sysSession.save();
410 }
411 isSysSessionDirty = false;
412 }
413 }
414
415 }
416
417 @Override
418 public Session wrapSession(Session session) {
419 return new MgnlPropertySettingSessionWrapper(session, this);
420 }
421
422 @Override
423 public Workspace wrapWorkspace(Workspace workspace) {
424 return new MgnlPropertySettingWorkspaceWrapper(workspace, this);
425 }
426
427 @Override
428 public Node wrapNode(Node node) {
429 return new MgnlPropertySettingNodeWrapper(node, this);
430 }
431
432 @Override
433 public Property wrapProperty(Property property) {
434 return new LastUpdatePropertyWrapper(property, this);
435 }
436
437 void updateLastModified(final Session session, final String destAbsPath, final boolean recursiveDown) throws RepositoryException, PathNotFoundException {
438 this.updateLastModified(session.getWorkspace().getName(), destAbsPath, recursiveDown);
439 }
440
441 void updateLastModified(final String workspaceName, final String destAbsPath, final boolean recursiveDown) throws RepositoryException, PathNotFoundException {
442
443 if ("/".equals(destAbsPath) && !recursiveDown) {
444
445 return;
446 }
447
448 final Calendar updateDate = Calendar.getInstance();
449
450
451 MgnlContext.doInSystemContext(new ChangeLastUpdateDateOp(workspaceName, MgnlContext.getUser().getName(), destAbsPath, updateDate, recursiveDown));
452
453 }
454
455 void updateLastModified(String workspaceName, String destAbsPath) throws RepositoryException, PathNotFoundException {
456 updateLastModified(workspaceName, destAbsPath, false);
457 }
458
459 void updateLastModified(Session session, String destAbsPath) throws RepositoryException, PathNotFoundException {
460 updateLastModified(session, destAbsPath, false);
461 }
462
463 void updateLastModifiedProperty(String workspaceName, String propertyName, String parentPath) throws RepositoryException {
464 if (shouldIgnoreUpdate(propertyName)) {
465 return;
466 }
467 updateLastModified(workspaceName, parentPath);
468 }
469
470 void setCreatedDate(final String workspaceName, final String absPath) throws RepositoryException {
471 setCreatedDate(workspaceName, absPath, false);
472 }
473
474 void setCreatedDate(final String workspaceName, final String absPath, final boolean recursiveDown) throws RepositoryException {
475 final Session session = MgnlContext.getJCRSession(workspaceName);
476
477
478 if (!session.itemExists(absPath)) {
479
480 if (log.isDebugEnabled()) {
481 log.warn("Can't update {}. Path {}:{} does not exist.", NodeTypes.Created.NAME, workspaceName, absPath);
482 }
483 return;
484 }
485 Node node = session.getNode(absPath);
486
487
488 if (node.getDepth() == 0) {
489 return;
490 }
491
492 if (node == null) {
493 return;
494 }
495
496
497 if (node instanceof DelegateNodeWrapper) {
498 node = ((DelegateNodeWrapper) node).deepUnwrap(MgnlPropertySettingNodeWrapper.class);
499 }
500
501 final String user = MgnlContext.getUser().getName();
502
503
504 final Calendar now = Calendar.getInstance();
505
506 if (node.isNodeType(NodeTypes.Created.NAME)) {
507 log.debug("Setting {} on {} from {}:{}", NodeTypes.Created.NAME, node.getPath(), node.getSession().toString(), Thread.currentThread().getName());
508 NodeTypes.Created.set(node, user, now);
509 }
510
511 if (recursiveDown) {
512
513 List<NodeIterator> iters = new ArrayList<NodeIterator>();
514 iters.add(node.getNodes());
515 while (!iters.isEmpty()) {
516 List<NodeIterator> tmp = updateChildren(iters, user, now);
517 iters.clear();
518 iters.addAll(tmp);
519 }
520 }
521 }
522
523 private List<NodeIterator> updateChildren(final List<NodeIterator> iters, final String user, final Calendar updateDate) {
524 List<NodeIterator> tmp = new ArrayList<NodeIterator>();
525 for (NodeIterator iterator : iters) {
526 while (iterator.hasNext()) {
527 Node node = iterator.nextNode();
528 try {
529 if (node.isNodeType(NodeTypes.Created.NAME)) {
530 NodeTypes.Created.set(node, user, updateDate);
531 }
532 tmp.add(node.getNodes());
533 } catch (RepositoryException e) {
534 log.error("Failed to set created date of " + node + " with " + e.getMessage(), e);
535 }
536 }
537 }
538 return tmp;
539 }
540
541
542
543
544 protected boolean shouldIgnoreUpdate(final String propertyName) {
545
546 return propertyName.startsWith(NodeTypes.JCR_PREFIX) || (propertyName.startsWith(NodeTypes.MGNL_PREFIX) && !NodeTypes.Renderable.TEMPLATE.equals(propertyName));
547 }
548 }