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