001: /*
002: * The contents of this file are subject to the
003: * Mozilla Public License Version 1.1 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
006: *
007: * Software distributed under the License is distributed on an "AS IS"
008: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
009: * See the License for the specific language governing rights and
010: * limitations under the License.
011: *
012: * The Initial Developer of the Original Code is Simulacra Media Ltd.
013: * Portions created by Simulacra Media Ltd are Copyright (C) Simulacra Media Ltd, 2004.
014: *
015: * All Rights Reserved.
016: *
017: * Contributor(s):
018: */
019: package org.openharmonise.rm.workflow;
020:
021: import java.util.*;
022: import java.util.logging.*;
023:
024: import org.openharmonise.commons.dsi.*;
025: import org.openharmonise.rm.*;
026: import org.openharmonise.rm.logging.*;
027: import org.openharmonise.rm.metadata.*;
028: import org.openharmonise.rm.resources.*;
029: import org.openharmonise.rm.resources.lifecycle.EditException;
030: import org.openharmonise.rm.resources.metadata.properties.Property;
031: import org.openharmonise.rm.resources.metadata.values.Value;
032: import org.openharmonise.rm.resources.users.User;
033: import org.openharmonise.rm.resources.workflow.properties.ranges.WorkflowRange;
034: import org.openharmonise.rm.resources.workflow.values.WorkflowStageValue;
035: import org.openharmonise.rm.security.authorization.*;
036:
037: /**
038: * Class to represent an instance of a workflow.
039: *
040: * This class extends <code>ChildObjectPropertyInstance</code>, offering
041: * utility methods specific to handling workflows and will only accept
042: * <code>WorkflowStageInstance</code> values.
043: *
044: * @author Michael Bell
045: * @version $Revision: 1.2 $
046: *
047: */
048: public class WorkflowPropertyInstance extends
049: ChildObjectPropertyInstance {
050:
051: //TODO review the need for this flag which allows <code>Search</code> to work
052: //allows the addition of values to prop inst for search
053: private boolean m_bAllowSave = true;
054:
055: /**
056: * Logger for this class
057: */
058: static private Logger m_logger = Logger
059: .getLogger(WorkflowPropertyInstance.class.getName());
060:
061: /**
062: * Basic constructor
063: */
064: public WorkflowPropertyInstance() {
065: super ();
066: }
067:
068: /**
069: * Constructs object with a interface to the datastore
070: *
071: * @param dbintrf
072: */
073: public WorkflowPropertyInstance(AbstractDataStoreInterface dbintrf) {
074: super (dbintrf);
075: }
076:
077: /**
078: * Returns a list of the completed workflow stage instances for this
079: * workflow instance
080: *
081: * @return @throws
082: * DataAccessException
083: */
084: public List getCompletedStages() throws DataAccessException {
085: return super .getValues();
086: }
087:
088: /**
089: * Returns <code>true</code> if work flow is complete
090: *
091: * @return @throws
092: * DataAccessException
093: */
094: public boolean isComplete() throws DataAccessException {
095: boolean bIsComplete = true;
096:
097: Property prop = getProperty();
098:
099: WorkflowRange range = (WorkflowRange) prop.getRange();
100:
101: List vals = range.getAvailableValues();
102:
103: Iterator iter = vals.iterator();
104:
105: List currVals = getValues();
106:
107: while (iter.hasNext() && bIsComplete == true) {
108: WorkflowStageValue stage = (WorkflowStageValue) iter.next();
109:
110: if (stage.isMandatory()
111: && currVals.contains(stage) == false) {
112: bIsComplete = false;
113: if (m_logger.isLoggable(Level.FINER)) {
114: try {
115: AbstractProfiledObject profObj = getProfiledObject();
116: m_logger.logp(Level.FINER, this .getClass()
117: .getName(), "isComplete", "stage "
118: + stage.getName()
119: + " not complete for "
120: + profObj.getClass().getName() + "("
121: + profObj.getKey() + ")");
122: } catch (DataAccessException e) {
123: m_logger.log(Level.WARNING, e
124: .getLocalizedMessage(), e);
125: }
126: }
127: }
128: }
129:
130: if (m_logger.isLoggable(Level.FINE)) {
131: try {
132: AbstractProfiledObject profObj = getProfiledObject();
133: m_logger.logp(Level.FINE, this .getClass().getName(),
134: "isComplete", "workflow (" + getName()
135: + ") for "
136: + profObj.getClass().getName() + "("
137: + profObj.getKey() + ") complete - "
138: + bIsComplete);
139: } catch (DataAccessException e) {
140: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
141: }
142: }
143:
144: return bIsComplete;
145: }
146:
147: /**
148: * Returns <code>true</code> if the given workflow stage instance is valid
149: * for this workflow instance
150: *
151: * @param instance
152: * @return
153: */
154: private boolean isValidStage(WorkflowStageValue stage)
155: throws DataAccessException {
156: boolean bIsValid = false;
157:
158: List depends = stage.getDependencies();
159:
160: if (depends.size() > 0) {
161: List completes = getCompletedStages();
162: bIsValid = completes.containsAll(depends);
163:
164: } else {
165: bIsValid = true;
166: }
167:
168: return bIsValid;
169: }
170:
171: /* (non-Javadoc)
172: * @see org.openharmonise.rm.metadata.ChildObjectPropertyInstance#addValue(org.openharmonise.rm.resources.AbstractChildObject)
173: */
174: public void addValue(AbstractChildObject child)
175: throws PopulateException {
176: m_bAllowSave = false;
177: super .addValue(child);
178: }
179:
180: /**
181: * Adds stage to this workflow instance
182: *
183: * @param stage
184: * @param usr
185: * @throws InvalidPropertyValueException
186: */
187: public void addWorkflowStage(WorkflowStageValue stage, User usr)
188: throws PopulateException {
189: try {
190:
191: boolean bRoleValid = false;
192:
193: //super users can do ANYTHING
194: if (AuthorizationValidator.isSuperUser(usr) == true) {
195: bRoleValid = true;
196: } else {
197:
198: List roles = AuthorizationValidator.getUserRoles(usr);
199:
200: List stageRoles = stage.getRoles();
201: if (stageRoles != null && stageRoles.size() > 0) {
202: Iterator iter = stageRoles.iterator();
203:
204: while (iter.hasNext() && bRoleValid == false) {
205: Value roleVal = (Value) iter.next();
206: if (roles.contains(roleVal)) {
207: bRoleValid = true;
208: }
209: }
210: } else {
211: ChildObjectPropertyInstance propInst = (ChildObjectPropertyInstance) stage
212: .getPropertyInstance(WorkflowStageValue.PROPNAME_DEFINITION);
213:
214: WorkflowStageValue defn = (WorkflowStageValue) propInst
215: .getValue();
216:
217: stageRoles = defn.getRoles();
218:
219: Iterator iter = stageRoles.iterator();
220:
221: while (iter.hasNext() && bRoleValid == false) {
222: Value roleVal = (Value) iter.next();
223: if (roles.contains(roleVal)) {
224: bRoleValid = true;
225: }
226: }
227: }
228: }
229:
230: if (bRoleValid == true) {
231: if (isValidStage(stage) == true) {
232:
233: super .addValue(stage);
234:
235: // should probably log during save but this is the
236: //only time we've got the user as well as the stage
237: LogEvent event = new LogEvent();
238: AbstractProfiledObject profObj = getProfiledObject();
239: if (profObj != null
240: && profObj.getId() == AbstractObject.NOTDBSAVED_ID) {
241: AbstractProfiledObject liveObj = (AbstractProfiledObject) profObj
242: .getLiveVersion();
243: if (liveObj != null) {
244: profObj = liveObj;
245: }
246: }
247: event.setEventObject(profObj);
248: event.setLabel("Workflow");
249: event.addAdditionalInfo(stage.getName()
250: + " completed");
251: event.setUser(usr);
252: EventLogController logger = EventLogController
253: .getInstance();
254: logger.logEvent(event);
255:
256: } else {
257: throw new InvalidWorkflowStageException(
258: "Dependency restrictions have not been met");
259: }
260:
261: } else {
262: throw new InvalidWorkflowStageException(
263: "User not allowed to add stage");
264: }
265: } catch (InvalidPropertyInstanceException e) {
266: throw new InvalidPropertyValueException(e
267: .getLocalizedMessage(), e);
268: } catch (DataAccessException e) {
269: throw new InvalidPropertyValueException(e
270: .getLocalizedMessage(), e);
271: } catch (AuthorizationException e) {
272: throw new InvalidPropertyValueException(e
273: .getLocalizedMessage(), e);
274: } catch (LogException e) {
275: throw new InvalidPropertyValueException(e);
276: }
277:
278: }
279:
280: /**
281: * Adds stage to this workflow instance
282: *
283: * @param stage
284: * @param usr
285: * @throws InvalidPropertyValueException
286: */
287: public void removeWorkflowStage(WorkflowStageValue stage, User usr)
288: throws InvalidPropertyValueException {
289: try {
290:
291: boolean bRoleValid = false;
292:
293: //super users can do ANYTHING
294: if (AuthorizationValidator.isSuperUser(usr) == true) {
295: bRoleValid = true;
296: } else {
297:
298: List roles = AuthorizationValidator.getUserRoles(usr);
299:
300: List stageRoles = stage.getRoles();
301: if (stageRoles != null && stageRoles.size() > 0) {
302: Iterator iter = stageRoles.iterator();
303:
304: while (iter.hasNext() && bRoleValid == false) {
305: Value roleVal = (Value) iter.next();
306: if (roles.contains(roleVal)) {
307: bRoleValid = true;
308: }
309: }
310: } else {
311: ChildObjectPropertyInstance propInst = (ChildObjectPropertyInstance) stage
312: .getPropertyInstance(WorkflowStageValue.PROPNAME_DEFINITION);
313:
314: WorkflowStageValue defn = (WorkflowStageValue) propInst
315: .getValue();
316:
317: stageRoles = defn.getRoles();
318:
319: Iterator iter = stageRoles.iterator();
320:
321: while (iter.hasNext() && bRoleValid == false) {
322: Value roleVal = (Value) iter.next();
323: if (roles.contains(roleVal)) {
324: bRoleValid = true;
325: }
326: }
327: }
328: }
329:
330: if (bRoleValid == true) {
331:
332: List vals = this .getValues();
333: EventLogController logger = EventLogController
334: .getInstance();
335:
336: for (Iterator iter = vals.iterator(); iter.hasNext();) {
337: WorkflowStageValue tmpStage = (WorkflowStageValue) iter
338: .next();
339: if (isStageDependant(stage, tmpStage)) {
340: super .removeValue(tmpStage);
341: // should probably log during save but this is the
342: //only time we've got the user as well as the stage
343:
344: LogEvent event = new LogEvent();
345: event.setEventObject(this .getProfiledObject());
346: event.setLabel("Workflow");
347: event.addAdditionalInfo(tmpStage.getName()
348: + " rescinded");
349: event.setUser(usr);
350: logger.logEvent(event);
351: }
352: }
353:
354: super .removeValue(stage);
355:
356: LogEvent event = new LogEvent();
357: event.setEventObject(this .getProfiledObject());
358: event.setLabel("Workflow");
359: event.addAdditionalInfo(stage.getName() + " rescinded");
360: event.setUser(usr);
361: logger.logEvent(event);
362: } else {
363: throw new InvalidWorkflowStageException(
364: "User not allowed to remove stage");
365: }
366: } catch (InvalidPropertyInstanceException e) {
367: throw new InvalidPropertyValueException(e
368: .getLocalizedMessage(), e);
369: } catch (DataAccessException e) {
370: throw new InvalidPropertyValueException(e
371: .getLocalizedMessage(), e);
372: } catch (AuthorizationException e) {
373: throw new InvalidPropertyValueException(e
374: .getLocalizedMessage(), e);
375: } catch (LogException e) {
376: throw new InvalidPropertyValueException(e);
377: }
378:
379: }
380:
381: /**
382: * @param stage
383: * @param tmpStage
384: * @return @throws
385: * DataAccessException
386: */
387: private boolean isStageDependant(WorkflowStageValue primaryStage,
388: WorkflowStageValue dependStage) throws DataAccessException {
389: boolean bIsDependant = false;
390:
391: List depends = dependStage.getDependencies();
392:
393: if (depends.contains(primaryStage)) {
394: bIsDependant = true;
395: } else {
396: for (Iterator iter = depends.iterator(); iter.hasNext();) {
397: WorkflowStageValue tmpStage = (WorkflowStageValue) iter
398: .next();
399: bIsDependant = isStageDependant(primaryStage, tmpStage);
400: }
401: }
402:
403: return bIsDependant;
404: }
405:
406: /* (non-Javadoc)
407: * @see org.openharmonise.rm.metadata.AbstractPropertyInstance#save(org.openharmonise.rm.metadata.Profile)
408: */
409: protected void save(Profile prof) throws ProfileException,
410: EditException {
411:
412: if (m_bAllowSave == false) {
413: throw new EditException(
414: "Invalid value contined in PropertyInstance");
415: }
416:
417: super .save(prof);
418: }
419:
420: /* (non-Javadoc)
421: * @see org.openharmonise.rm.metadata.AbstractPropertyInstance#getValues()
422: */
423: public List getValues() {
424: List result = new ArrayList();
425: List vals = super .getValues();
426:
427: try {
428: //check all current values are valid, in case workflow def has
429: //changed
430: for (Iterator iter = vals.iterator(); iter.hasNext();) {
431: WorkflowStageValue stage = (WorkflowStageValue) iter
432: .next();
433: if (isValidStage(stage) == false) {
434: removeChildReferences(stage);
435: } else {
436: result.add(stage);
437: }
438: }
439: } catch (DataAccessException e) {
440: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
441: } catch (DataStoreException e) {
442: m_logger.log(Level.WARNING, e.getLocalizedMessage(), e);
443: }
444:
445: return result;
446: }
447: }
|