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.workflow.jcr;
35
36 import info.magnolia.cms.core.Content;
37 import info.magnolia.cms.core.HierarchyManager;
38 import info.magnolia.cms.core.ItemType;
39 import info.magnolia.cms.core.search.Query;
40 import info.magnolia.cms.core.search.QueryManager;
41 import info.magnolia.cms.core.search.QueryResult;
42 import info.magnolia.cms.util.ContentUtil;
43 import info.magnolia.context.LifeTimeJCRSessionUtil;
44 import info.magnolia.context.MgnlContext;
45 import info.magnolia.module.workflow.WorkflowConstants;
46
47 import java.io.InputStream;
48 import java.util.ArrayList;
49 import java.util.Iterator;
50 import java.util.Map;
51 import java.util.NoSuchElementException;
52
53 import javax.jcr.RepositoryException;
54 import javax.jcr.ValueFactory;
55
56 import openwfe.org.ApplicationContext;
57 import openwfe.org.ServiceException;
58 import openwfe.org.engine.expool.PoolException;
59 import openwfe.org.engine.expressions.FlowExpression;
60 import openwfe.org.engine.expressions.FlowExpressionId;
61 import openwfe.org.engine.expressions.raw.RawExpression;
62 import openwfe.org.engine.impl.expool.AbstractExpressionStore;
63 import openwfe.org.util.beancoder.XmlBeanCoder;
64 import openwfe.org.xml.XmlUtils;
65
66 import org.jdom.Document;
67 import org.jdom.input.SAXBuilder;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71
72
73
74
75
76
77
78
79 public class JCRExpressionStore extends AbstractExpressionStore {
80 private static final Logger log = LoggerFactory.getLogger(JCRExpressionStore.class);
81
82 private static final String ENGINE_ID = "ee";
83
84 private boolean useLifeTimeJCRSession = true;
85
86 private boolean cleanUp = false;
87
88 public JCRExpressionStore(boolean useLifeTimeJCRSession, boolean cleanUp) {
89 super();
90 this.useLifeTimeJCRSession = useLifeTimeJCRSession;
91 this.cleanUp = cleanUp;
92 }
93
94 @Override
95 public void init(final String serviceName, final ApplicationContext context, final Map serviceParams) throws ServiceException {
96 super.init(serviceName, context, serviceParams);
97 }
98
99
100
101
102 @Override
103 public synchronized void storeExpression(final FlowExpression fe) throws PoolException {
104 boolean release = !useLifeTimeJCRSession && !MgnlContext.hasInstance();
105 HierarchyManager hm = null;
106 try {
107 hm = getHierarchyManager();
108 final Content cExpression = findOrCreateExpression(fe, hm);
109
110 log.debug("storeExpression() handle is " + cExpression.getHandle());
111
112
113 ValueFactory vf = cExpression.getJCRNode().getSession().getValueFactory();
114 String value = fe.getId().toParseableString();
115
116 cExpression.createNodeData(WorkflowConstants.NODEDATA_ID, vf.createValue(value));
117
118
119 serializeExpressionAsXml(cExpression, fe);
120
121 hm.save();
122 } catch (Exception e) {
123 log.error("storeExpression() store exception failed", e);
124 try {
125 if (hm.hasPendingChanges()) {
126 hm.refresh(true);
127 }
128 } catch (RepositoryException e1) {
129 log.error("Corrupted HM during WKF access", e);
130 }
131 throw new PoolException("storeExpression() store exception failed", e);
132 } finally {
133 if (release) {
134 MgnlContext.release();
135 }
136 }
137 }
138
139
140
141
142 @Override
143 public synchronized void unstoreExpression(final FlowExpression fe) throws PoolException {
144 boolean release = !useLifeTimeJCRSession && !MgnlContext.hasInstance();
145 try {
146 final HierarchyManager hm = getHierarchyManager();
147 final Content cExpression = findOrCreateExpression(fe, hm);
148
149 if (cExpression != null) {
150 if (cleanUp) {
151 ContentUtil.deleteAndRemoveEmptyParents(cExpression, 1);
152 } else {
153 cExpression.delete();
154 }
155 hm.save();
156 } else {
157 log.info("unstoreExpression() " + "didn't find content node for fe " + fe.getId().toParseableString());
158 }
159 } catch (Exception e) {
160 log.error("unstoreExpression() unstore exception failed", e);
161 throw new PoolException("unstoreExpression() unstore exception failed", e);
162 } finally {
163 if (release) {
164 MgnlContext.release();
165 }
166 }
167 }
168
169
170
171
172 @Override
173 public synchronized Iterator contentIterator(final Class assignClass) {
174 try {
175 return new StoreIterator(assignClass);
176 } catch (final Throwable t) {
177 log.error("contentIterator() failed to set up an iterator", t);
178 }
179
180
181
182
183 return new ArrayList(0).iterator();
184 }
185
186
187
188
189 @Override
190 public synchronized FlowExpression loadExpression(final FlowExpressionId fei) throws PoolException {
191 try {
192 Content cExpression = findExpression(fei, getHierarchyManager());
193
194 if (cExpression != null) {
195 final FlowExpression expression = deserializeExpressionAsXml(cExpression);
196 if (expression != null) {
197 expression.setApplicationContext(getContext());
198 return expression;
199 }
200 }
201 } catch (final Exception e) {
202 log.error("loadExpression() failed for " + fei.asStringId(), e);
203
204 throw new PoolException("loadExpression() failed for " + fei.asStringId(), e);
205 }
206
207
208 log.info("Expected expression " + fei.asStringId() + " was not found in the repository.");
209
210 throw new PoolException("loadExpression() " + "didn't find expression " + fei.asStringId() + " in the repository");
211 }
212
213
214
215
216 @Override
217 public int size() {
218 try {
219 final QueryManager qm = MgnlContext.getSystemContext().getQueryManager(WorkflowConstants.WORKSPACE_EXPRESSION);
220 Query q = qm.createQuery(WorkflowConstants.STORE_ITERATOR_QUERY, Query.SQL);
221 QueryResult qr = q.execute();
222
223 return qr.getContent().size();
224 } catch (final Exception e) {
225 log.error("size() failed", e);
226 return -1;
227 }
228 }
229
230 private void serializeExpressionAsXml(Content c, FlowExpression fe) throws Exception {
231 final org.jdom.Document doc = XmlBeanCoder.xmlEncode(fe);
232 String s = XmlUtils.toString(doc, null);
233 ValueFactory vf = c.getJCRNode().getSession().getValueFactory();
234 c.createNodeData(WorkflowConstants.NODEDATA_VALUE, vf.createValue(s));
235 }
236
237 public final String toXPathFriendlyString(final FlowExpressionId fei) {
238 final StringBuffer buffer = new StringBuffer();
239 final String engineId = fei.getEngineId();
240
241 buffer.append(WorkflowConstants.SLASH);
242 buffer.append(engineId);
243
244
245 if (engineId.equals(ENGINE_ID)) {
246 return buffer.toString();
247 }
248
249 buffer.append(WorkflowConstants.SLASH);
250 buffer.append(fei.getWorkflowDefinitionName());
251
252 buffer.append(WorkflowConstants.SLASH);
253 buffer.append(fei.getWorkflowInstanceId());
254
255 buffer.append(WorkflowConstants.SLASH);
256 buffer.append(fei.getExpressionId());
257 buffer.append("__");
258 buffer.append(fei.getExpressionName());
259
260 return buffer.toString();
261 }
262
263 private Content findOrCreateExpression(final FlowExpression fe, HierarchyManager hm) throws Exception {
264 Content content = findExpression(fe.getId(), hm);
265 if (content == null) {
266 content = ContentUtil.createPath(hm, toXPathFriendlyString(fe.getId()), ItemType.EXPRESSION);
267 }
268 return content;
269 }
270
271 private Content findExpression(final FlowExpressionId fei, HierarchyManager hm) throws Exception {
272 if (log.isDebugEnabled()) {
273 log.debug("findExpression() looking for " + fei.toParseableString());
274 }
275
276 final String path = toXPathFriendlyString(fei);
277
278 if (hm.isExist(path)) {
279 return hm.getContent(path);
280 } else {
281 return null;
282 }
283 }
284
285 private FlowExpression deserializeExpressionAsXml(final Content c) throws Exception {
286 final InputStream is = c.getNodeData(WorkflowConstants.NODEDATA_VALUE).getStream();
287
288 if (is == null) {
289 return null;
290 }
291
292 final SAXBuilder builder = new SAXBuilder();
293 final Document doc = builder.build(is);
294 return (FlowExpression) XmlBeanCoder.xmlDecode(doc);
295 }
296
297 protected HierarchyManager getHierarchyManager() {
298 HierarchyManager hm;
299 if (useLifeTimeJCRSession) {
300 hm = LifeTimeJCRSessionUtil.getHierarchyManager(WorkflowConstants.WORKSPACE_EXPRESSION);
301 } else {
302 hm = MgnlContext.getSystemContext().getHierarchyManager(WorkflowConstants.WORKSPACE_EXPRESSION);
303 }
304 try {
305 if (hm.hasPendingChanges()) {
306
307
308
309 log.warn("The workflow expression session has pending changes while " + (useLifeTimeJCRSession ? "" : "not ") + "using Life Time session. Will clean the session",
310 new Exception());
311 hm.refresh(true);
312 }
313 } catch (RepositoryException e) {
314
315 log.error("Can't check/refresh worflow expression session.", e);
316 }
317 return hm;
318 }
319
320
321
322
323
324
325
326
327 protected final class StoreIterator implements Iterator {
328 private final Class assignClass;
329 private Iterator rootIterator = null;
330 private FlowExpression next = null;
331
332 public StoreIterator(final Class assignClass) throws Exception {
333 super();
334
335 this.assignClass = assignClass;
336
337 final QueryManager qm = LifeTimeJCRSessionUtil.getQueryManager(WorkflowConstants.WORKSPACE_EXPRESSION);
338
339 final Query query = qm.createQuery(WorkflowConstants.STORE_ITERATOR_QUERY, Query.SQL);
340
341 final QueryResult qr = query.execute();
342
343 if (log.isDebugEnabled()) {
344 log.debug("() query found " + qr.getContent("expression").size() + " elements");
345 }
346
347 this.rootIterator = qr.getContent("expression").iterator();
348
349 this.next = fetchNext();
350 }
351
352 @Override
353 public boolean hasNext() {
354 return (this.next != null);
355 }
356
357 public FlowExpression fetchNext() {
358 if (!this.rootIterator.hasNext()) {
359 return null;
360 }
361
362 final Content content = (Content) this.rootIterator.next();
363 try {
364 final FlowExpression fe = deserializeExpressionAsXml(content);
365
366 if (fe == null) {
367 return fetchNext();
368 }
369
370 fe.setApplicationContext(getContext());
371
372 if (!isAssignableFromClass(fe, this.assignClass)) {
373 return fetchNext();
374 }
375
376 return fe;
377 } catch (final Exception e) {
378 log.error("fetchNext() problem", e);
379 return null;
380 }
381 }
382
383 @Override
384 public Object next() throws java.util.NoSuchElementException {
385 final FlowExpression current = this.next;
386
387 if (current == null) {
388 throw new NoSuchElementException();
389 }
390
391 this.next = fetchNext();
392
393 if (log.isDebugEnabled()) {
394 log.debug("next() is " + (next != null ? next.getId().toString() : "'null'"));
395 }
396
397 return current;
398 }
399
400 @Override
401 public void remove() {
402 throw new UnsupportedOperationException();
403 }
404
405
406
407 private boolean isAssignableFromClass(final FlowExpression fe, final Class expClass) {
408 if (expClass == null) {
409 return true;
410 }
411
412 Class c = fe.getClass();
413
414 if (fe instanceof RawExpression) {
415 c = fe.getExpressionClass();
416 if (c == null) {
417
418
419
420 log.warn("Skipping expression " + fe.getId() + " (" + ((RawExpression) fe).getDefinitionName() + ")");
421 return false;
422 }
423 }
424
425 return expClass.isAssignableFrom(c);
426 }
427 }
428
429 }