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