001: /*
002: * Bossa Workflow System
003: *
004: * $Id: Historian.java,v 1.10 2004/03/19 19:09:26 gdvieira Exp $
005: *
006: * Copyright (C) 2003,2004 OpenBR Sistemas S/C Ltda.
007: *
008: * This file is part of Bossa.
009: *
010: * Bossa is free software; you can redistribute it and/or modify it
011: * under the terms of version 2 of the GNU General Public License as
012: * published by the Free Software Foundation.
013: *
014: * This program is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * General Public License for more details.
018: *
019: * You should have received a copy of the GNU General Public
020: * License along with this program; if not, write to the
021: * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
022: * Boston, MA 02111-1307, USA.
023: */
024:
025: package com.bigbross.bossa.history;
026:
027: import java.io.Serializable;
028: import java.util.ArrayList;
029: import java.util.Collections;
030: import java.util.Date;
031: import java.util.List;
032:
033: import com.bigbross.bossa.Bossa;
034: import com.bigbross.bossa.BossaException;
035: import com.bigbross.bossa.io.DataTransferException;
036: import com.bigbross.bossa.io.EventsXMLHelper;
037: import com.bigbross.bossa.notify.Event;
038: import com.bigbross.bossa.resource.ResourceEvents;
039: import com.bigbross.bossa.wfnet.WFNetEvents;
040:
041: /**
042: * This class keeps the historical records of a Bossa engine. <p>
043: *
044: * @author <a href="http://www.bigbross.com">BigBross Team</a>
045: */
046: public class Historian implements Serializable {
047:
048: private Bossa engine;
049:
050: private ArrayList history;
051:
052: /**
053: * Creates a new empty historian. <p>
054: *
055: * @param engine the bossa engine this historian is part.
056: */
057: public Historian(Bossa engine) {
058: this .engine = engine;
059: this .history = new ArrayList();
060: }
061:
062: /**
063: * Returns the bossa engine this historian is part. <p>
064: *
065: * @return the bossa engine this historian is part.
066: */
067: Bossa getBossa() {
068: return engine;
069: }
070:
071: /**
072: * Finds the place in history where an event happening in the provided time
073: * would appear. <p>
074: *
075: * @param time the time.
076: * @return the position the event would appear.
077: */
078: private int findPlaceInHistory(Date time) {
079: Event proxy = new Event(null, -1, null, time);
080: int place = Collections.binarySearch(history, proxy);
081: if (place < 0) {
082: place = -(place + 1);
083: }
084: return place;
085: }
086:
087: /**
088: * Filters the events inside a time range by case, case type and
089: * resource. The time range will be computed including the start date
090: * and excluding the end date. <p>
091: *
092: * @param start the start date.
093: * @param end the end date.
094: * @return the filtered events.
095: */
096: private List filterHistory(Date start, Date end, String caseTypeId,
097: int caseId, String resourceId) {
098: int startPosition, endPosition;
099: startPosition = start == null ? 0 : findPlaceInHistory(start);
100: endPosition = end == null ? history.size()
101: : findPlaceInHistory(end);
102: List events = new ArrayList(Math.abs(endPosition
103: - startPosition));
104: for (int i = startPosition; i < endPosition; i++) {
105: Event event = (Event) history.get(i);
106: if (caseTypeId != null && !hasCaseType(event, caseTypeId)) {
107: continue;
108: }
109: if (caseId != -1 && caseTypeId != null
110: && !hasCase(event, caseId)) {
111: continue;
112: }
113: if (resourceId != null && !hasResource(event, resourceId)) {
114: continue;
115: }
116: events.add(event);
117: }
118: return events;
119: }
120:
121: /**
122: * Indicates if an event was related to a case type. <p>
123: *
124: * @param event the event.
125: * @param caseTypeId the id of the case type.
126: * @return <code>true</code> if the case type is present in the event;
127: * <code>false</code> otherwise.
128: */
129: private boolean hasCaseType(Event event, String caseTypeId) {
130: if (event.getType() == Event.WFNET_EVENT) {
131: return caseTypeId.equals(event.getAttributes().get(
132: WFNetEvents.ATTRIB_CASE_TYPE_ID));
133: }
134: return false;
135: }
136:
137: /**
138: * Indicates if an event was related to a case. <p>
139: *
140: * @param event the event.
141: * @param caseId the id of the case.
142: * @return <code>true</code> if the case is present in the event;
143: * <code>false</code> otherwise.
144: */
145: private boolean hasCase(Event event, int caseId) {
146: if (event.getType() == Event.WFNET_EVENT) {
147: return Integer.toString(caseId).equals(
148: event.getAttributes().get(
149: WFNetEvents.ATTRIB_CASE_ID));
150: }
151: return false;
152: }
153:
154: /**
155: * Indicates if an event was related to a resource. <p>
156: *
157: * @param event the event.
158: * @param resourceId the resource id.
159: * @return <code>true</code> if the resource is present in the event;
160: * <code>false</code> otherwise.
161: */
162: private boolean hasResource(Event event, String resourceId) {
163: if (event.getType() == Event.RESOURCE_EVENT) {
164: return resourceId.equals(event.getAttributes().get(
165: ResourceEvents.ATTRIB_HOST_RESOURCE_ID))
166: || resourceId.equals(event.getAttributes().get(
167: ResourceEvents.ATTRIB_RESOURCE_ID));
168: }
169: if (event.getType() == Event.WFNET_EVENT) {
170: return resourceId.equals(event.getAttributes().get(
171: WFNetEvents.ATTRIB_RESOURCE_ID));
172: }
173: return false;
174: }
175:
176: /**
177: * Return all events that took place until now. <p>
178: *
179: * @return all events that took place until now.
180: */
181: public List getHistory() {
182: return filterHistory(null, null, null, -1, null);
183: }
184:
185: /**
186: * Returns the events that took place after the start date, inclusive,
187: * until now. <p>
188: *
189: * @param start the start date.
190: * @return the events that took place after the start date until now.
191: */
192: public List getHistory(Date start) {
193: return filterHistory(start, null, null, -1, null);
194: }
195:
196: /**
197: * Returns the events that took place between the start date and
198: * the end date, including the start date and excluding the end date. <p>
199: *
200: * @param start the start date.
201: * @param end the end date.
202: * @return the events that took place between the two dates.
203: */
204: public List getHistory(Date start, Date end) {
205: return filterHistory(start, end, null, -1, null);
206: }
207:
208: /**
209: * Return all events that took place until now and are related to a
210: * case type. <p>
211: *
212: * @param caseTypeId the id of the case type.
213: * @return all events that took place until now related to the case type.
214: */
215: public List getCaseTypeHistory(String caseTypeId) {
216: return filterHistory(null, null, caseTypeId, -1, null);
217: }
218:
219: /**
220: * Returns the events that took place after the start date, inclusive,
221: * until now and are related to a case type. <p>
222: *
223: * @param start the start date.
224: * @param caseTypeId the id of the case type.
225: * @return the events that took place after the start date until now
226: * related to the case type.
227: */
228: public List getCaseTypeHistory(Date start, String caseTypeId) {
229: return filterHistory(start, null, caseTypeId, -1, null);
230: }
231:
232: /**
233: * Returns the events that took place between the start date and the
234: * end date, including the start date and excluding the end date, and
235: * are related to a case type. <p>
236: *
237: * @param start the start date.
238: * @param end the end date.
239: * @param caseTypeId the id of the case type.
240: * @return the events that took place between the two dates, related to
241: * the case type.
242: */
243: public List getCaseTypeHistory(Date start, Date end,
244: String caseTypeId) {
245: return filterHistory(start, end, caseTypeId, -1, null);
246: }
247:
248: /**
249: * Return all events that took place until now and are related to a
250: * case. <p>
251: *
252: * @param caseTypeId the id of the case type.
253: * @param caseId the id of the case.
254: * @return all events that took place util now related to the case.
255: */
256: public List getCaseHistory(String caseTypeId, int caseId) {
257: return filterHistory(null, null, caseTypeId, caseId, null);
258: }
259:
260: /**
261: * Returns the events that took place after the start date, inclusive,
262: * until now and are related to a case. <p>
263: *
264: * @param start the start date.
265: * @param caseTypeId the id of the case type.
266: * @param caseId the id of the case.
267: * @return the events that took place after the start date until now
268: * related to the case.
269: */
270: public List getCaseHistory(Date start, String caseTypeId, int caseId) {
271: return filterHistory(start, null, caseTypeId, caseId, null);
272: }
273:
274: /**
275: * Returns the events that took place between the start date and the
276: * end date, including the start date and excluding the end date, and
277: * are related to a case. <p>
278: *
279: * @param start the start date.
280: * @param end the end date.
281: * @param caseTypeId the id of the case type.
282: * @param caseId the id of the case.
283: * @return the events that took place between the two dates, related
284: * to the case.
285: */
286: public List getCaseHistory(Date start, Date end, String caseTypeId,
287: int caseId) {
288: return filterHistory(start, end, caseTypeId, caseId, null);
289: }
290:
291: /**
292: * Return all events that took place until now and are related to a
293: * resource. <p>
294: *
295: * @param resourceId the id of the resource.
296: * @return all events that took place until now related to the resource.
297: */
298: public List getResourceHistory(String resourceId) {
299: return filterHistory(null, null, null, -1, resourceId);
300: }
301:
302: /**
303: * Returns the events that took place after the start date, inclusive,
304: * until now and are related to a resource. <p>
305: *
306: * @param start the start date.
307: * @param resourceId the id of the resource.
308: * @return the events that took place after the start date until now
309: * related to the resource.
310: */
311: public List getResourceHistory(Date start, String resourceId) {
312: return filterHistory(start, null, null, -1, resourceId);
313: }
314:
315: /**
316: * Returns the events that took place between the start date and the end
317: * date, including the start date and excluding the end date, and
318: * are related to a resource. <p>
319: *
320: * @param start the start date.
321: * @param end the end date.
322: * @param resourceId the id of the resource.
323: * @return the events that took place between the two dates, related to
324: * the resource.
325: */
326: public List getResourceHistory(Date start, Date end,
327: String resourceId) {
328: return filterHistory(start, end, null, -1, resourceId);
329: }
330:
331: /**
332: * Adds a new event to the history. <p>
333: *
334: * @param event the new event.
335: */
336: void newEvent(Event event) {
337: /*
338: * Let's see if the new event happened before the last event.
339: * I'm not sure this can happen, but it won't hurt to deal with it.
340: */
341: int last = history.size() - 1;
342: while (last >= 0 && event.compareTo(history.get(last)) < 0) {
343: last--;
344: }
345: history.add(last + 1, event);
346: }
347:
348: /**
349: * Removes from history all events that took place before the provided
350: * date. Events that happened exactly at the provided date are not
351: * removed. <p>
352: *
353: * @param end the limit date for event removal.
354: * @exception PersistenceException if an error occours when making the
355: * execution of this method persistent.
356: */
357: public void purgeHistory(Date end) throws BossaException {
358: HistorianTransaction purgeTransaction = new PurgeHistory(end);
359: getBossa().execute(purgeTransaction);
360: }
361:
362: /**
363: * Removes from history all events that took place before the provided
364: * date. Events that happened exactly at the provided date are not
365: * removed. <p>
366: *
367: * This method does not create a transaction in the prevalent system. The
368: * execution of this method will not be persistent unless it is called
369: * inside an appropriate transaction. <p>
370: *
371: * @param end the limit date for event removal.
372: */
373: void purgeHistoryImpl(Date end) {
374: history.subList(0, findPlaceInHistory(end)).clear();
375: }
376:
377: /**
378: * Exports to a XML file all events that took place before the provided
379: * date. Events that happened exactly at the provided date are not
380: * exported. This method complements the <code>purgeHistory</code> method,
381: * providing a way to save events before purging them. <p>
382: *
383: * The format of the exported file is the same used by the
384: * <code>EventsXMLHelper</code> class. This class may also be used to
385: * export any event list obtained from the other historian methods. <p>
386: *
387: * @param end the limit date for event removal.
388: * @param file the name of the file.
389: * @see Historian#purgeHistory(Date)
390: * @see com.bigbross.bossa.io.EventsXMLHelper
391: */
392: public void exportHistory(Date end, String file)
393: throws DataTransferException {
394: EventsXMLHelper.export(
395: filterHistory(null, end, null, -1, null), file);
396: }
397: }
|