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.module.exchangesimple;
35
36 import info.magnolia.cms.beans.runtime.Document;
37 import info.magnolia.cms.beans.runtime.MultipartForm;
38 import info.magnolia.cms.core.Access;
39 import info.magnolia.cms.core.Content;
40 import info.magnolia.cms.core.HierarchyManager;
41 import info.magnolia.cms.core.ItemType;
42 import info.magnolia.cms.core.NodeData;
43 import info.magnolia.cms.core.SystemProperty;
44 import info.magnolia.cms.exchange.ExchangeException;
45 import info.magnolia.cms.filters.AbstractMgnlFilter;
46 import info.magnolia.cms.security.AccessDeniedException;
47 import info.magnolia.cms.security.Permission;
48 import info.magnolia.cms.util.ContentUtil;
49 import info.magnolia.cms.util.Rule;
50 import info.magnolia.cms.util.RuleBasedContentFilter;
51 import info.magnolia.context.MgnlContext;
52
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.security.InvalidParameterException;
56 import java.util.Iterator;
57 import java.util.List;
58 import java.util.zip.GZIPInputStream;
59
60 import javax.jcr.ImportUUIDBehavior;
61 import javax.jcr.ItemNotFoundException;
62 import javax.jcr.Node;
63 import javax.jcr.PathNotFoundException;
64 import javax.jcr.Property;
65 import javax.jcr.PropertyType;
66 import javax.jcr.RepositoryException;
67 import javax.jcr.UnsupportedRepositoryOperationException;
68 import javax.jcr.lock.LockException;
69 import javax.servlet.FilterChain;
70 import javax.servlet.ServletException;
71 import javax.servlet.http.HttpServletRequest;
72 import javax.servlet.http.HttpServletResponse;
73 import javax.servlet.http.HttpSession;
74
75 import org.apache.commons.codec.binary.Base64;
76 import org.apache.commons.io.IOUtils;
77 import org.apache.commons.lang.StringUtils;
78 import org.jdom.Element;
79 import org.jdom.JDOMException;
80 import org.jdom.input.SAXBuilder;
81 import org.safehaus.uuid.UUIDGenerator;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
84
85
86
87
88
89
90
91 public class ReceiveFilter extends AbstractMgnlFilter {
92
93 private static final Logger log = LoggerFactory.getLogger(ReceiveFilter.class);
94
95
96
97
98 @Deprecated
99 private static final String SIBLING_UUID_3_0 = "UUID";
100
101 public static final String SYSTEM_REPO = "mgnlSystem";
102
103 public static final String ROOT_LOCK_NAME = "rootLock";
104
105 private int unlockRetries = 10;
106
107 private int retryWait = 2;
108
109 public int getUnlockRetries() {
110 return unlockRetries;
111 }
112
113 public void setUnlockRetries(int unlockRetries) {
114 this.unlockRetries = unlockRetries;
115 }
116
117 public long getRetryWait() {
118 return retryWait;
119 }
120
121 public void setRetryWait(int retryWait) {
122 this.retryWait = retryWait;
123 }
124
125 @Override
126 public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
127 String statusMessage = "";
128 String status = "";
129 String result = null;
130
131 try {
132 final String utf8AuthorStatus = request.getHeader(BaseSyndicatorImpl.UTF8_STATUS);
133
134 if (utf8AuthorStatus != null && (Boolean.parseBoolean(utf8AuthorStatus) != SystemProperty.getBooleanProperty(SystemProperty.MAGNOLIA_UTF8_ENABLED))) {
135 throw new UnsupportedOperationException("Activation between instances with different UTF-8 setting is not supported.");
136 }
137 final String action = request.getHeader(BaseSyndicatorImpl.ACTION);
138 if (action == null) {
139 throw new InvalidParameterException("Activation action must be set for each activation request.");
140 }
141 applyLock(request);
142 } catch (ExchangeException e) {
143
144
145 log.debug(e.getMessage(), e);
146
147
148 statusMessage = StringUtils.defaultIfEmpty(e.getMessage(), e.getClass().getSimpleName());
149 status = BaseSyndicatorImpl.ACTIVATION_FAILED;
150 setResponseHeaders(response, statusMessage, status, result);
151 return;
152 } catch (Throwable e) {
153 log.error(e.getMessage(), e);
154
155 statusMessage = StringUtils.defaultIfEmpty(e.getMessage(), e.getClass().getSimpleName());
156 status = BaseSyndicatorImpl.ACTIVATION_FAILED;
157 setResponseHeaders(response, statusMessage, status, result);
158 return;
159 }
160 try {
161 result = receive(request);
162 status = BaseSyndicatorImpl.ACTIVATION_SUCCESSFUL;
163 }
164 catch (OutOfMemoryError e) {
165 Runtime rt = Runtime.getRuntime();
166 log.error("---------\nOutOfMemoryError caught during activation. Total memory = "
167 + rt.totalMemory()
168 + ", free memory = "
169 + rt.freeMemory()
170 + "\n---------");
171 statusMessage = e.getMessage();
172 status = BaseSyndicatorImpl.ACTIVATION_FAILED;
173 }
174 catch (PathNotFoundException e) {
175
176 log.error(e.getMessage(), e);
177 statusMessage = "Parent not found (not yet activated): " + e.getMessage();
178 status = BaseSyndicatorImpl.ACTIVATION_FAILED;
179 } catch (ExchangeException e) {
180 log.debug(e.getMessage(), e);
181 statusMessage = e.getMessage();
182 status = BaseSyndicatorImpl.ACTIVATION_FAILED;
183 } catch (Throwable e) {
184 log.error(e.getMessage(), e);
185
186 statusMessage = StringUtils.defaultIfEmpty(e.getMessage(), e.getClass().getSimpleName());
187 status = BaseSyndicatorImpl.ACTIVATION_FAILED;
188 }
189 finally {
190 cleanUp(request, status);
191 setResponseHeaders(response, statusMessage, status, result);
192 }
193 }
194
195 protected void setResponseHeaders(HttpServletResponse response, String statusMessage, String status, String result) {
196 response.setHeader(BaseSyndicatorImpl.ACTIVATION_ATTRIBUTE_STATUS, status);
197 response.setHeader(BaseSyndicatorImpl.ACTIVATION_ATTRIBUTE_MESSAGE, statusMessage);
198 }
199
200
201
202
203
204
205 protected synchronized String receive(HttpServletRequest request) throws Exception {
206 String action = request.getHeader(BaseSyndicatorImpl.ACTION);
207 log.debug("action: " + action);
208 String authorization = getUser(request);
209 String webapp = getWebappName();
210
211 if (action.equalsIgnoreCase(BaseSyndicatorImpl.ACTIVATE)) {
212 String name = update(request);
213
214 log.info("User {} successfuly activated {} on {}.", new Object[]{authorization, name, webapp});
215 }
216 else if (action.equalsIgnoreCase(BaseSyndicatorImpl.DEACTIVATE)) {
217 String name = remove(request);
218
219 log.info("User {} succeessfuly deactivated {} on {}.", new Object[] {authorization, name, webapp});
220 }
221 else {
222 throw new UnsupportedOperationException("Method not supported : " + action);
223 }
224 return null;
225 }
226
227 protected String getWebappName() {
228 return SystemProperty.getProperty(SystemProperty.MAGNOLIA_WEBAPP);
229 }
230
231 protected String getUser(HttpServletRequest request) {
232
233 String user = request.getHeader(BaseSyndicatorImpl.AUTHORIZATION);
234 if (StringUtils.isEmpty(user)) {
235 user = request.getParameter(BaseSyndicatorImpl.AUTH_USER);
236 } else {
237 user = new String(Base64.decodeBase64(user.substring(6).getBytes()));
238 user = user.substring(0, user.indexOf(":"));
239 }
240 return user;
241 }
242
243
244
245
246
247
248 protected synchronized String update(HttpServletRequest request) throws Exception {
249 MultipartForm data = MgnlContext.getPostedForm();
250 if (null != data) {
251 String newParentPath = this.getParentPath(request);
252 String resourceFileName = request.getHeader(BaseSyndicatorImpl.RESOURCE_MAPPING_FILE);
253 HierarchyManager hm = getHierarchyManager(request);
254 Element rootElement = getImportedContentRoot(data, resourceFileName);
255 Element topContentElement = rootElement.getChild(BaseSyndicatorImpl.RESOURCE_MAPPING_FILE_ELEMENT);
256 Content content = null;
257 try {
258 String uuid = topContentElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_UUID_ATTRIBUTE);
259 content = hm.getContentByUUID(uuid);
260
261 newParentPath = handleMovedContent(newParentPath, hm, topContentElement, content);
262 handleChildren(request, content);
263 this.importOnExisting(topContentElement, data, hm, content);
264 }
265 catch (ItemNotFoundException e) {
266
267 importFresh(topContentElement, data, hm, newParentPath);
268 }
269
270 return orderImportedNode(newParentPath, hm, rootElement, topContentElement);
271 }
272 return null;
273 }
274
275 protected Element getImportedContentRoot(MultipartForm data, String resourceFileName) throws JDOMException, IOException {
276 Document resourceDocument = data.getDocument(resourceFileName);
277 SAXBuilder builder = new SAXBuilder();
278 InputStream documentInputStream = resourceDocument.getStream();
279 org.jdom.Document jdomDocument = builder.build(documentInputStream);
280 IOUtils.closeQuietly(documentInputStream);
281 return jdomDocument.getRootElement();
282 }
283
284 protected void handleChildren(HttpServletRequest request, Content content) {
285 String ruleString = request.getHeader(BaseSyndicatorImpl.CONTENT_FILTER_RULE);
286 Rule rule = new Rule(ruleString, ",");
287 RuleBasedContentFilter filter = new RuleBasedContentFilter(rule);
288
289 this.removeChildren(content, filter);
290 }
291
292 protected String handleMovedContent(String newParentPath,
293 HierarchyManager hm, Element topContentElement, Content content)
294 throws RepositoryException {
295 String currentParentPath = content.getHandle();
296 currentParentPath = currentParentPath.substring(0, currentParentPath.lastIndexOf('/'));
297 String newName = topContentElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_NAME_ATTRIBUTE);
298 if (!newParentPath.endsWith("/")) {
299 newParentPath += "/";
300 }
301 if (!currentParentPath.endsWith("/")) {
302 currentParentPath += "/";
303 }
304 if (!newParentPath.equals(currentParentPath) || !content.getName().equals(newName)) {
305 log.info("Moving content from {} to {} due to activation request.", new Object[] { content.getHandle(), newParentPath + newName});
306 hm.moveTo(content.getHandle(), newParentPath + newName);
307 }
308 return newParentPath;
309 }
310
311 protected String orderImportedNode(String newParentPath, HierarchyManager hm, Element rootElement, Element topContentElement) throws RepositoryException {
312 String name;
313
314 name = topContentElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_NAME_ATTRIBUTE);
315 Content parent = hm.getContent(newParentPath);
316 List siblings = rootElement.getChild(BaseSyndicatorImpl.SIBLINGS_ROOT_ELEMENT).getChildren(BaseSyndicatorImpl.SIBLINGS_ELEMENT);
317 Iterator siblingsIterator = siblings.iterator();
318 while (siblingsIterator.hasNext()) {
319 Element sibling = (Element) siblingsIterator.next();
320
321 String siblingUUID = sibling.getAttributeValue(BaseSyndicatorImpl.SIBLING_UUID);
322 try {
323
324 if (StringUtils.isEmpty(siblingUUID)) {
325 log.debug("Activating from a Magnolia 3.0 instance");
326 siblingUUID = sibling.getAttributeValue(SIBLING_UUID_3_0);
327 }
328 Content beforeContent = hm.getContentByUUID(siblingUUID);
329 log.debug("Ordering {} before {}", name, beforeContent.getName());
330 order(parent, name, beforeContent.getName());
331 break;
332 } catch (AccessDeniedException e) {
333 log.info("User doesn't have enough rights to force order of nodes on {}", siblingUUID);
334 } catch (ItemNotFoundException e) {
335
336 } catch (RepositoryException re) {
337 log.warn("Failed to order node");
338 log.debug("Failed to order node", re);
339 }
340 }
341
342
343 if (siblings.isEmpty()) {
344 order(parent, name, null);
345 }
346 return name;
347 }
348
349
350 protected void order(Content parent, String name, String orderBefore) throws RepositoryException {
351 try {
352 parent.orderBefore(name, orderBefore);
353 } catch (UnsupportedRepositoryOperationException e) {
354
355 log.warn("Failed to order unorderable content {} at {} due to {}", new Object[] {name, parent.getHandle(), e.getMessage()});
356 }
357 parent.save();
358 }
359
360
361
362
363
364
365 protected synchronized void copyProperties(Content source, Content destination) throws RepositoryException {
366
367
368 Iterator nodeDataIterator = destination.getNodeDataCollection().iterator();
369 while (nodeDataIterator.hasNext()) {
370 NodeData nodeData = (NodeData) nodeDataIterator.next();
371
372
373 if (nodeData.getType() != PropertyType.BINARY) {
374 nodeData.delete();
375 }
376 }
377
378
379 Node destinationNode = destination.getJCRNode();
380 nodeDataIterator = source.getNodeDataCollection().iterator();
381 while (nodeDataIterator.hasNext()) {
382 NodeData nodeData = (NodeData) nodeDataIterator.next();
383 Property property = nodeData.getJCRProperty();
384 if (property.getDefinition().isMultiple()) {
385 if (destination.isGranted(Permission.WRITE)) {
386 destinationNode.setProperty(nodeData.getName(), property.getValues());
387 }
388 else {
389 throw new AccessDeniedException("User not allowed to " + Permission.PERMISSION_NAME_WRITE + " at [" + nodeData.getHandle() + "]");
390 }
391 }
392 else {
393 destination.createNodeData(nodeData.getName(), nodeData.getValue());
394 }
395 }
396 }
397
398
399
400
401
402
403 protected synchronized void removeChildren(Content content, Content.ContentFilter filter) {
404 Iterator children = content.getChildren(filter).iterator();
405
406
407 while (children.hasNext()) {
408 Content child = (Content) children.next();
409 try {
410 child.delete();
411 }
412 catch (Exception e) {
413 log.error("Failed to remove " + child.getHandle() + " | " + e.getMessage());
414 }
415 }
416 }
417
418
419
420
421
422
423
424
425
426
427 protected synchronized void importFresh(Element topContentElement, MultipartForm data, HierarchyManager hierarchyManager, String parentPath) throws ExchangeException, RepositoryException {
428
429
430 String path = parentPath + (parentPath.endsWith("/") ? "" : "/") + topContentElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_NAME_ATTRIBUTE);
431 if (hierarchyManager.isExist(path)) {
432 log.warn("Replacing {} due to name collision (but different UUIDs.). This operation could not be rolled back in case of failure and you need to reactivate the page manually.", path);
433 hierarchyManager.delete(path);
434 }
435 try {
436 importResource(data, topContentElement, hierarchyManager, parentPath);
437 hierarchyManager.save();
438 } catch (PathNotFoundException e) {
439 final String message = "Parent content " + parentPath + " is not yet activated or you do not have write access to it. Please activate the parent content before activating children and ensure you have appropriate rights";
440
441 log.debug(message, e);
442 hierarchyManager.refresh(false);
443 throw new ExchangeException(message);
444 } catch (Exception e) {
445 final String message = "Activation failed | " + e.getMessage();
446 log.error("Exception caught", e);
447 hierarchyManager.refresh(false);
448 throw new ExchangeException(message);
449 }
450 }
451
452
453
454
455
456
457
458
459
460
461 protected synchronized void importOnExisting(Element topContentElement, MultipartForm data,
462 final HierarchyManager hierarchyManager, Content existingContent) throws ExchangeException, RepositoryException {
463 final Iterator<Content> fileListIterator = topContentElement.getChildren(BaseSyndicatorImpl.RESOURCE_MAPPING_FILE_ELEMENT).iterator();
464 final String uuid = UUIDGenerator.getInstance().generateTimeBasedUUID().toString();
465 final String handle = existingContent.getHandle();
466
467 final HierarchyManager systemHM = MgnlContext.getSystemContext().getHierarchyManager(SYSTEM_REPO);
468 try {
469 while (fileListIterator.hasNext()) {
470 Element fileElement = (Element) fileListIterator.next();
471 importResource(data, fileElement, hierarchyManager, handle);
472 }
473
474 Content activationTmp = ContentUtil.getOrCreateContent(systemHM.getRoot(), "activation-tmp", ItemType.FOLDER, true);
475 final Content transientNode = activationTmp.createContent(uuid, ItemType.CONTENTNODE.toString());
476 systemHM.save();
477 final String transientStoreHandle = transientNode.getHandle();
478
479 final String fileName = topContentElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_ID_ATTRIBUTE);
480 final GZIPInputStream inputStream = new GZIPInputStream(data.getDocument(fileName).getStream());
481
482
483
484
485 systemHM.getWorkspace().getSession().importXML(transientStoreHandle, inputStream, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
486 IOUtils.closeQuietly(inputStream);
487
488 Content tmpContent = transientNode.getContent(topContentElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_NAME_ATTRIBUTE));
489 copyProperties(tmpContent, existingContent);
490 systemHM.delete(transientStoreHandle);
491 hierarchyManager.save();
492 systemHM.save();
493 } catch (Exception e) {
494
495 hierarchyManager.refresh(false);
496 systemHM.refresh(false);
497
498 log.error("Exception caught", e);
499 throw new ExchangeException("Activation failed : " + e.getMessage());
500 }
501 }
502
503
504
505
506
507
508
509
510
511 protected synchronized void importResource(MultipartForm data, Element resourceElement, HierarchyManager hm, String parentPath) throws Exception {
512
513
514 Access.isGranted(hm.getAccessManager(), parentPath, Permission.WRITE);
515
516 final String name = resourceElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_NAME_ATTRIBUTE);
517 final String fileName = resourceElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_ID_ATTRIBUTE);
518
519 final GZIPInputStream inputStream = new GZIPInputStream(data.getDocument(fileName).getStream());
520 log.debug("Importing {} into parent path {}", new Object[] {name, parentPath});
521 hm.getWorkspace().getSession().importXML(parentPath, inputStream, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING);
522 IOUtils.closeQuietly(inputStream);
523 Iterator fileListIterator = resourceElement.getChildren(BaseSyndicatorImpl.RESOURCE_MAPPING_FILE_ELEMENT).iterator();
524
525 try {
526 parentPath = hm.getContentByUUID(resourceElement.getAttributeValue(BaseSyndicatorImpl.RESOURCE_MAPPING_UUID_ATTRIBUTE)).getHandle();
527 } catch (ItemNotFoundException e) {
528
529
530 parentPath = StringUtils.removeEnd(parentPath, "/") + "/" + name;
531 }
532 while (fileListIterator.hasNext()) {
533 Element fileElement = (Element) fileListIterator.next();
534 importResource(data, fileElement, hm, parentPath);
535 }
536 }
537
538
539
540
541
542
543 protected synchronized String remove(HttpServletRequest request) throws Exception {
544 HierarchyManager hm = getHierarchyManager(request);
545 String handle = null;
546 try {
547 Content node = this.getNode(request);
548 handle = node.getHandle();
549 hm.delete(handle);
550 hm.save();
551 } catch (ItemNotFoundException e) {
552 log.debug("Unable to delete node", e);
553 }
554 return handle;
555 }
556
557
558
559
560
561
562 protected HierarchyManager getHierarchyManager(HttpServletRequest request) throws ExchangeException {
563 String repositoryName = request.getHeader(BaseSyndicatorImpl.REPOSITORY_NAME);
564 String workspaceName = request.getHeader(BaseSyndicatorImpl.WORKSPACE_NAME);
565
566 if (StringUtils.isEmpty(repositoryName) || StringUtils.isEmpty(workspaceName)) {
567 throw new ExchangeException("Repository or workspace name not sent, unable to activate. Repository: " + repositoryName + ", workspace: " + workspaceName) ;
568 }
569
570 return MgnlContext.getHierarchyManager(repositoryName, workspaceName);
571 }
572
573
574
575
576
577
578
579 protected void cleanUp(HttpServletRequest request, String status) {
580 if (!BaseSyndicatorImpl.DEACTIVATE.equalsIgnoreCase(request.getHeader(BaseSyndicatorImpl.ACTION))) {
581 MultipartForm data = MgnlContext.getPostedForm();
582 if (null != data) {
583 Iterator keys = data.getDocuments().keySet().iterator();
584 while (keys.hasNext()) {
585 String key = (String) keys.next();
586 data.getDocument(key).delete();
587 }
588 }
589 releaseLock(request);
590 }
591
592
593 try {
594 HttpSession httpSession = request.getSession(false);
595 if (httpSession != null) {
596 httpSession.invalidate();
597 }
598 } catch (Throwable t) {
599
600 log.error("failed to invalidate session", t);
601 }
602 }
603
604 protected void releaseLock(HttpServletRequest request) {
605 try {
606 final String parentPath = getParentPath(request);
607 Content content = null;
608
609 if (StringUtils.isEmpty(parentPath) || this.getHierarchyManager(request).isExist(parentPath)) {
610 try {
611 content = this.getNode(request);
612 } catch (ItemNotFoundException e) {
613
614 }
615 unlock(content);
616 }
617
618
619 Content item = null;
620 try {
621 final String uuid = this.getUUID(request);
622 if (StringUtils.isNotBlank(uuid)) {
623 item = this.getHierarchyManager(request).getContentByUUID(uuid);
624 }
625 } catch (ItemNotFoundException e) {
626
627 } catch (PathNotFoundException e) {
628
629 }
630 unlock(item);
631
632
633 Content rootLock = getRootLockOrNull(content);
634 unlock(rootLock);
635 } catch (LockException le) {
636
637 log.warn(le.getMessage());
638 } catch (RepositoryException re) {
639
640 log.warn("Exception caught", re);
641 } catch (ExchangeException e) {
642
643 log.warn("Exception caught", e);
644 }
645 }
646
647 private void unlock(Content content) throws RepositoryException, LockException {
648 if (content != null && content.isLocked()) {
649 content.unlock();
650 logLockStatus(content, false);
651 }
652 }
653
654
655
656
657
658 protected void applyLock(HttpServletRequest request) throws ExchangeException {
659 Content node = null;
660 try{
661 node = getHierarchyManager(request).getContentByUUID(getUUID(request));
662 } catch (ItemNotFoundException e) {
663
664 } catch (RepositoryException e) {
665 throw new ExchangeException(e.getMessage(), e);
666 }
667 try {
668 Content parent = waitForLock(request);
669 lock(node, parent);
670 } catch (RepositoryException re) {
671
672 log.warn("Exception caught", re);
673 }
674 }
675
676
677
678
679
680
681
682
683
684 protected Content waitForLock(HttpServletRequest request) throws ExchangeException, RepositoryException {
685 int retries = getUnlockRetries();
686 long retryWait = getRetryWait() * 1000;
687 Content parentOnActivationNodeOtherwise = null;
688 Content rootLock = null;
689 Content activatedNode;
690 try {
691 activatedNode = this.getHierarchyManager(request).getContentByUUID(this.getUUID(request));
692 } catch (RepositoryException e) {
693
694 activatedNode = null;
695 }
696 try {
697 parentOnActivationNodeOtherwise = this.getNode(request);
698 rootLock = getRootLockOrNull(parentOnActivationNodeOtherwise);
699
700 while (isLocked(parentOnActivationNodeOtherwise, rootLock, activatedNode) && retries > 0) {
701 log.info("Content " + parentOnActivationNodeOtherwise.getHandle() + " is locked. Will retry " + retries + " more times.");
702 try {
703 Thread.sleep(retryWait);
704 } catch (InterruptedException e) {
705
706 Thread.currentThread().interrupt();
707 }
708 retries--;
709 parentOnActivationNodeOtherwise = this.getNode(request);
710 rootLock = getRootLockOrNull(parentOnActivationNodeOtherwise);
711 }
712
713 if (isLocked(parentOnActivationNodeOtherwise, rootLock, activatedNode)) {
714 throw new ExchangeException("Content " + parentOnActivationNodeOtherwise.getHandle() + " is locked while activating " + request.getHeader(BaseSyndicatorImpl.NODE_UUID)
715 + ". This most likely means that content have been at the same time activated by some other user. Please try again and if problem persists contact administrator.");
716 }
717 } catch (ItemNotFoundException e) {
718
719 log.debug("Attempt to lock non existing content {} during (de)activation.", getUUID(request));
720 } catch (PathNotFoundException e) {
721
722 log.debug("Attempt to lock non existing content {}:{} during (de)activation.",getHierarchyManager(request).getName(), getParentPath(request));
723 }
724 return parentOnActivationNodeOtherwise;
725 }
726
727 private boolean isLocked(Content parentContent, Content rootLock, Content activatedNode) throws RepositoryException {
728 return (rootLock != null && rootLock.isLocked()) || parentContent.isLocked() || (activatedNode != null && activatedNode.isLocked());
729 }
730
731 private Content getRootLockOrNull(Content content) throws ExchangeException {
732 try {
733 if (content != null && "/".equals(content.getHandle())) {
734 return ContentUtil.getOrCreateContent(getBackupHierarchyManager().getRoot(), content.getHierarchyManager().getName() + "-" + ROOT_LOCK_NAME, ItemType.CONTENT, true);
735 }
736 } catch (RepositoryException e) {
737 throw new ExchangeException("Failed to obtain root lock.", e);
738 }
739 return null;
740 }
741
742 protected HierarchyManager getBackupHierarchyManager() {
743 return MgnlContext.getSystemContext().getHierarchyManager(SYSTEM_REPO);
744 }
745
746
747
748
749
750 protected Content getNode(HttpServletRequest request) throws ExchangeException, RepositoryException {
751 if (request.getHeader(BaseSyndicatorImpl.PARENT_PATH) != null) {
752 String parentPath = this.getParentPath(request);
753 log.debug("parent path:" + parentPath);
754 return this.getHierarchyManager(request).getContent(parentPath);
755 } else if (!StringUtils.isEmpty(getUUID(request))){
756 final String uuid = getUUID(request);
757 log.debug("node uuid:" + uuid);
758 return this.getHierarchyManager(request).getContentByUUID(uuid);
759 }
760
761 else {
762 log.debug("path: {}", request.getHeader(BaseSyndicatorImpl.PATH));
763 return this.getHierarchyManager(request).getContent(request.getHeader(BaseSyndicatorImpl.PATH));
764 }
765 }
766
767 protected String getParentPath(HttpServletRequest request) {
768 String parentPath = request.getHeader(BaseSyndicatorImpl.PARENT_PATH);
769 if (StringUtils.isNotEmpty(parentPath)) {
770 return parentPath;
771 }
772 return "";
773 }
774
775 protected String getUUID(HttpServletRequest request) {
776 final String uuid = request.getHeader(BaseSyndicatorImpl.NODE_UUID);
777 if (StringUtils.isNotEmpty(uuid)) {
778 return uuid;
779 }
780 return "";
781 }
782
783
784
785
786
787
788
789
790
791
792
793 protected void lock(Content node, Content parent) throws ExchangeException, RepositoryException {
794 try {
795 if (parent != null) {
796 if (parent.getHandle().equals("/")) {
797
798 Content rootLock = getRootLockOrNull(parent);
799 rootLock.lock(false, true);
800 logLockStatus(rootLock, true);
801
802 if (node != null) {
803 node.lock(true, true);
804 logLockStatus(node, true);
805 }
806 } else {
807 parent.lock(true, true);
808 logLockStatus(parent, true);
809 }
810 }
811 } catch (LockException le) {
812
813 log.error(le.getMessage(), le);
814 }
815
816 }
817
818 private void logLockStatus(Content content, boolean isLock) throws RepositoryException {
819 if (log.isDebugEnabled()) {
820
821 log.debug("{} {} {}locked {}:{}", new Object[] { content.getWorkspace().getSession(), isLock ^ content.isLocked() ? "DIDN'T" : "DID", isLock ? "" : "un", content.getWorkspace().getName(), content.getHandle() });
822 }
823 }
824
825
826 }