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 static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.ACTION;
37 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.ACTIVATE;
38 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.CONTENT_FILTER_RULE;
39 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.NODE_UUID;
40 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.PARENT_PATH;
41 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.REPOSITORY_NAME;
42 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.RESOURCE_MAPPING_FILE;
43 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.RESOURCE_MAPPING_FILE_ELEMENT;
44 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.RESOURCE_MAPPING_ID_ATTRIBUTE;
45 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.RESOURCE_MAPPING_MD_ATTRIBUTE;
46 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.RESOURCE_MAPPING_NAME_ATTRIBUTE;
47 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.RESOURCE_MAPPING_ROOT_ELEMENT;
48 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.RESOURCE_MAPPING_UUID_ATTRIBUTE;
49 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.SIBLINGS_ELEMENT;
50 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.SIBLINGS_ROOT_ELEMENT;
51 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.SIBLING_UUID;
52 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.UTF8_STATUS;
53 import static info.magnolia.module.exchangesimple.BaseSyndicatorImpl.WORKSPACE_NAME;
54 import info.magnolia.cms.core.Content;
55 import info.magnolia.cms.core.ItemType;
56 import info.magnolia.cms.core.Path;
57 import info.magnolia.cms.core.SystemProperty;
58 import info.magnolia.cms.exchange.ExchangeException;
59 import info.magnolia.cms.security.SecurityUtil;
60 import info.magnolia.cms.util.Rule;
61 import info.magnolia.cms.util.RuleBasedContentFilter;
62 import info.magnolia.repository.RepositoryConstants;
63
64 import java.io.File;
65 import java.io.FileInputStream;
66 import java.io.FileOutputStream;
67 import java.io.IOException;
68 import java.io.OutputStream;
69 import java.security.DigestOutputStream;
70 import java.security.MessageDigest;
71 import java.security.NoSuchAlgorithmException;
72 import java.util.Iterator;
73 import java.util.List;
74 import java.util.zip.GZIPOutputStream;
75
76 import javax.jcr.RepositoryException;
77 import javax.jcr.Session;
78
79 import org.apache.commons.io.IOUtils;
80 import org.apache.xml.serialize.OutputFormat;
81 import org.apache.xml.serialize.XMLSerializer;
82 import org.jdom.Document;
83 import org.jdom.Element;
84 import org.jdom.output.XMLOutputter;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
87 import org.xml.sax.InputSource;
88 import org.xml.sax.SAXException;
89 import org.xml.sax.XMLReader;
90 import org.xml.sax.helpers.XMLReaderFactory;
91
92
93
94
95
96
97
98 public class ResourceCollector {
99
100 private final MessageDigest md;
101
102 public ResourceCollector() throws ExchangeException {
103 try {
104 md = MessageDigest.getInstance("MD5");
105 } catch (NoSuchAlgorithmException e) {
106 throw new ExchangeException("In order to proceed with activation please run Magnolia CMS using Java version with MD5 support.", e);
107 }
108 }
109
110 private static final Logger log = LoggerFactory.getLogger(ResourceCollector.class);
111
112
113
114
115
116
117
118
119
120
121
122 protected ActivationContent collect(Content node, List<String> orderBefore, String parent, String workspaceName, String repositoryName, Rule contentFilterRule) throws Exception {
123
124
125 md.reset();
126
127 Content.ContentFilter contentFilter = new RuleBasedContentFilter(contentFilterRule);
128
129
130 File resourceFile = File.createTempFile("resources", ".xml", Path.getTempDirectory());
131
132 ActivationContent activationContent = new ActivationContent();
133
134 activationContent.addProperty(PARENT_PATH, parent);
135 activationContent.addProperty(WORKSPACE_NAME, workspaceName);
136 activationContent.addProperty(REPOSITORY_NAME, repositoryName);
137 activationContent.addProperty(RESOURCE_MAPPING_FILE, resourceFile.getName());
138 activationContent.addProperty(ACTION, ACTIVATE);
139 activationContent.addProperty(CONTENT_FILTER_RULE, contentFilterRule.toString());
140 activationContent.addProperty(NODE_UUID, node.getUUID());
141 activationContent.addProperty(UTF8_STATUS, SystemProperty.getProperty(SystemProperty.MAGNOLIA_UTF8_ENABLED));
142
143 Document document = new Document();
144 Element root = new Element(RESOURCE_MAPPING_ROOT_ELEMENT);
145 document.setRootElement(root);
146
147 addOrderingInfo(root, orderBefore);
148
149 this.addResources(root, node.getWorkspace().getSession(), node, contentFilter, activationContent);
150 XMLOutputter outputter = new XMLOutputter();
151
152 outputter.output(document, new DigestOutputStream(new FileOutputStream(resourceFile), md));
153
154 activationContent.addFile(resourceFile.getName(), resourceFile);
155
156 activationContent.addProperty(RESOURCE_MAPPING_MD_ATTRIBUTE, SecurityUtil.byteArrayToHex(md.digest()));
157
158
159 activationContent.addProperty(ItemType.DELETED_NODE_MIXIN, "" + node.hasMixin(ItemType.DELETED_NODE_MIXIN));
160
161 return activationContent;
162 }
163
164
165
166
167
168
169
170
171 protected void addOrderingInfo(Element root, List<String> orderBefore) {
172
173 Element siblingRoot = new Element(SIBLINGS_ROOT_ELEMENT);
174 root.addContent(siblingRoot);
175 if (orderBefore == null) {
176 return;
177 }
178 Iterator<String> siblings = orderBefore.iterator();
179 while (siblings.hasNext()) {
180 String uuid = siblings.next();
181 Element e = new Element(SIBLINGS_ELEMENT);
182 e.setAttribute(SIBLING_UUID, uuid);
183 siblingRoot.addContent(e);
184 }
185 }
186
187 protected void addResources(Element resourceElement, Session session, final Content content, Content.ContentFilter filter, ActivationContent activationContent) throws IOException, RepositoryException, SAXException, Exception {
188 final String workspaceName = content.getWorkspace().getName();
189 log.debug("Preparing content {}:{} for publishing.", new String[] { workspaceName, content.getHandle() });
190 final String uuid = content.getUUID();
191
192 File file = File.createTempFile("exchange_" + uuid, ".xml.gz", Path.getTempDirectory());
193 OutputStream outputStream = new DigestOutputStream(new GZIPOutputStream(new FileOutputStream(file)), md);
194
195
196 if (content.isNodeType("nt:frozenNode") || workspaceName.equals(RepositoryConstants.VERSION_STORE)) {
197 XMLReader elementfilter = new FrozenElementFilter(XMLReaderFactory
198 .createXMLReader(org.apache.xerces.parsers.SAXParser.class.getName()));
199 ((FrozenElementFilter) elementfilter).setNodeName(content.getName());
200
201
202
203 boolean noRecurse = !content.isNodeType(ItemType.NT_FILE);
204 exportAndParse(session, content, elementfilter, outputStream, noRecurse);
205 } else {
206
207
208
209 if (content.isNodeType(ItemType.NT_FILE)) {
210 session.exportSystemView(content.getJCRNode().getPath(), outputStream, false, false);
211 } else {
212 session.exportSystemView(content.getJCRNode().getPath(), outputStream, false, true);
213 }
214 }
215
216 IOUtils.closeQuietly(outputStream);
217
218 Element element = new Element(RESOURCE_MAPPING_FILE_ELEMENT);
219 element.setAttribute(RESOURCE_MAPPING_NAME_ATTRIBUTE, content.getName());
220 element.setAttribute(RESOURCE_MAPPING_UUID_ATTRIBUTE, uuid);
221 element.setAttribute(RESOURCE_MAPPING_ID_ATTRIBUTE, file.getName());
222 element.setAttribute(RESOURCE_MAPPING_MD_ATTRIBUTE, SecurityUtil.byteArrayToHex(md.digest()));
223 resourceElement.addContent(element);
224
225 activationContent.addFile(file.getName(), file);
226
227 Iterator<Content> children = content.getChildren(filter).iterator();
228 while (children.hasNext()) {
229 Content child = children.next();
230 this.addResources(element, session, child, filter, activationContent);
231 }
232 }
233
234
235
236
237 protected void exportAndParse(Session session, Content content, XMLReader elementfilter, OutputStream os, boolean noRecurse) throws Exception {
238 File tempFile = File.createTempFile("Frozen_" + content.getName(), ".xml");
239 OutputStream tmpFileOutStream = null;
240 FileInputStream tmpFileInStream = null;
241 try {
242 tmpFileOutStream = new FileOutputStream(tempFile);
243
244 session.exportSystemView(content.getJCRNode().getPath(), tmpFileOutStream, false, noRecurse);
245 tmpFileOutStream.flush();
246 tmpFileOutStream.close();
247
248 OutputFormat outputFormat = new OutputFormat();
249 outputFormat.setPreserveSpace(false);
250
251 tmpFileInStream = new FileInputStream(tempFile);
252 elementfilter.setContentHandler(new XMLSerializer(os, outputFormat));
253 elementfilter.parse(new InputSource(tmpFileInStream));
254 tmpFileInStream.close();
255 } catch (Throwable t) {
256 log.error("Failed to parse XML using FrozenElementFilter", t);
257 throw new Exception(t);
258 } finally {
259 IOUtils.closeQuietly(tmpFileInStream);
260 IOUtils.closeQuietly(tmpFileOutStream);
261 tempFile.delete();
262 }
263 }
264
265
266
267
268
269 }