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.exchange.ExchangeException;
44  import info.magnolia.cms.exchange.Subscriber;
45  import info.magnolia.cms.exchange.Syndicator;
46  import info.magnolia.cms.security.AccessDeniedException;
47  import info.magnolia.cms.security.User;
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  import info.magnolia.logging.AuditLoggingUtil;
53  
54  import java.io.File;
55  import java.io.FileInputStream;
56  import java.io.FileOutputStream;
57  import java.io.IOException;
58  import java.io.OutputStream;
59  import java.io.UnsupportedEncodingException;
60  import java.net.URLConnection;
61  import java.net.URLEncoder;
62  import java.util.Iterator;
63  import java.util.List;
64  import java.util.zip.GZIPOutputStream;
65  
66  import javax.jcr.RepositoryException;
67  import javax.jcr.Session;
68  
69  import org.apache.commons.codec.binary.Base64;
70  import org.apache.commons.io.IOUtils;
71  import org.apache.xml.serialize.OutputFormat;
72  import org.apache.xml.serialize.XMLSerializer;
73  import org.jdom.Document;
74  import org.jdom.Element;
75  import org.jdom.output.XMLOutputter;
76  import org.slf4j.Logger;
77  import org.slf4j.LoggerFactory;
78  import org.xml.sax.InputSource;
79  import org.xml.sax.SAXException;
80  import org.xml.sax.XMLReader;
81  import org.xml.sax.helpers.XMLReaderFactory;
82  
83  import EDU.oswego.cs.dl.util.concurrent.Sync;
84  
85  /**
86   * Default implementation of {@link Syndicator}. Activates all the content to a subscriber configured on the server.
87   * @author Sameer Charles
88   * $Id: $
89   */
90  public abstract class BaseSyndicatorImpl implements Syndicator {
91       private static final Logger log = LoggerFactory.getLogger(BaseSyndicatorImpl.class);
92  
93      /**
94        * URI used for activation.
95        */
96       public static final String DEFAULT_HANDLER = ".magnolia/activation"; //$NON-NLS-1$
97  
98       public static final String PARENT_PATH = "mgnlExchangeParentPath";
99  
100      public static final String MAPPED_PARENT_PATH = "mgnlExchangeMappedParent";
101 
102      /**
103       * Path to be activated or deactivated.
104       */
105      public static final String PATH = "mgnlExchangePath";
106 
107      public static final String NODE_UUID = "mgnlExchangeNodeUUID";
108 
109      public static final String REPOSITORY_NAME = "mgnlExchangeRepositoryName";
110 
111      public static final String WORKSPACE_NAME = "mgnlExchangeWorkspaceName";
112 
113      public static final String VERSION_NAME = "mgnlExchangeVersionName";
114 
115      /**
116       * Mane of the resource containing reading sequence for importing the data in activation target.
117       */
118      public static final String RESOURCE_MAPPING_FILE = "mgnlExchangeResourceMappingFile";
119 
120      /**
121       * Name of the element in the resource file describing siblings of activated node.
122       * Siblings element will contain all siblings of the same node type which are "before"
123       * this node.
124       */
125      public static final String SIBLINGS_ROOT_ELEMENT = "NodeSiblings";
126 
127      public static final String SIBLINGS_ELEMENT = "sibling";
128 
129      public static final String SIBLING_UUID = "siblingUUID";
130 
131      public static final String RESOURCE_MAPPING_FILE_ELEMENT = "File";
132 
133      public static final String RESOURCE_MAPPING_NAME_ATTRIBUTE = "name";
134 
135      public static final String RESOURCE_MAPPING_UUID_ATTRIBUTE = "contentUUID";
136 
137      public static final String RESOURCE_MAPPING_ID_ATTRIBUTE = "resourceId";
138 
139      public static final String RESOURCE_MAPPING_ROOT_ELEMENT = "Resources";
140 
141      public static final String ACTION = "mgnlExchangeAction";
142 
143      public static final String ACTIVATE = "activate"; //$NON-NLS-1$
144 
145      public static final String DEACTIVATE = "deactivate"; //$NON-NLS-1$
146 
147      public static final String COMMIT = "commit";
148 
149      public static final String ROLLBACK = "rollback";
150 
151      public static final String AUTHORIZATION = "Authorization";
152 
153      public static final String AUTH_CREDENTIALS= "mgnlUserPSWD";
154 
155      public static final String AUTH_USER = "mgnlUserId";
156 
157      public static final String CONTENT_FILTER_RULE = "mgnlExchangeFilterRule";
158 
159      public static final String ACTIVATION_SUCCESSFUL = "sa_success"; //$NON-NLS-1$
160 
161      public static final String ACTIVATION_FAILED = "sa_failed"; //$NON-NLS-1$
162 
163      public static final String ACTIVATION_ATTRIBUTE_STATUS = "sa_attribute_status"; //$NON-NLS-1$
164 
165      public static final String ACTIVATION_ATTRIBUTE_MESSAGE = "sa_attribute_message"; //$NON-NLS-1$
166 
167      public static final String ACTIVATION_ATTRIBUTE_VERSION = "sa_attribute_version"; //$NON-NLS-1$
168 
169      /**
170      * Runs a given job in the thread pool.
171      *
172      * @param job the job to run
173      * @throws ExchangeException if the job could not be put in the pool
174      */
175     protected static void executeInPool(Runnable job) throws ExchangeException {
176         try {
177             ThreadPool.getInstance().execute(job);
178         } catch (InterruptedException e) {
179             // this is kind of a problem, we could not add the job to the pool
180             // retrying might or might not work now that the interruption
181             // status is cleared but there is not much we can do so throwing
182             // an ExchangeException seems like the least bad choice
183             String message = "could not execute job in pool";
184             log.error(message, e);
185             throw new ExchangeException(message, e);
186         }
187     }
188 
189     /**
190      * Acquires a {@link Sync} ignoring any interruptions. Should any
191      * interruption occur the interruption status will be set. Might
192      * potentially block/wait forever.
193      *
194      * @see Sync#acquire()
195      *
196      * @param latch the latch on which to wait
197      */
198     protected static void acquireIgnoringInterruption(Sync latch) {
199         try {
200             latch.acquire();
201         } catch (InterruptedException e) {
202             // waked up externally - ignore try again
203             acquireIgnoringInterruption(latch);
204             // be a good citizen and set back the interruption status
205             Thread.currentThread().interrupt();
206         }
207     }
208 
209     protected String repositoryName;
210 
211      protected String workspaceName;
212 
213      protected String parent;
214 
215      protected String path;
216 
217      protected String nodeUUID;
218 
219      protected Content.ContentFilter contentFilter;
220 
221      protected Rule contentFilterRule;
222 
223      protected User user;
224 
225      protected String basicCredentials;
226 
227      /**
228       * @param user
229       * @param repositoryName repository ID
230       * @param workspaceName workspace ID
231       * @param rule content filter rule
232       * @see info.magnolia.cms.exchange.Syndicator#init(info.magnolia.cms.security.User, String, String,
233       * info.magnolia.cms.util.Rule)
234       */
235      public void init(User user, String repositoryName, String workspaceName, Rule rule) {
236          this.user = user;
237          this.basicCredentials = "Basic "
238              + new String(Base64.encodeBase64((this.user.getName() + ":" + this.user.getPassword()).getBytes()));
239          this.contentFilter = new RuleBasedContentFilter(rule);
240          this.contentFilterRule = rule;
241          this.repositoryName = repositoryName;
242          this.workspaceName = workspaceName;
243      }
244 
245     /**
246       * This will activate specifies page (sub pages) to all configured subscribers.
247      *
248       * @param parent parent under which this page will be activated
249       * @param content to be activated
250       * @throws javax.jcr.RepositoryException
251       * @throws info.magnolia.cms.exchange.ExchangeException
252       */
253      public void activate(String parent, Content content) throws ExchangeException, RepositoryException {
254          this.activate(parent, content, null);
255      }
256 
257      /**
258       * This will activate specified node to all configured subscribers.
259       *
260       * @param parent parent under which this page will be activated
261       * @param content to be activated
262       * @param orderBefore List of UUID to be used by the implementation to order this node after activation
263       * @throws javax.jcr.RepositoryException
264       * @throws info.magnolia.cms.exchange.ExchangeException
265       *
266       */
267      public void activate(String parent, Content content, List orderBefore) throws ExchangeException, RepositoryException {
268          this.activate(null, parent, content, orderBefore);
269      }
270 
271      /**
272       * This will activate specifies page (sub pages) to the specified subscriber.
273       *
274       * @param subscriber
275       * @param parent parent under which this page will be activated
276       * @param content to be activated
277       * @throws javax.jcr.RepositoryException
278       * @throws info.magnolia.cms.exchange.ExchangeException
279       */
280      public void activate(Subscriber subscriber, String parent, Content content) throws ExchangeException, RepositoryException {
281          this.activate(subscriber, parent, content, null);
282      }
283 
284      /**
285       * This will activate specifies node to the specified subscriber.
286       *
287       * @param subscriber
288       * @param parent      parent under which this page will be activated
289       * @param content     to be activated
290       * @param orderBefore List of UUID to be used by the subscriber to order this node after activation
291       * @throws javax.jcr.RepositoryException
292       * @throws info.magnolia.cms.exchange.ExchangeException
293       */
294      public void activate(Subscriber subscriber, String parent, Content content, List orderBefore) throws ExchangeException, RepositoryException {
295          this.parent = parent;
296          this.path = content.getHandle();
297          ActivationContent activationContent = null;
298          try {
299              activationContent = this.collect(content, orderBefore);
300              if (null == subscriber) {
301                  this.activate(activationContent);
302              } else {
303                  this.activate(subscriber, activationContent);
304              }
305              this.updateActivationDetails();
306              log.info("Exchange: activation succeeded [{}]", content.getHandle());
307          }
308          catch (Exception e) {
309              if (log.isDebugEnabled()) {
310                  log.error("Exchange: activation failed for path:" + ((path != null) ? path : "[null]"), e);
311                  long timestamp = System.currentTimeMillis();
312                  log.warn("moving files from failed activation to *.failed" + timestamp );
313                  Iterator keys = activationContent.getFiles().values().iterator();
314                  while (keys.hasNext()) {
315                      File f = (File) keys.next();
316                      f.renameTo(new File(f.getAbsolutePath()+".failed" + timestamp));
317                  }
318                  activationContent.getFiles().clear();
319 
320              }
321              throw new ExchangeException(e);
322          }
323          finally {
324              log.debug("Cleaning temporary files");
325              cleanTemporaryStore(activationContent);
326          }
327      }
328 
329      /**
330       * @throws ExchangeException
331       */
332      public abstract void activate(ActivationContent activationContent) throws ExchangeException;
333 
334 
335      /**
336       * 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.
337       */
338      public abstract String activate(Subscriber subscriber, ActivationContent activationContent) throws ExchangeException;
339 
340      /**
341       * Cleans up temporary file store after activation.
342       */
343      protected void cleanTemporaryStore(ActivationContent activationContent) {
344          if (activationContent == null) {
345              log.debug("Clean temporary store - nothing to do");
346              return;
347          }
348          if (log.isDebugEnabled()) {
349              log.debug("Debugging is enabled. Keeping temporary files in store for debugging purposes. Clean the store manually once done with debugging.");
350              return;
351          }
352 
353          Iterator keys = activationContent.getFiles().keySet().iterator();
354          while (keys.hasNext()) {
355              String key = (String) keys.next();
356              log.debug("Removing temporary file {}", key);
357              activationContent.getFile(key).delete();
358          }
359      }
360 
361      public synchronized void deactivate(String path) throws ExchangeException, RepositoryException {
362          final Content node = getHierarchyManager().getContent(path);
363          deactivate(node);
364      }
365 
366     /**
367      * @param node to deactivate
368      * @throws RepositoryException
369      * @throws ExchangeException
370      */
371     public synchronized void deactivate(Content node) throws ExchangeException, RepositoryException {
372         this.nodeUUID = node.getUUID();
373         this.path = node.getHandle();
374         this.doDeactivate();
375         updateDeactivationDetails();
376     }
377 
378     /**
379      * @param node , to deactivate
380      * @param subscriber
381      * @throws RepositoryException
382      * @throws ExchangeException
383      */
384     public synchronized void deactivate(Subscriber subscriber, Content node) throws ExchangeException, RepositoryException {
385         this.nodeUUID = node.getUUID();
386         this.path = node.getHandle();
387         this.doDeactivate(subscriber);
388         updateDeactivationDetails();
389     }
390 
391      /**
392       * @throws ExchangeException
393       */
394      public abstract void doDeactivate() throws ExchangeException;
395 
396      /**
397       * Deactivate content from specified subscriber.
398       * @param subscriber
399       * @throws ExchangeException
400       */
401      public abstract String doDeactivate(Subscriber subscriber) throws ExchangeException;
402 
403      /**
404       * Return URI set for deactivation.
405       * @param subscriberInfo
406       */
407      protected String getDeactivationURL(Subscriber subscriberInfo) {
408          return getActivationURL(subscriberInfo);
409      }
410 
411      /**
412       * Adds header fields describing deactivation request.
413       * @param connection
414       */
415      protected void addDeactivationHeaders(URLConnection connection) {
416          connection.addRequestProperty(REPOSITORY_NAME, this.repositoryName);
417          connection.addRequestProperty(WORKSPACE_NAME, this.workspaceName);
418          connection.addRequestProperty(NODE_UUID, this.nodeUUID);
419          connection.addRequestProperty(ACTION, DEACTIVATE);
420      }
421 
422      /**
423       * Retrieves URL subscriber is listening on for (de)activation requests.
424       */
425      protected String getActivationURL(Subscriber subscriberInfo) {
426          final String url = subscriberInfo.getURL();
427          if (!url.endsWith("/")) {
428              return url + "/" + DEFAULT_HANDLER;
429          }
430          return url + DEFAULT_HANDLER;
431      }
432 
433      /**
434       * Adds headers fields describing activation request.
435       */
436      protected void addActivationHeaders(URLConnection connection, ActivationContent activationContent) {
437          Iterator headerKeys = activationContent.getProperties().keySet().iterator();
438          while (headerKeys.hasNext()) {
439              String key = (String) headerKeys.next();
440              String value = activationContent.getproperty(key);
441              if(SystemProperty.getBooleanProperty(SystemProperty.MAGNOLIA_UTF8_ENABLED)) {
442                  try {
443                      value = URLEncoder.encode(value, "UTF-8");
444                  }
445                  catch (UnsupportedEncodingException e) {
446                   // do nothing
447                  }
448              }
449              connection.setRequestProperty(key, value);
450          }
451      }
452 
453      /**
454       * Updates current content activation meta data with the timestamp and user details of the activation.
455       */
456      protected void updateActivationDetails() throws RepositoryException {
457          // page activated already use system context to ensure metadata is activated even if activating user has no rights to the activated page children
458          Content page = getSystemHierarchyManager().getContent(this.path);
459          updateMetaData(page, ACTIVATE);
460          page.save();
461          AuditLoggingUtil.log(AuditLoggingUtil.ACTION_ACTIVATE, this.workspaceName, page.getItemType(), this.path );
462      }
463 
464      /**
465       * Updates current content activation meta data with the timestamp and user details of the deactivation.
466       */
467      protected void updateDeactivationDetails() throws RepositoryException {
468          // page deactivated already use system context to ensure metadata is activated even if activating user has no rights to the activated page children
469          Content page = getSystemHierarchyManager().getContentByUUID(this.nodeUUID);
470          updateMetaData(page, DEACTIVATE);
471          page.save();
472          AuditLoggingUtil.log(AuditLoggingUtil.ACTION_DEACTIVATE, this.workspaceName, page.getItemType(), page.getHandle() );
473      }
474 
475 
476      private HierarchyManager getHierarchyManager() {
477          return MgnlContext.getHierarchyManager(this.repositoryName, this.workspaceName);
478      }
479 
480      private HierarchyManager getSystemHierarchyManager() {
481          return MgnlContext.getSystemContext().getHierarchyManager(this.repositoryName, this.workspaceName);
482      }
483 
484      /**
485       * @param node
486       * @param type (activate / deactivate)
487       */
488      protected void updateMetaData(Content node, String type) throws AccessDeniedException {
489          // update the passed node
490          MetaData md = node.getMetaData();
491          if (type.equals(ACTIVATE)) {
492              md.setActivated();
493          }
494          else {
495              md.setUnActivated();
496          }
497          md.setActivatorId(this.user.getName());
498          md.setLastActivationActionDate();
499 
500          Iterator children;
501          if (type.equals(ACTIVATE)) {
502              // use syndicator rule based filter
503              children = node.getChildren(this.contentFilter).iterator();
504          }
505          else {
506              // all children
507              children = node.getChildren(ContentUtil.EXCLUDE_META_DATA_CONTENT_FILTER).iterator();
508          }
509 
510          while (children.hasNext()) {
511              Content child = (Content) children.next();
512              this.updateMetaData(child, type);
513          }
514 
515 
516      }
517 
518      /**
519       * Collects all information about activated content and its children (those that are set to be activated with the parent by filter rules).
520       * @throws Exception
521       */
522      protected ActivationContent collect(Content node, List orderBefore) throws Exception {
523          File resourceFile = File.createTempFile("resources", ".xml", Path.getTempDirectory());
524 
525          ActivationContent activationContent = new ActivationContent();
526          // add global properties true for this path/hierarchy
527          activationContent.addProperty(PARENT_PATH, this.parent);
528          activationContent.addProperty(WORKSPACE_NAME, this.workspaceName);
529          activationContent.addProperty(REPOSITORY_NAME, this.repositoryName);
530          activationContent.addProperty(RESOURCE_MAPPING_FILE, resourceFile.getName());//"resources.xml");
531          activationContent.addProperty(ACTION, ACTIVATE);
532          activationContent.addProperty(CONTENT_FILTER_RULE, this.contentFilterRule.toString());
533          activationContent.addProperty(NODE_UUID, node.getUUID());
534 
535 
536          Document document = new Document();
537          Element root = new Element(RESOURCE_MAPPING_ROOT_ELEMENT);
538          document.setRootElement(root);
539          // collect exact order of this node within its same nodeType siblings
540          addOrderingInfo(root, orderBefore);
541 
542          this.addResources(root, node.getWorkspace().getSession(), node, this.contentFilter, activationContent);
543          XMLOutputter outputter = new XMLOutputter();
544          outputter.output(document, new FileOutputStream(resourceFile));
545          // add resource file to the list
546          //activationContent.addFile("resources.xml", resourceFile);
547          activationContent.addFile(resourceFile.getName(), resourceFile);
548 
549          return activationContent;
550      }
551 
552      /**
553       * Adds ordering information to the resource mapping file.
554       * @param root element of the resource file under which ordering info must be added
555       * @param orderBefore
556       */
557      protected void addOrderingInfo(Element root, List orderBefore) {
558          //do not use magnolia Content class since these objects are only meant for a single use to read UUID
559          Element siblingRoot = new Element(SIBLINGS_ROOT_ELEMENT);
560          root.addContent(siblingRoot);
561          if (orderBefore == null) return;
562          Iterator siblings = orderBefore.iterator();
563          while (siblings.hasNext()) {
564              String uuid = (String) siblings.next();
565              Element e = new Element(SIBLINGS_ELEMENT);
566              e.setAttribute(SIBLING_UUID, uuid);
567              siblingRoot.addContent(e);
568          }
569      }
570 
571      protected void addResources(Element resourceElement, Session session, Content content, Content.ContentFilter filter, ActivationContent activationContent) throws IOException, RepositoryException, SAXException, Exception {
572          File file = File.createTempFile("exchange_" + content.getUUID(), ".xml.gz", Path.getTempDirectory());
573          GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new FileOutputStream(file));
574 
575          // 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
576          if (content.isNodeType("nt:frozenNode") || content.getWorkspace().getName().equals(ContentRepository.VERSION_STORE)) {
577              XMLReader elementfilter = new FrozenElementFilter(XMLReaderFactory
578                  .createXMLReader(org.apache.xerces.parsers.SAXParser.class.getName()));
579              ((FrozenElementFilter) elementfilter).setNodeName(content.getName());
580              /**
581               * nt:file node type has mandatory sub nodes
582               */
583              boolean noRecurse = !content.isNodeType(ItemType.NT_FILE);
584              exportAndParse(session, content, elementfilter, gzipOutputStream, noRecurse);
585          } else {
586              /**
587               * nt:file node type has mandatory sub nodes
588               */
589              if (content.isNodeType(ItemType.NT_FILE)) {
590                  session.exportSystemView(content.getJCRNode().getPath(), gzipOutputStream, false, false);
591              } else {
592                  session.exportSystemView(content.getJCRNode().getPath(), gzipOutputStream, false, true);
593              }
594          }
595 
596          IOUtils.closeQuietly(gzipOutputStream);
597          // add file entry in mapping.xml
598          Element element = new Element(RESOURCE_MAPPING_FILE_ELEMENT);
599          element.setAttribute(RESOURCE_MAPPING_NAME_ATTRIBUTE, content.getName());
600          element.setAttribute(RESOURCE_MAPPING_UUID_ATTRIBUTE, content.getUUID());
601          element.setAttribute(RESOURCE_MAPPING_ID_ATTRIBUTE, file.getName());
602          resourceElement.addContent(element);
603          // add this file element as resource in activation content
604          activationContent.addFile(file.getName(), file);
605 
606          Iterator children = content.getChildren(filter).iterator();
607          while (children.hasNext()) {
608              Content child = (Content) children.next();
609              this.addResources(element, session, child, filter, activationContent);
610          }
611      }
612 
613      protected void exportAndParse(Session session, Content content, XMLReader elementfilter, OutputStream os, boolean noRecurse) throws Exception {
614          File tempFile = File.createTempFile("Frozen_"+content.getName(), ".xml"); //$NON-NLS-1$ //$NON-NLS-2$
615          OutputStream tmpFileOutStream = null;
616          FileInputStream tmpFileInStream = null;
617          try {
618              tmpFileOutStream = new FileOutputStream(tempFile);
619              // has to get path via JCR node since if "content" is of type ContentVersion, getHandle() call would have returned path to the base
620              session.exportSystemView(content.getJCRNode().getPath(), tmpFileOutStream, false, noRecurse);
621              tmpFileOutStream.flush();
622              tmpFileOutStream.close();
623 
624              OutputFormat outputFormat = new OutputFormat();
625              outputFormat.setPreserveSpace(false);
626 
627              tmpFileInStream = new FileInputStream(tempFile);
628              elementfilter.setContentHandler(new XMLSerializer(os, outputFormat));
629              elementfilter.parse(new InputSource(tmpFileInStream));
630              tmpFileInStream.close();
631          } catch (Throwable t) {
632              log.error("Failed to parse XML using FrozenElementFilter",t);
633              throw new Exception(t);
634          } finally {
635              IOUtils.closeQuietly(tmpFileInStream);
636              IOUtils.closeQuietly(tmpFileOutStream);
637              tempFile.delete();
638          }
639      }
640 
641 
642 }