View Javadoc

1   /**
2    * This file Copyright (c) 2003-2010 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.exchangesimple;
35  
36  import info.magnolia.cms.beans.config.ContentRepository;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.HierarchyManager;
39  import info.magnolia.cms.core.ItemType;
40  import info.magnolia.cms.core.MetaData;
41  import info.magnolia.cms.core.Path;
42  import info.magnolia.cms.core.SystemProperty;
43  import info.magnolia.cms.core.version.ContentVersion;
44  import info.magnolia.cms.exchange.ExchangeException;
45  import info.magnolia.cms.exchange.Subscriber;
46  import info.magnolia.cms.exchange.Subscription;
47  import info.magnolia.cms.exchange.Syndicator;
48  import info.magnolia.cms.security.AccessDeniedException;
49  import info.magnolia.cms.security.User;
50  import info.magnolia.cms.util.ContentUtil;
51  import info.magnolia.cms.util.Rule;
52  import info.magnolia.cms.util.RuleBasedContentFilter;
53  import info.magnolia.context.MgnlContext;
54  import info.magnolia.logging.AuditLoggingUtil;
55  
56  import java.io.File;
57  import java.io.FileInputStream;
58  import java.io.FileOutputStream;
59  import java.io.IOException;
60  import java.io.OutputStream;
61  import java.io.UnsupportedEncodingException;
62  import java.net.MalformedURLException;
63  import java.net.URL;
64  import java.net.URLConnection;
65  import java.net.URLEncoder;
66  import java.util.Iterator;
67  import java.util.List;
68  import java.util.zip.GZIPOutputStream;
69  
70  import javax.jcr.RepositoryException;
71  import javax.jcr.Session;
72  
73  import org.apache.commons.codec.binary.Base64;
74  import org.apache.commons.io.IOUtils;
75  import org.apache.commons.lang.StringUtils;
76  import org.apache.xml.serialize.OutputFormat;
77  import org.apache.xml.serialize.XMLSerializer;
78  import org.jdom.Document;
79  import org.jdom.Element;
80  import org.jdom.output.XMLOutputter;
81  import org.slf4j.Logger;
82  import org.slf4j.LoggerFactory;
83  import org.xml.sax.InputSource;
84  import org.xml.sax.SAXException;
85  import org.xml.sax.XMLReader;
86  import org.xml.sax.helpers.XMLReaderFactory;
87  
88  import EDU.oswego.cs.dl.util.concurrent.Sync;
89  
90  /**
91   * Default implementation of {@link Syndicator}. Activates all the content to a subscriber configured on the server.
92   * @author Sameer Charles
93   * $Id: $
94   */
95  public abstract class BaseSyndicatorImpl implements Syndicator {
96      private static final Logger log = LoggerFactory.getLogger(BaseSyndicatorImpl.class);
97  
98      /**
99       * URI used for activation.
100      */
101     public static final String DEFAULT_HANDLER = ".magnolia/activation"; //$NON-NLS-1$
102 
103     public static final String PARENT_PATH = "mgnlExchangeParentPath";
104 
105     public static final String MAPPED_PARENT_PATH = "mgnlExchangeMappedParent";
106 
107     /**
108      * Path to be activated or deactivated.
109      */
110     public static final String PATH = "mgnlExchangePath";
111 
112     public static final String NODE_UUID = "mgnlExchangeNodeUUID";
113 
114     public static final String REPOSITORY_NAME = "mgnlExchangeRepositoryName";
115 
116     public static final String WORKSPACE_NAME = "mgnlExchangeWorkspaceName";
117 
118     public static final String VERSION_NAME = "mgnlExchangeVersionName";
119 
120     /**
121      * Mane of the resource containing reading sequence for importing the data in activation target.
122      */
123     public static final String RESOURCE_MAPPING_FILE = "mgnlExchangeResourceMappingFile";
124 
125     /**
126      * Name of the element in the resource file describing siblings of activated node.
127      * Siblings element will contain all siblings of the same node type which are "before"
128      * this node.
129      */
130     public static final String SIBLINGS_ROOT_ELEMENT = "NodeSiblings";
131 
132     public static final String SIBLINGS_ELEMENT = "sibling";
133 
134     public static final String SIBLING_UUID = "siblingUUID";
135 
136     public static final String RESOURCE_MAPPING_FILE_ELEMENT = "File";
137 
138     public static final String RESOURCE_MAPPING_NAME_ATTRIBUTE = "name";
139 
140     public static final String RESOURCE_MAPPING_UUID_ATTRIBUTE = "contentUUID";
141 
142     public static final String RESOURCE_MAPPING_ID_ATTRIBUTE = "resourceId";
143 
144     public static final String RESOURCE_MAPPING_ROOT_ELEMENT = "Resources";
145 
146     public static final String ACTION = "mgnlExchangeAction";
147 
148     public static final String ACTIVATE = "activate"; //$NON-NLS-1$
149 
150     public static final String DEACTIVATE = "deactivate"; //$NON-NLS-1$
151 
152     public static final String COMMIT = "commit";
153 
154     public static final String ROLLBACK = "rollback";
155 
156     public static final String AUTHORIZATION = "Authorization";
157 
158     public static final String AUTH_CREDENTIALS= "mgnlUserPSWD";
159 
160     public static final String AUTH_USER = "mgnlUserId";
161 
162     public static final String CONTENT_FILTER_RULE = "mgnlExchangeFilterRule";
163 
164     public static final String ACTIVATION_SUCCESSFUL = "sa_success"; //$NON-NLS-1$
165 
166     public static final String ACTIVATION_FAILED = "sa_failed"; //$NON-NLS-1$
167 
168     public static final String ACTIVATION_ATTRIBUTE_STATUS = "sa_attribute_status"; //$NON-NLS-1$
169 
170     public static final String ACTIVATION_ATTRIBUTE_MESSAGE = "sa_attribute_message"; //$NON-NLS-1$
171 
172     public static final String ACTIVATION_ATTRIBUTE_VERSION = "sa_attribute_version"; //$NON-NLS-1$
173 
174     /**
175      * Runs a given job in the thread pool.
176      *
177      * @param job the job to run
178      * @throws ExchangeException if the job could not be put in the pool
179      */
180     protected static void executeInPool(Runnable job) throws ExchangeException {
181         try {
182             ThreadPool.getInstance().execute(job);
183         } catch (InterruptedException e) {
184             // this is kind of a problem, we could not add the job to the pool
185             // retrying might or might not work now that the interruption
186             // status is cleared but there is not much we can do so throwing
187             // an ExchangeException seems like the least bad choice
188             String message = "could not execute job in pool";
189             log.error(message, e);
190             throw new ExchangeException(message, e);
191         }
192     }
193 
194     /**
195      * Acquires a {@link Sync} ignoring any interruptions. Should any
196      * interruption occur the interruption status will be set. Might
197      * potentially block/wait forever.
198      *
199      * @see Sync#acquire()
200      *
201      * @param latch the latch on which to wait
202      */
203     protected static void acquireIgnoringInterruption(Sync latch) {
204         try {
205             latch.acquire();
206         } catch (InterruptedException e) {
207             // waken up externally - ignore try again
208             acquireIgnoringInterruption(latch);
209             // be a good citizen and set back the interruption status
210             Thread.currentThread().interrupt();
211         }
212     }
213 
214     protected String repositoryName;
215 
216     protected String workspaceName;
217 
218     protected String parent;
219 
220     protected Content.ContentFilter contentFilter;
221 
222     protected Rule contentFilterRule;
223 
224     protected User user;
225 
226     protected String basicCredentials;
227 
228     /**
229      * @param user
230      * @param repositoryName repository ID
231      * @param workspaceName workspace ID
232      * @param rule content filter rule
233      * @see info.magnolia.cms.exchange.Syndicator#init(info.magnolia.cms.security.User, String, String,
234      * info.magnolia.cms.util.Rule)
235      */
236     public void init(User user, String repositoryName, String workspaceName, Rule rule) {
237         this.user = user;
238         this.basicCredentials = "Basic "
239             + new String(Base64.encodeBase64((this.user.getName() + ":" + this.user.getPassword()).getBytes()));
240         this.contentFilter = new RuleBasedContentFilter(rule);
241         this.contentFilterRule = rule;
242         this.repositoryName = repositoryName;
243         this.workspaceName = workspaceName;
244     }
245 
246     /**
247      * This will activate specifies page (sub pages) to all configured subscribers.
248      *
249      * @param parent parent under which this page will be activated
250      * @param content to be activated
251      * @throws javax.jcr.RepositoryException
252      * @throws info.magnolia.cms.exchange.ExchangeException
253      */
254     public void activate(String parent, Content content) throws ExchangeException, RepositoryException {
255         this.activate(parent, content, null);
256     }
257 
258     /**
259      * This will activate specified node to all configured subscribers.
260      *
261      * @param parent parent under which this page will be activated
262      * @param content to be activated
263      * @param orderBefore List of UUID to be used by the implementation to order this node after activation
264      * @throws javax.jcr.RepositoryException
265      * @throws info.magnolia.cms.exchange.ExchangeException
266      *
267      */
268     public void activate(String parent, Content content, List<String> orderBefore) throws ExchangeException, RepositoryException {
269         this.activate(null, parent, content, orderBefore);
270     }
271 
272     /**
273      * This will activate specifies page (sub pages) to the specified subscriber.
274      *
275      * @param subscriber
276      * @param parent parent under which this page will be activated
277      * @param content to be activated
278      * @throws javax.jcr.RepositoryException
279      * @throws info.magnolia.cms.exchange.ExchangeException
280      */
281     public void activate(Subscriber subscriber, String parent, Content content) throws ExchangeException, RepositoryException {
282         this.activate(subscriber, parent, content, null);
283     }
284 
285     /**
286      * This will activate specifies node to the specified subscriber.
287      *
288      * @param subscriber
289      * @param parent      parent under which this page will be activated
290      * @param content     to be activated
291      * @param orderBefore List of UUID to be used by the subscriber to order this node after activation
292      * @throws javax.jcr.RepositoryException
293      * @throws info.magnolia.cms.exchange.ExchangeException
294      */
295     public void activate(Subscriber subscriber, String parent, Content content, List<String> orderBefore) throws ExchangeException, RepositoryException {
296         this.parent = parent;
297         String path = content.getHandle();
298         ActivationContent activationContent = null;
299         try {
300             activationContent = this.collect(content, orderBefore);
301             if (null == subscriber) {
302                 this.activate(activationContent, path);
303             } else {
304                 this.activate(subscriber, activationContent, path);
305             }
306             if (Boolean.parseBoolean(activationContent.getproperty(ItemType.DELETED_NODE_MIXIN))) {
307                 if (content instanceof ContentVersion) {
308                     // replace versioned content with the real node
309                     content = content.getHierarchyManager().getContentByUUID(content.getUUID());
310                 }
311                 Content parentContent = content.getParent();
312                 content.delete();
313                 parentContent.save();
314             } else {
315                 this.updateActivationDetails(path);
316             }
317             log.info("Exchange: activation succeeded [{}]", path);
318         } catch (Exception e) {
319             if (log.isDebugEnabled()) {
320                 log.error("Exchange: activation failed for path:" + ((path != null) ? path : "[null]"), e);
321                 long timestamp = System.currentTimeMillis();
322                 log.warn("moving files from failed activation to *.failed" + timestamp );
323                 Iterator<File> keys = activationContent.getFiles().values().iterator();
324                 while (keys.hasNext()) {
325                     File f = keys.next();
326                     f.renameTo(new File(f.getAbsolutePath()+".failed" + timestamp));
327                 }
328                 activationContent.getFiles().clear();
329 
330             }
331             throw new ExchangeException(e);
332         } finally {
333             log.debug("Cleaning temporary files");
334             cleanTemporaryStore(activationContent);
335         }
336     }
337 
338     /**
339      * @throws ExchangeException
340      */
341     public abstract void activate(ActivationContent activationContent, String nodePath) throws ExchangeException;
342 
343 
344     /**
345      * Send request of activation of activationContent to the subscriber. Subscriber might choose not to react if it is not subscribed to the URI under which activationContent exists.
346      */
347     public abstract String activate(Subscriber subscriber, ActivationContent activationContent, String nodePath) throws ExchangeException;
348 
349     /**
350      * Cleans up temporary file store after activation.
351      */
352     protected void cleanTemporaryStore(ActivationContent activationContent) {
353         if (activationContent == null) {
354             log.debug("Clean temporary store - nothing to do");
355             return;
356         }
357         if (log.isDebugEnabled()) {
358             log.debug("Debugging is enabled. Keeping temporary files in store for debugging purposes. Clean the store manually once done with debugging.");
359             return;
360         }
361 
362         Iterator<String> keys = activationContent.getFiles().keySet().iterator();
363         while (keys.hasNext()) {
364             String key = keys.next();
365             log.debug("Removing temporary file {}", key);
366             activationContent.getFile(key).delete();
367         }
368     }
369 
370     public synchronized void deactivate(String path) throws ExchangeException, RepositoryException {
371         final Content node = getHierarchyManager().getContent(path);
372         deactivate(node);
373     }
374 
375     /**
376      * @param node to deactivate
377      * @throws RepositoryException
378      * @throws ExchangeException
379      */
380     public synchronized void deactivate(Content node) throws ExchangeException, RepositoryException {
381         String nodeUUID = node.getUUID();
382         String path = node.getHandle();
383         this.doDeactivate(nodeUUID, path);
384         updateDeactivationDetails(nodeUUID);
385     }
386 
387     /**
388      * @param node , to deactivate
389      * @param subscriber
390      * @throws RepositoryException
391      * @throws ExchangeException
392      */
393     public synchronized void deactivate(Subscriber subscriber, Content node) throws ExchangeException, RepositoryException {
394         String nodeUUID = node.getUUID();
395         String path = node.getHandle();
396         this.doDeactivate(subscriber, nodeUUID, path);
397         updateDeactivationDetails(nodeUUID);
398     }
399 
400     /**
401      * @throws ExchangeException
402      */
403     public abstract void doDeactivate(String nodeUUID, String nodePath) throws ExchangeException;
404 
405     /**
406      * Deactivate content from specified subscriber.
407      * @param subscriber
408      * @throws ExchangeException
409      */
410     public abstract String doDeactivate(Subscriber subscriber, String nodeUUID, String nodePath) throws ExchangeException;
411 
412     /**
413      * Return URI set for deactivation.
414      * @param subscriberInfo
415      */
416     protected String getDeactivationURL(Subscriber subscriberInfo) {
417         return getActivationURL(subscriberInfo);
418     }
419 
420     /**
421      * Adds header fields describing deactivation request.
422      * @param connection
423      */
424     protected void addDeactivationHeaders(URLConnection connection, String nodeUUID) {
425         connection.addRequestProperty(REPOSITORY_NAME, this.repositoryName);
426         connection.addRequestProperty(WORKSPACE_NAME, this.workspaceName);
427         if (nodeUUID != null) {
428             connection.addRequestProperty(NODE_UUID, nodeUUID);
429         }
430         connection.addRequestProperty(ACTION, DEACTIVATE);
431     }
432 
433     /**
434      * Retrieves URL subscriber is listening on for (de)activation requests.
435      */
436     protected String getActivationURL(Subscriber subscriberInfo) {
437         final String url = subscriberInfo.getURL();
438         if (!url.endsWith("/")) {
439             return url + "/" + DEFAULT_HANDLER;
440         }
441         return url + DEFAULT_HANDLER;
442     }
443 
444     /**
445      * Adds headers fields describing activation request.
446      */
447     protected void addActivationHeaders(URLConnection connection, ActivationContent activationContent) {
448         Iterator<String> headerKeys = activationContent.getProperties().keySet().iterator();
449         while (headerKeys.hasNext()) {
450             String key = headerKeys.next();
451             String value = activationContent.getproperty(key);
452             if(SystemProperty.getBooleanProperty(SystemProperty.MAGNOLIA_UTF8_ENABLED)) {
453                 try {
454                     value = URLEncoder.encode(value, "UTF-8");
455                 }
456                 catch (UnsupportedEncodingException e) {
457                     // do nothing
458                 }
459             }
460             connection.setRequestProperty(key, value);
461         }
462     }
463 
464     /**
465      * Updates current content activation meta data with the time stamp and user details of the activation.
466      */
467     protected void updateActivationDetails(String path) throws RepositoryException {
468         // page activated already use system context to ensure meta data is activated even if activating user has no rights to the activated page children
469         Content page = getSystemHierarchyManager().getContent(path);
470         updateMetaData(page, ACTIVATE);
471         page.save();
472         AuditLoggingUtil.log(AuditLoggingUtil.ACTION_ACTIVATE, this.workspaceName, page.getItemType(), path );
473     }
474 
475     /**
476      * Updates current content activation meta data with the timestamp and user details of the deactivation.
477      */
478     protected void updateDeactivationDetails(String nodeUUID) throws RepositoryException {
479         // page deactivated already use system context to ensure meta data is activated even if activating user has no rights to the activated page children
480         Content page = getSystemHierarchyManager().getContentByUUID(nodeUUID);
481         updateMetaData(page, DEACTIVATE);
482         page.save();
483         AuditLoggingUtil.log(AuditLoggingUtil.ACTION_DEACTIVATE, this.workspaceName, page.getItemType(), page.getHandle() );
484     }
485 
486 
487     private HierarchyManager getHierarchyManager() {
488         return MgnlContext.getHierarchyManager(this.repositoryName, this.workspaceName);
489     }
490 
491     private HierarchyManager getSystemHierarchyManager() {
492         return MgnlContext.getSystemContext().getHierarchyManager(this.repositoryName, this.workspaceName);
493     }
494 
495     /**
496      * @param node
497      * @param type (activate / deactivate)
498      */
499     protected void updateMetaData(Content node, String type) throws AccessDeniedException {
500         // update the passed node
501         MetaData md = node.getMetaData();
502         if (type.equals(ACTIVATE)) {
503             md.setActivated();
504         }
505         else {
506             md.setUnActivated();
507         }
508         md.setActivatorId(this.user.getName());
509         md.setLastActivationActionDate();
510 
511         Iterator<Content> children;
512         if (type.equals(ACTIVATE)) {
513             // use syndicator rule based filter
514             children = node.getChildren(this.contentFilter).iterator();
515         }
516         else {
517             // all children
518             children = node.getChildren(ContentUtil.EXCLUDE_META_DATA_CONTENT_FILTER).iterator();
519         }
520 
521         while (children.hasNext()) {
522             Content child = children.next();
523             this.updateMetaData(child, type);
524         }
525 
526 
527     }
528 
529     /**
530      * Collects all information about activated content and its children (those that are set to be activated with the parent by filter rules).
531      * @throws Exception
532      */
533     protected ActivationContent collect(Content node, List<String> orderBefore) throws Exception {
534         // make sure resource file is unique
535         File resourceFile = File.createTempFile("resources", ".xml", Path.getTempDirectory());
536 
537         ActivationContent activationContent = new ActivationContent();
538         // add global properties true for this path/hierarchy
539         activationContent.addProperty(PARENT_PATH, this.parent);
540         activationContent.addProperty(WORKSPACE_NAME, this.workspaceName);
541         activationContent.addProperty(REPOSITORY_NAME, this.repositoryName);
542         activationContent.addProperty(RESOURCE_MAPPING_FILE, resourceFile.getName());//"resources.xml");
543         activationContent.addProperty(ACTION, ACTIVATE);
544         activationContent.addProperty(CONTENT_FILTER_RULE, this.contentFilterRule.toString());
545         activationContent.addProperty(NODE_UUID, node.getUUID());
546 
547 
548         Document document = new Document();
549         Element root = new Element(RESOURCE_MAPPING_ROOT_ELEMENT);
550         document.setRootElement(root);
551         // collect exact order of this node within its same nodeType siblings
552         addOrderingInfo(root, orderBefore);
553 
554         this.addResources(root, node.getWorkspace().getSession(), node, this.contentFilter, activationContent);
555         XMLOutputter outputter = new XMLOutputter();
556         outputter.output(document, new FileOutputStream(resourceFile));
557         // add resource file to the list
558         activationContent.addFile(resourceFile.getName(), resourceFile);
559 
560         // add deletion info
561         activationContent.addProperty(ItemType.DELETED_NODE_MIXIN, "" + node.hasMixin(ItemType.DELETED_NODE_MIXIN));
562 
563         return activationContent;
564     }
565 
566     /**
567      * Adds ordering information to the resource mapping file.
568      * @param root element of the resource file under which ordering info must be added
569      * @param orderBefore
570      */
571     protected void addOrderingInfo(Element root, List<String> orderBefore) {
572         //do not use magnolia Content class since these objects are only meant for a single use to read UUID
573         Element siblingRoot = new Element(SIBLINGS_ROOT_ELEMENT);
574         root.addContent(siblingRoot);
575         if (orderBefore == null) {
576             return;
577         }
578         Iterator<String> siblings = orderBefore.iterator();
579         while (siblings.hasNext()) {
580             String uuid = siblings.next();
581             Element e = new Element(SIBLINGS_ELEMENT);
582             e.setAttribute(SIBLING_UUID, uuid);
583             siblingRoot.addContent(e);
584         }
585     }
586 
587     protected void addResources(Element resourceElement, Session session, Content content, Content.ContentFilter filter, ActivationContent activationContent) throws IOException, RepositoryException, SAXException, Exception {
588         File file = File.createTempFile("exchange_" + content.getUUID(), ".xml.gz", Path.getTempDirectory());
589         GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new FileOutputStream(file));
590 
591         // TODO: remove the second check. It should not be necessary. The only safe way to identify the versioned node is by looking at its type since the type is mandated by spec. and the frozen nodes is what the filter below removes anyway
592         if (content.isNodeType("nt:frozenNode") || content.getWorkspace().getName().equals(ContentRepository.VERSION_STORE)) {
593             XMLReader elementfilter = new FrozenElementFilter(XMLReaderFactory
594                     .createXMLReader(org.apache.xerces.parsers.SAXParser.class.getName()));
595             ((FrozenElementFilter) elementfilter).setNodeName(content.getName());
596             /**
597              * nt:file node type has mandatory sub nodes
598              */
599             boolean noRecurse = !content.isNodeType(ItemType.NT_FILE);
600             exportAndParse(session, content, elementfilter, gzipOutputStream, noRecurse);
601         } else {
602             /**
603              * nt:file node type has mandatory sub nodes
604              */
605             if (content.isNodeType(ItemType.NT_FILE)) {
606                 session.exportSystemView(content.getJCRNode().getPath(), gzipOutputStream, false, false);
607             } else {
608                 session.exportSystemView(content.getJCRNode().getPath(), gzipOutputStream, false, true);
609             }
610         }
611 
612         IOUtils.closeQuietly(gzipOutputStream);
613         // add file entry in mapping.xml
614         Element element = new Element(RESOURCE_MAPPING_FILE_ELEMENT);
615         element.setAttribute(RESOURCE_MAPPING_NAME_ATTRIBUTE, content.getName());
616         element.setAttribute(RESOURCE_MAPPING_UUID_ATTRIBUTE, content.getUUID());
617         element.setAttribute(RESOURCE_MAPPING_ID_ATTRIBUTE, file.getName());
618         resourceElement.addContent(element);
619         // add this file element as resource in activation content
620         activationContent.addFile(file.getName(), file);
621 
622         Iterator<Content> children = content.getChildren(filter).iterator();
623         while (children.hasNext()) {
624             Content child = children.next();
625             this.addResources(element, session, child, filter, activationContent);
626         }
627     }
628 
629     protected void exportAndParse(Session session, Content content, XMLReader elementfilter, OutputStream os, boolean noRecurse) throws Exception {
630         File tempFile = File.createTempFile("Frozen_"+content.getName(), ".xml"); //$NON-NLS-1$ //$NON-NLS-2$
631         OutputStream tmpFileOutStream = null;
632         FileInputStream tmpFileInStream = null;
633         try {
634             tmpFileOutStream = new FileOutputStream(tempFile);
635             // has to get path via JCR node since if "content" is of type ContentVersion, getHandle() call would have returned path to the base
636             session.exportSystemView(content.getJCRNode().getPath(), tmpFileOutStream, false, noRecurse);
637             tmpFileOutStream.flush();
638             tmpFileOutStream.close();
639 
640             OutputFormat outputFormat = new OutputFormat();
641             outputFormat.setPreserveSpace(false);
642 
643             tmpFileInStream = new FileInputStream(tempFile);
644             elementfilter.setContentHandler(new XMLSerializer(os, outputFormat));
645             elementfilter.parse(new InputSource(tmpFileInStream));
646             tmpFileInStream.close();
647         } catch (Throwable t) {
648             log.error("Failed to parse XML using FrozenElementFilter",t);
649             throw new Exception(t);
650         } finally {
651             IOUtils.closeQuietly(tmpFileInStream);
652             IOUtils.closeQuietly(tmpFileOutStream);
653             tempFile.delete();
654         }
655     }
656 
657     /**
658      * Gets target path to which the current path is mapped in given subscription. Provided path should be without trailing slash.
659      */
660     protected String getMappedPath(String path, Subscription subscription) {
661         String toURI = subscription.getToURI();
662         if (null != toURI) {
663             String fromURI = subscription.getFromURI();
664             // remove trailing slash if any
665             fromURI = StringUtils.removeEnd(fromURI, "/");
666             toURI = StringUtils.removeEnd(toURI, "/");
667             // apply path transformation if any
668             path = path.replaceFirst(fromURI, toURI);
669             if (path.equals("")) {
670                 path = "/";
671             }
672         }
673 
674         if(SystemProperty.getBooleanProperty(SystemProperty.MAGNOLIA_UTF8_ENABLED)) {
675             try {
676                 path = URLEncoder.encode(path, "UTF-8");
677             }
678             catch (UnsupportedEncodingException e) {
679                 // do nothing
680             }
681         }
682         return path;
683     }
684 
685     protected URLConnection prepareConnection(Subscriber subscriber) throws ExchangeException {
686 
687         String handle = getActivationURL(subscriber);
688 
689         try {
690             String authMethod = subscriber.getAuthenticationMethod();
691             // authentication headers
692             if (authMethod != null && "form".equalsIgnoreCase(authMethod)) {
693                 handle += (handle.indexOf('?') > 0 ? "&" : "?") + AUTH_USER + "=" + this.user.getName();
694                 handle += "&" + AUTH_CREDENTIALS + "=" + this.user.getPassword();
695             }
696             URL url = new URL(handle);
697             URLConnection urlConnection = url.openConnection();
698             urlConnection.setConnectTimeout(subscriber.getConnectTimeout());
699             urlConnection.setReadTimeout(subscriber.getReadTimeout());
700             // authentication headers
701             if (authMethod == null || "basic".equalsIgnoreCase(authMethod)) {
702                 urlConnection.setRequestProperty(AUTHORIZATION, this.basicCredentials);
703             } else if (!"form".equalsIgnoreCase(subscriber.getAuthenticationMethod())) {
704                 log.info("Unknown Authentication method for deactivation: " + subscriber.getAuthenticationMethod());
705             }
706 
707             return urlConnection;
708         } catch (MalformedURLException e) {
709             throw new ExchangeException("Incorrect URL for subscriber " + subscriber + "[" + handle + "]");
710         } catch (IOException e) {
711             throw new ExchangeException("Not able to send the activation request [" + handle + "]: " + e.getMessage());
712         } catch (Exception e) {
713             throw new ExchangeException(e);
714         }
715     }
716 
717 
718 }