001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.oldboss.framework.bo.impl;
016:
017: import java.util.HashMap;
018: import java.util.Map;
019:
020: import com.metaboss.enterprise.bo.BOException;
021: import com.metaboss.enterprise.bo.BOInvalidOperationForDeletedObjectException;
022: import com.metaboss.enterprise.bo.BOInvalidOperationForObjectBeingEditedException;
023: import com.metaboss.enterprise.bo.BOInvalidOperationForObjectException;
024: import com.metaboss.enterprise.bo.BOInvalidOperationForReadOnlyObjectException;
025: import com.metaboss.enterprise.bo.BONewObjectRequiredException;
026: import com.metaboss.enterprise.bo.BOObjectAlreadyInTransactionException;
027: import com.metaboss.enterprise.bo.BOObjectNotInitialisedException;
028: import com.metaboss.enterprise.bo.BOTransactionRequiredException;
029: import com.metaboss.enterprise.bo.BOUnexpectedProgramConditionException;
030: import com.oldboss.framework.bo.BOObject;
031: import com.oldboss.framework.bo.BOTransaction;
032:
033: /* Base class for all BO Objects */
034: public class BOObjectImpl implements BOObject,
035: BOTransactionImpl.TransactionObject {
036: /* The BO Object can be in one of the following states */
037: private static final int c_StateUnusable = -1;
038: private static final int c_StateNotSetUp = 0;
039: private static final int c_StateExistingReadOnly = 1;
040: private static final int c_StateExistingBeingEdited = 2;
041: private static final int c_StateExistingBeingEditedDeleted = 3;
042: private static final int c_StateExistingDeleted = 4;
043: private static final int c_StateNewBeingEdited = 5;
044: private static final int c_StateNewBeingEditedDeleted = 6;
045: private int m_State = c_StateNotSetUp; /* State variable */
046: private BOTransactionImpl m_Transaction = null; /* Transaction we belong to or null if no tran */
047: private BOObjectImpl mOwnerObject = null;
048: private Map mOwnedObjects = null;
049: private Map mAssociatedObjects = null;
050:
051: /** This method returns globally unique object id. To make it globally unique it has to
052: * incorporate type identifier and instance identifier */
053: public String getObjectId() throws BOException {
054: return toString();
055: }
056:
057: /** Returns list of associated objects, excluding the owner */
058: public BOObjectImpl[] getAssociatedObjects() throws BOException {
059: return mAssociatedObjects != null ? (BOObjectImpl[]) mAssociatedObjects
060: .values().toArray(
061: new BOObjectImpl[mAssociatedObjects.size()])
062: : new BOObjectImpl[0];
063: }
064:
065: /** Returns owner object or null if this object is a top level one */
066: public BOObjectImpl getOwnerObject() throws BOException {
067: return mOwnerObject;
068: }
069:
070: /** Returns list of zero or more owned objects */
071: public BOObjectImpl[] getOwnedObjects() throws BOException {
072: return mOwnedObjects != null ? (BOObjectImpl[]) mOwnedObjects
073: .values().toArray(
074: new BOObjectImpl[mOwnedObjects.size()])
075: : new BOObjectImpl[0];
076: }
077:
078: /** Returns list of zero or more owned objects */
079: private void addOwnedObject(BOObjectImpl pOwnedObject)
080: throws BOException {
081: if (pOwnedObject.mOwnerObject != this )
082: throw new BOUnexpectedProgramConditionException(
083: "Proposed owned object is not setup correctly");
084: if (mOwnedObjects == null)
085: mOwnedObjects = new HashMap();
086: if (mOwnedObjects.containsKey(pOwnedObject.getObjectId()))
087: throw new BOUnexpectedProgramConditionException(
088: "Proposed owned object is already registered with the owner");
089: mOwnedObjects.put(pOwnedObject.getObjectId(), pOwnedObject);
090: }
091:
092: /* Returns true if object is read only */
093: public boolean isReadOnly() throws BOException {
094: checkIfSetUp();
095: return m_State == c_StateExistingReadOnly;
096: }
097:
098: /* Returns true if object is deleted */
099: public boolean isDeleted() throws BOException {
100: checkIfSetUp();
101: return ((m_State == c_StateNewBeingEditedDeleted)
102: || (m_State == c_StateExistingDeleted) || (m_State == c_StateExistingBeingEditedDeleted));
103: }
104:
105: /* Returns true if object is being edited */
106: public boolean isBeingEdited() throws BOException {
107: checkIfSetUp();
108: return (m_State == c_StateNewBeingEdited)
109: || (m_State == c_StateNewBeingEditedDeleted)
110: || (m_State == c_StateExistingBeingEdited)
111: || (m_State == c_StateExistingBeingEditedDeleted);
112: }
113:
114: /* Return true if object is newly created */
115: public boolean isNewlyCreated() throws BOException {
116: checkIfSetUp();
117: return ((m_State == c_StateNewBeingEdited) || (m_State == c_StateNewBeingEditedDeleted));
118: }
119:
120: /* Returns true if object is Existing */
121: public boolean isExisting() throws BOException {
122: checkIfSetUp();
123: return (m_State == c_StateExistingBeingEdited)
124: || (m_State == c_StateExistingBeingEditedDeleted)
125: || (m_State == c_StateExistingDeleted)
126: || (m_State == c_StateExistingReadOnly);
127: }
128:
129: /** Returns true if object is associated with any transaction */
130: public boolean isInTransaction() throws BOException {
131: checkIfSetUp();
132: return m_Transaction != null;
133: }
134:
135: /** This method must be called immediately after BO construction in order
136: * to set it up as newly created object. Object is setup as NewlyCreate / BeingEdited
137: * in this case and therefore transaction is required. This version of the method
138: * assumes that the object is top level and not owned by anyone */
139: public final void setupForNew(BOTransaction pTransaction)
140: throws BOException {
141: setupForNew(pTransaction, null);
142: }
143:
144: /** This method must be called immediately after BO construction in order
145: * to set it up as newly created object. Object is setup as NewlyCreate / BeingEdited
146: * in this case and therefore transaction is required. This version of the method
147: * requires to specify owner object */
148: public final void setupForNew(BOTransaction pTransaction,
149: BOObjectImpl pOwnerObject) throws BOException {
150: /* Check if we have got valid transaction object */
151: if (pTransaction == null)
152: throw new BOTransactionRequiredException();
153: BOTransactionImpl lTransactionImpl = BOTransactionImpl
154: .getBOTransaction(pTransaction);
155: if (lTransactionImpl == null)
156: throw new BOTransactionRequiredException();
157: /* Check if we are in a right state */
158: if (m_State != c_StateNotSetUp)
159: throw new BONewObjectRequiredException(); /* Attempt to set up the object second time */
160: /* Check if we already are part of the transaction */
161: if (m_Transaction != null)
162: throw new BOObjectAlreadyInTransactionException(); /* Must not have belong to transaction already */
163:
164: m_State = c_StateNewBeingEdited;
165: if ((mOwnerObject = pOwnerObject) != null)
166: pOwnerObject.addOwnedObject(this );
167: lTransactionImpl.addTransactionObject(this );
168: try {
169: onCreateNew();
170: } catch (BOException e) {
171: /* Undo the effects of beginEdit() */
172: lTransactionImpl.removeTransactionObject(this );
173: m_State = c_StateNotSetUp;
174: /* Throw same exception further */
175: throw e;
176: }
177: }
178:
179: /* Overridable method. Called when new object being created */
180: protected void onCreateNew() throws BOException {
181: }
182:
183: /**
184: * This method must be called immediately after BO construction in order
185: * to set it up as existing object. Object is setup as Existing / ReadOnly
186: * in this case and therefore transaction is not required
187: */
188: public final void setupForExisting() throws BOException {
189: setupForExisting(null);
190: }
191:
192: /**
193: * This method must be called immediately after BO construction in order
194: * to set it up as existing object. Object is setup as Existing / ReadOnly
195: * in this case and therefore transaction is not required
196: */
197: public final void setupForExisting(BOObjectImpl pOwnerObject)
198: throws BOException {
199: /* Check if we are in a right state */
200: if (m_State != c_StateNotSetUp)
201: throw new BONewObjectRequiredException(); /* Must be newly created object */
202:
203: /* Check if we already are part of the transaction */
204: if (m_Transaction != null)
205: throw new BOObjectAlreadyInTransactionException(); /* Must not have belong to transaction already */
206: m_State = c_StateExistingReadOnly;
207: if ((mOwnerObject = pOwnerObject) != null)
208: pOwnerObject.addOwnedObject(this );
209: }
210:
211: /*
212: * Moves an Existing/ReadOnly BO to the Existing/BeingEdited stage
213: */
214: public final void beginEdit(BOTransaction pTransaction)
215: throws BOException {
216: checkIfSetUp();
217:
218: /* Check if we have got valid transaction object */
219: if (pTransaction == null)
220: throw new BOTransactionRequiredException();
221: BOTransactionImpl lTransactionImpl = BOTransactionImpl
222: .getBOTransaction(pTransaction);
223: if (lTransactionImpl == null)
224: throw new BOTransactionRequiredException();
225:
226: if (isDeleted())
227: throw new BOInvalidOperationForDeletedObjectException();
228:
229: if (isBeingEdited()) {
230: /* Object is already being edited. Let the beginEdit() succeed if transaction is the same */
231: if (pTransaction == m_Transaction)
232: return;
233:
234: throw new BOObjectAlreadyInTransactionException();
235: }
236:
237: m_State = c_StateExistingBeingEdited;
238: lTransactionImpl.addTransactionObject(this );
239: try {
240: onBeginEdit();
241: } catch (BOException e) {
242: /* Undo the effects of beginEdit() */
243: lTransactionImpl.removeTransactionObject(this );
244: m_State = c_StateExistingReadOnly;
245: /* Throw same exception further */
246: throw e;
247: }
248: }
249:
250: /* Overridable method. Called when existing object enters being edited stage */
251: protected void onBeginEdit() throws BOException {
252: }
253:
254: /* Overridable method. Called when particualr object instance is being deleted.
255: * If object can not be deleted - this method is expected to throw exception detailing
256: * why is it so. If this method has returned successfully - onDelete() will be called
257: * eventually to perform actual deletion */
258: protected void testDeletion(BOObjectImpl pObjectToBeDeleted)
259: throws BOException {
260: }
261:
262: /* Overridable method. Called when this instance is being deleted
263: * If this object can not be deleted for some reason this method is expected to throw exception detailing
264: * why is it so. If this method has returned without any exception - the object will be put in the deleted state */
265: protected void onDelete(BOObjectImpl pObjectToBeDeleted)
266: throws BOException {
267: }
268:
269: /*
270: * Marks this object for deletion of the underlying data base record. If object is
271: * already in transaction, and transaction supplied is the same one - than object
272: * is marked for deletion If object is not in transaction yet - attempt is made
273: * to beginEdit() this object and than mark it for deletion
274: */
275: public void delete(BOTransaction pTransaction) throws BOException {
276: checkIfSetUp();
277: /* Check if we have got valid transaction object */
278: if (pTransaction == null)
279: throw new BOTransactionRequiredException();
280: BOTransactionImpl lTransactionImpl = BOTransactionImpl
281: .getBOTransaction(pTransaction);
282: if (lTransactionImpl == null)
283: throw new BOTransactionRequiredException();
284: // Only let read only object through
285: if (isDeleted())
286: throw new BOInvalidOperationForDeletedObjectException();
287: else if (isBeingEdited())
288: throw new BOInvalidOperationForObjectBeingEditedException();
289: else if (!isReadOnly())
290: throw new BOInvalidOperationForObjectException();
291: m_State = c_StateExistingDeleted;
292: lTransactionImpl.addTransactionObject(this );
293: try {
294: // First let owner object know that we are being deleted
295: if (mOwnerObject != null)
296: mOwnerObject.testDeletion(this );
297: // Second let all associated objects know that we are being deleted
298: BOObjectImpl[] lAssociatedObjects = getAssociatedObjects();
299: for (int i = 0; i < lAssociatedObjects.length; i++)
300: lAssociatedObjects[i].testDeletion(this );
301: // Now delete all owned objects
302: BOObjectImpl[] lOwnedObjects = getOwnedObjects();
303: for (int i = 0; i < lOwnedObjects.length; i++)
304: lOwnedObjects[i].delete(pTransaction);
305: // Now delete object from owner
306: if (mOwnerObject != null) {
307: mOwnerObject.beginEdit(pTransaction);
308: mOwnerObject.onDelete(this );
309: }
310: // Now delete object associated objects
311: for (int i = 0; i < lAssociatedObjects.length; i++) {
312: BOObjectImpl lAssociatedObject = lAssociatedObjects[i];
313: lAssociatedObject.beginEdit(pTransaction);
314: lAssociatedObject.onDelete(this );
315: }
316: } catch (BOException e) {
317: /* Undo the effects of deletion */
318: lTransactionImpl.removeTransactionObject(this );
319: m_State = c_StateExistingReadOnly;
320: /* Throw same exception further */
321: throw e;
322: }
323: }
324:
325: /* Encapsulates commit procedure */
326: public void doCommit() throws BOException {
327: checkIfSetUp();
328: /* It only makes sence to commit the object being edited or deleted */
329: if (isDeleted()) {
330: /* Modify internal state of the object */
331: switch (m_State) {
332: case c_StateNewBeingEditedDeleted:
333: onCommitCreationDeletion();
334: m_State = c_StateUnusable;
335: break;
336: case c_StateExistingBeingEditedDeleted:
337: onCommitDeletion();
338: m_State = c_StateUnusable;
339: break;
340: case c_StateExistingDeleted:
341: onCommitDeletion();
342: m_State = c_StateUnusable;
343: break;
344: default:
345: throw new BOUnexpectedProgramConditionException(
346: "Unrecognised state of the object");
347: }
348: } else if (isBeingEdited()) {
349: /* Modify internal state of the object */
350: switch (m_State) {
351: case c_StateNewBeingEdited:
352: onCommitCreation();
353: m_State = c_StateExistingReadOnly;
354: break;
355: case c_StateExistingBeingEdited:
356: onCommitUpdate();
357: m_State = c_StateExistingReadOnly;
358: break;
359: default:
360: throw new BOUnexpectedProgramConditionException(
361: "Unrecognised state of the object");
362: }
363: } else {
364: /* Can not be done when object is readonly */
365: if (isReadOnly()) {
366: throw new BOInvalidOperationForReadOnlyObjectException();
367: }
368: /* Unrecognised state */
369: throw new BOInvalidOperationForObjectException();
370: }
371: }
372:
373: /* Encapsulates commit action by this object */
374: protected void onCommitCreation() throws BOException {
375: /* Do nothing by default */
376: }
377:
378: /* Encapsulates commit action by this object */
379: protected void onCommitCreationDeletion() throws BOException {
380: /* Do nothing by default */
381: }
382:
383: /* Encapsulates commit action by this object */
384: protected void onCommitUpdate() throws BOException {
385: /* Do nothing by default */
386: }
387:
388: /* Encapsulates commit action by this object */
389: protected void onCommitDeletion() throws BOException {
390: /* Do nothing by default */
391: }
392:
393: /* Encapsulates an action after successfull commit */
394: public void onCommitSucceeded() throws BOException {
395: /* Do nothing by default */
396: }
397:
398: /* Encapsulates rollback procedure */
399: public void doRollback() throws BOException {
400: checkIfSetUp();
401: /* It only makes sence to commit the object being edited */
402: if (isBeingEdited()) {
403: /* Modify internal state of the object */
404: switch (m_State) {
405: case c_StateNewBeingEdited:
406: onRollbackCreation();
407: m_State = c_StateUnusable;
408: break;
409: case c_StateNewBeingEditedDeleted:
410: onRollbackCreationDeletion();
411: m_State = c_StateUnusable;
412: break;
413: case c_StateExistingBeingEdited:
414: onRollbackUpdate();
415: m_State = c_StateExistingReadOnly;
416: break;
417: case c_StateExistingBeingEditedDeleted:
418: onRollbackDeletion();
419: m_State = c_StateExistingReadOnly;
420: break;
421: default:
422: /* Unrecognised state */
423: throw new BOException();
424: }
425: } else {
426: /* Can not be done when object is readonly */
427: if (isReadOnly()) {
428: throw new BOInvalidOperationForReadOnlyObjectException();
429: }
430: /* Can not be done when object is deleted */
431: if (isDeleted()) {
432: throw new BOInvalidOperationForDeletedObjectException();
433: }
434: /* Unrecognised state */
435: throw new BOException();
436: }
437: }
438:
439: /* Encapsulates rollback action by this object */
440: protected void onRollbackCreation() throws BOException {
441: /* Do nothing by default */
442: }
443:
444: /* Encapsulates rollback action by this object */
445: protected void onRollbackCreationDeletion() throws BOException {
446: /* Do nothing by default */
447: }
448:
449: /* Encapsulates rollback action by this object */
450: protected void onRollbackUpdate() throws BOException {
451: /* Do nothing by default */
452: }
453:
454: /* Encapsulates rollback action by this object */
455: protected void onRollbackDeletion() throws BOException {
456: /* Do nothing by default */
457: }
458:
459: /* This method returns a transaction object this BO is running with
460: * The reason it is using 'fetch' verb and not 'get' is not to clash
461: * with possible getTransaction() getter on application level*/
462: protected final BOTransaction fetchTransaction() throws BOException {
463: checkIfSetUp();
464: return m_Transaction;
465: }
466:
467: /* This method adds transaction object this BO is running with */
468: public void joinTransaction(BOTransactionImpl pOwnerTransaction)
469: throws BOException {
470: checkIfSetUp();
471: if (m_Transaction != null)
472: throw new BOUnexpectedProgramConditionException();
473: m_Transaction = pOwnerTransaction;
474: }
475:
476: /* This method removes transaction object this BO is running with
477: * by doing that object stops being associated with any transaction
478: * and can be put into another transaction again */
479: public void disjoinTransaction(BOTransactionImpl pOwnerTransaction)
480: throws BOException {
481: checkIfSetUp();
482: if (m_Transaction == pOwnerTransaction)
483: m_Transaction = null;
484: else
485: throw new BOUnexpectedProgramConditionException();
486: }
487:
488: /* Checks if BO is setup and throws exception if it is not set */
489: private void checkIfSetUp() throws BOException {
490: if (m_State == c_StateNotSetUp) {
491: /* Object is not setup and is unusable */
492: throw new BOObjectNotInitialisedException();
493: }
494: }
495: }
|