001: /*
002: * Copyright (c) 2002-2003 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.workflow.spi.hibernate;
006:
007: import com.opensymphony.module.propertyset.PropertySet;
008: import com.opensymphony.module.propertyset.PropertySetManager;
009: import com.opensymphony.module.propertyset.hibernate.DefaultHibernateConfigurationProvider;
010:
011: import com.opensymphony.workflow.QueryNotSupportedException;
012: import com.opensymphony.workflow.StoreException;
013: import com.opensymphony.workflow.query.FieldExpression;
014: import com.opensymphony.workflow.query.NestedExpression;
015: import com.opensymphony.workflow.query.WorkflowExpressionQuery;
016: import com.opensymphony.workflow.query.WorkflowQuery;
017: import com.opensymphony.workflow.spi.Step;
018: import com.opensymphony.workflow.spi.WorkflowEntry;
019: import com.opensymphony.workflow.spi.WorkflowStore;
020: import com.opensymphony.workflow.util.PropertySetDelegate;
021:
022: import net.sf.hibernate.Criteria;
023: import net.sf.hibernate.HibernateException;
024: import net.sf.hibernate.Session;
025: import net.sf.hibernate.expression.Criterion;
026: import net.sf.hibernate.expression.Expression;
027:
028: import org.springframework.orm.hibernate.HibernateCallback;
029: import org.springframework.orm.hibernate.support.HibernateDaoSupport;
030:
031: import java.util.ArrayList;
032: import java.util.Collection;
033: import java.util.Collections;
034: import java.util.Date;
035: import java.util.HashMap;
036: import java.util.HashSet;
037: import java.util.Iterator;
038: import java.util.List;
039: import java.util.Map;
040: import java.util.Set;
041:
042: /**
043: * @author Quake Wang
044: * @since 2004-5-2
045: *
046: **/
047: public class SpringHibernateWorkflowStore extends HibernateDaoSupport
048: implements WorkflowStore {
049: //~ Instance fields ////////////////////////////////////////////////////////
050:
051: private PropertySetDelegate propertySetDelegate;
052: private String cacheRegion = null;
053: private boolean cacheable = false;
054:
055: //~ Methods ////////////////////////////////////////////////////////////////
056:
057: public void setCacheRegion(String cacheRegion) {
058: this .cacheRegion = cacheRegion;
059: }
060:
061: public void setCacheable(boolean cacheable) {
062: this .cacheable = cacheable;
063: }
064:
065: public void setEntryState(long entryId, int state)
066: throws StoreException {
067: HibernateWorkflowEntry entry = loadEntry(entryId);
068: entry.setState(state);
069: getHibernateTemplate().update(entry);
070: }
071:
072: public PropertySet getPropertySet(long entryId)
073: throws StoreException {
074: if (propertySetDelegate != null) {
075: return propertySetDelegate.getPropertySet(entryId);
076: }
077:
078: HashMap args = new HashMap();
079: args.put("entityName", "OSWorkflowEntry");
080: args.put("entityId", new Long(entryId));
081:
082: DefaultHibernateConfigurationProvider configurationProvider = new DefaultHibernateConfigurationProvider();
083: configurationProvider.setSessionFactory(getSessionFactory());
084:
085: args.put("configurationProvider", configurationProvider);
086:
087: return PropertySetManager.getInstance("hibernate", args);
088: }
089:
090: public void setPropertySetDelegate(
091: PropertySetDelegate propertySetDelegate) {
092: this .propertySetDelegate = propertySetDelegate;
093: }
094:
095: public PropertySetDelegate getPropertySetDelegate() {
096: return propertySetDelegate;
097: }
098:
099: public Step createCurrentStep(long entryId, int stepId,
100: String owner, Date startDate, Date dueDate, String status,
101: long[] previousIds) throws StoreException {
102: HibernateCurrentStep step = new HibernateCurrentStep();
103: HibernateWorkflowEntry entry = loadEntry(entryId);
104:
105: step.setEntry(entry);
106: step.setStepId(stepId);
107: step.setOwner(owner);
108: step.setStartDate(startDate);
109: step.setDueDate(dueDate);
110: step.setStatus(status);
111:
112: final List stepIdList = new ArrayList(previousIds.length);
113:
114: for (int i = 0; i < previousIds.length; i++) {
115: long previousId = previousIds[i];
116: stepIdList.add(new Long(previousId));
117: }
118:
119: if (!stepIdList.isEmpty()) {
120: step.setPreviousSteps((List) getHibernateTemplate()
121: .execute(new HibernateCallback() {
122: public Object doInHibernate(Session session)
123: throws HibernateException {
124: return session
125: .createQuery(
126: "FROM "
127: + HibernateCurrentStep.class
128: .getName()
129: + " step WHERE step.id IN (:stepIds)")
130: .setParameterList("stepIds",
131: stepIdList).setCacheable(
132: isCacheable())
133: .setCacheRegion(getCacheRegion())
134: .list();
135: }
136: }));
137: } else {
138: step.setPreviousSteps(Collections.EMPTY_LIST);
139: }
140:
141: getHibernateTemplate().save(step);
142:
143: return step;
144: }
145:
146: public WorkflowEntry createEntry(String workflowName)
147: throws StoreException {
148: HibernateWorkflowEntry entry = new HibernateWorkflowEntry();
149: entry.setState(WorkflowEntry.CREATED);
150: entry.setWorkflowName(workflowName);
151: getHibernateTemplate().save(entry);
152:
153: return entry;
154: }
155:
156: public List findCurrentSteps(final long entryId)
157: throws StoreException {
158: return (List) getHibernateTemplate().execute(
159: new HibernateCallback() {
160: public Object doInHibernate(Session session)
161: throws HibernateException {
162: return session
163: .createQuery(
164: "FROM "
165: + HibernateCurrentStep.class
166: .getName()
167: + " step WHERE step.entry.id = :entryId")
168: .setLong("entryId", entryId)
169: .setCacheable(isCacheable())
170: .setCacheRegion(getCacheRegion())
171: .list();
172: }
173: });
174: }
175:
176: public WorkflowEntry findEntry(long entryId) throws StoreException {
177: return loadEntry(entryId);
178: }
179:
180: public List findHistorySteps(final long entryId)
181: throws StoreException {
182: return (List) getHibernateTemplate().execute(
183: new HibernateCallback() {
184: public Object doInHibernate(Session session)
185: throws HibernateException {
186: return session
187: .createQuery(
188: "FROM "
189: + HibernateHistoryStep.class
190: .getName()
191: + " step WHERE step.entry.id = :entryId")
192: .setLong("entryId", entryId)
193: .setCacheable(isCacheable())
194: .setCacheRegion(getCacheRegion())
195: .list();
196: }
197: });
198: }
199:
200: public void init(Map props) throws StoreException {
201: // TODO Auto-generated method stub
202: }
203:
204: public Step markFinished(Step step, int actionId, Date finishDate,
205: String status, String caller) throws StoreException {
206: HibernateCurrentStep currentStep = (HibernateCurrentStep) step;
207:
208: currentStep.setActionId(actionId);
209: currentStep.setFinishDate(finishDate);
210: currentStep.setStatus(status);
211: currentStep.setCaller(caller);
212: getHibernateTemplate().update(currentStep);
213:
214: return currentStep;
215: }
216:
217: public void moveToHistory(Step step) throws StoreException {
218: HibernateHistoryStep hStep = new HibernateHistoryStep(
219: (HibernateStep) step);
220: getHibernateTemplate().delete(step);
221: getHibernateTemplate().save(hStep);
222: }
223:
224: public List query(WorkflowQuery query) throws StoreException {
225: Class entityClass;
226:
227: int qtype = query.getType();
228:
229: if (qtype == 0) { // then not set, so look in sub queries
230:
231: if (query.getLeft() != null) {
232: qtype = query.getLeft().getType();
233: }
234: }
235:
236: if (qtype == WorkflowQuery.CURRENT) {
237: entityClass = HibernateCurrentStep.class;
238: } else {
239: entityClass = HibernateHistoryStep.class;
240: }
241:
242: Criteria criteria = getSession().createCriteria(entityClass);
243: Criterion expression = buildExpression(query);
244: criteria.add(expression);
245:
246: //get results and send them back
247: try {
248: Set results = new HashSet();
249: Iterator iter = criteria.list().iterator();
250:
251: while (iter.hasNext()) {
252: HibernateStep step = (HibernateStep) iter.next();
253: results.add(new Long(step.getEntryId()));
254: }
255:
256: return new ArrayList(results);
257: } catch (HibernateException e) {
258: throw new StoreException("Error executing query "
259: + expression, e);
260: }
261: }
262:
263: /* (non-Javadoc)
264: * @see com.opensymphony.workflow.spi.WorkflowStore#query(com.opensymphony.workflow.query.WorkflowExpressionQuery)
265: */
266: public List query(WorkflowExpressionQuery query)
267: throws StoreException {
268: com.opensymphony.workflow.query.Expression expression = query
269: .getExpression();
270:
271: Criterion expr;
272:
273: Class entityClass = getQueryClass(expression, null);
274:
275: if (expression.isNested()) {
276: expr = buildNested((NestedExpression) expression);
277: } else {
278: expr = queryComparison((FieldExpression) expression);
279: }
280:
281: Criteria criteria = getSession().createCriteria(entityClass);
282: criteria.add(expr);
283:
284: try {
285: Set results = new HashSet();
286:
287: Iterator iter = criteria.list().iterator();
288:
289: while (iter.hasNext()) {
290: Object next = iter.next();
291: Object item;
292:
293: if (next instanceof HibernateStep) {
294: HibernateStep step = (HibernateStep) next;
295: item = new Long(step.getEntryId());
296: } else {
297: WorkflowEntry entry = (WorkflowEntry) next;
298: item = new Long(entry.getId());
299: }
300:
301: results.add(item);
302: }
303:
304: return new ArrayList(results);
305: } catch (HibernateException e) {
306: throw new StoreException("Error executing query "
307: + expression, e);
308: }
309: }
310:
311: protected String getCacheRegion() {
312: return cacheRegion;
313: }
314:
315: protected boolean isCacheable() {
316: return cacheable;
317: }
318:
319: private Criterion getExpression(WorkflowQuery query) {
320: int operator = query.getOperator();
321:
322: switch (operator) {
323: case WorkflowQuery.EQUALS:
324: return Expression.eq(getFieldName(query.getField()), query
325: .getValue());
326:
327: case WorkflowQuery.NOT_EQUALS:
328: return Expression.not(Expression.like(getFieldName(query
329: .getField()), query.getValue()));
330:
331: case WorkflowQuery.GT:
332: return Expression.gt(getFieldName(query.getField()), query
333: .getValue());
334:
335: case WorkflowQuery.LT:
336: return Expression.lt(getFieldName(query.getField()), query
337: .getValue());
338:
339: default:
340: return Expression.eq(getFieldName(query.getField()), query
341: .getValue());
342: }
343: }
344:
345: private String getFieldName(int field) {
346: switch (field) {
347: case FieldExpression.ACTION: // actionId
348: return "actionId";
349:
350: case FieldExpression.CALLER:
351: return "caller";
352:
353: case FieldExpression.FINISH_DATE:
354: return "finishDate";
355:
356: case FieldExpression.OWNER:
357: return "owner";
358:
359: case FieldExpression.START_DATE:
360: return "startDate";
361:
362: case FieldExpression.STEP: // stepId
363: return "stepId";
364:
365: case FieldExpression.STATUS:
366: return "status";
367:
368: case FieldExpression.STATE:
369: return "state";
370:
371: case FieldExpression.NAME:
372: return "workflowName";
373:
374: default:
375: return "1";
376: }
377: }
378:
379: private Class getQueryClass(
380: com.opensymphony.workflow.query.Expression expr,
381: Collection classesCache) throws StoreException {
382: if (classesCache == null) {
383: classesCache = new HashSet();
384: }
385:
386: if (expr instanceof FieldExpression) {
387: FieldExpression fieldExpression = (FieldExpression) expr;
388:
389: switch (fieldExpression.getContext()) {
390: case FieldExpression.CURRENT_STEPS:
391: classesCache.add(HibernateCurrentStep.class);
392:
393: break;
394:
395: case FieldExpression.HISTORY_STEPS:
396: classesCache.add(HibernateHistoryStep.class);
397:
398: break;
399:
400: case FieldExpression.ENTRY:
401: classesCache.add(HibernateWorkflowEntry.class);
402:
403: break;
404:
405: default:
406: throw new QueryNotSupportedException(
407: "Query for unsupported context "
408: + fieldExpression.getContext());
409: }
410: } else {
411: NestedExpression nestedExpression = (NestedExpression) expr;
412:
413: for (int i = 0; i < nestedExpression.getExpressionCount(); i++) {
414: com.opensymphony.workflow.query.Expression expression = nestedExpression
415: .getExpression(i);
416:
417: if (expression.isNested()) {
418: classesCache.add(getQueryClass(nestedExpression
419: .getExpression(i), classesCache));
420: } else {
421: classesCache.add(getQueryClass(expression,
422: classesCache));
423: }
424: }
425: }
426:
427: if (classesCache.size() > 1) {
428: throw new QueryNotSupportedException(
429: "Store does not support nested queries of different types (types found:"
430: + classesCache + ")");
431: }
432:
433: return (Class) classesCache.iterator().next();
434: }
435:
436: private Criterion buildExpression(WorkflowQuery query)
437: throws StoreException {
438: if (query.getLeft() == null) {
439: if (query.getRight() == null) {
440: return getExpression(query); //leaf node
441: } else {
442: throw new StoreException(
443: "Invalid WorkflowQuery object. QueryLeft is null but QueryRight is not.");
444: }
445: } else {
446: if (query.getRight() == null) {
447: throw new StoreException(
448: "Invalid WorkflowQuery object. QueryLeft is not null but QueryRight is.");
449: }
450:
451: int operator = query.getOperator();
452: WorkflowQuery left = query.getLeft();
453: WorkflowQuery right = query.getRight();
454:
455: switch (operator) {
456: case WorkflowQuery.AND:
457: return Expression.and(buildExpression(left),
458: buildExpression(right));
459:
460: case WorkflowQuery.OR:
461: return Expression.or(buildExpression(left),
462: buildExpression(right));
463:
464: case WorkflowQuery.XOR:
465: throw new QueryNotSupportedException(
466: "XOR Operator in Queries not supported by "
467: + this .getClass().getName());
468:
469: default:
470: throw new QueryNotSupportedException("Operator '"
471: + operator + "' is not supported by "
472: + this .getClass().getName());
473: }
474: }
475: }
476:
477: private Criterion buildNested(NestedExpression nestedExpression)
478: throws StoreException {
479: Criterion full = null;
480:
481: for (int i = 0; i < nestedExpression.getExpressionCount(); i++) {
482: Criterion expr;
483: com.opensymphony.workflow.query.Expression expression = nestedExpression
484: .getExpression(i);
485:
486: if (expression.isNested()) {
487: expr = buildNested((NestedExpression) nestedExpression
488: .getExpression(i));
489: } else {
490: FieldExpression sub = (FieldExpression) nestedExpression
491: .getExpression(i);
492: expr = queryComparison(sub);
493:
494: if (sub.isNegate()) {
495: expr = Expression.not(expr);
496: }
497: }
498:
499: if (full == null) {
500: full = expr;
501: } else {
502: switch (nestedExpression.getExpressionOperator()) {
503: case NestedExpression.AND:
504: full = Expression.and(full, expr);
505:
506: break;
507:
508: case NestedExpression.OR:
509: full = Expression.or(full, expr);
510: }
511: }
512: }
513:
514: return full;
515: }
516:
517: private HibernateWorkflowEntry loadEntry(long entryId) {
518: return (HibernateWorkflowEntry) getHibernateTemplate().load(
519: HibernateWorkflowEntry.class, new Long(entryId));
520: }
521:
522: private Criterion queryComparison(FieldExpression expression) {
523: int operator = expression.getOperator();
524:
525: switch (operator) {
526: case FieldExpression.EQUALS:
527: return Expression.eq(getFieldName(expression.getField()),
528: expression.getValue());
529:
530: case FieldExpression.NOT_EQUALS:
531: return Expression.not(Expression.like(
532: getFieldName(expression.getField()), expression
533: .getValue()));
534:
535: case FieldExpression.GT:
536: return Expression.gt(getFieldName(expression.getField()),
537: expression.getValue());
538:
539: case FieldExpression.LT:
540: return Expression.lt(getFieldName(expression.getField()),
541: expression.getValue());
542:
543: default:
544: return Expression.eq(getFieldName(expression.getField()),
545: expression.getValue());
546: }
547: }
548: }
|