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