001: /*--
002:
003: Copyright (C) 2002-2005 Adrian Price.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: adrianprice@sourceforge.net.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Adrian Price (adrianprice@users.sourceforge.net).
027:
028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
038: POSSIBILITY OF SUCH DAMAGE.
039:
040: For more information on OBE, please see
041: <http://obe.sourceforge.net/>.
042:
043: */
044:
045: package org.obe.test.server;
046:
047: import junit.framework.Test;
048: import org.apache.commons.logging.Log;
049: import org.apache.commons.logging.LogFactory;
050: import org.obe.client.api.WMClient;
051: import org.obe.client.api.WMClientFactory;
052: import org.obe.client.api.model.OBEFilter;
053: import org.obe.client.api.model.WorkItemAttributes;
054: import org.obe.test.OBEClientTest;
055: import org.obe.xpdl.model.XPDLProperties;
056: import org.wfmc.audit.WMAAuditEntry;
057: import org.wfmc.audit.WMAAuditEntryIterator;
058: import org.wfmc.audit.WMAEventCode;
059: import org.wfmc.wapi.*;
060:
061: import java.io.IOException;
062: import java.util.HashMap;
063: import java.util.Map;
064:
065: /**
066: * This test checks that audit entries are generated correctly.
067: *
068: * @author Adrian Price
069: */
070: public class AuditTest extends OBEClientTest {
071: private static final Log _logger = LogFactory
072: .getLog(AuditTest.class);
073: private static final String[] _tests = { "testConnect",
074: "testAuditing", "testDisconnect" };
075: // TODO: test domainId, roleId and userId.
076: private static final String TEST_PACKAGE = "/org/obe/test/server/AuditTest.xpdl";
077: private static final String ORDER_BY = "AUDITENTRYID ASC";
078: private static final String AUDIT_WF_1 = "audit-wf-1";
079: private static final String AUDIT_WF_2 = "audit-wf-2";
080: private static final String[][] _expectedEntries = {
081: // processDefinitionId, activityDefinitionId, processState, eventCode,
082: // initProcInstId, curProcInstId, actInstId
083: { AUDIT_WF_1, null, null, "54", null, null, null },
084: { AUDIT_WF_1, null, null, "54", null, null, null },
085: { AUDIT_WF_1, null, "2", "2", "${audit-wf-1}",
086: "${audit-wf-1}", null },
087: { AUDIT_WF_1, "a-1", "2", "12", "${audit-wf-1}",
088: "${audit-wf-1}", "${a-1}" },
089: { AUDIT_WF_1, "a-1", "2", "17", "${audit-wf-1}",
090: "${audit-wf-1}", "${a-1}" },
091: { AUDIT_WF_1, "a-1", "2", "18", "${audit-wf-1}",
092: "${audit-wf-1}", "${a-1}" },
093: { AUDIT_WF_1, "a-1", "2", "13", "${audit-wf-1}",
094: "${audit-wf-1}", "${a-1}" },
095: { AUDIT_WF_1, "a-2", "2", "12", "${audit-wf-1}",
096: "${audit-wf-1}", "${a-2}" },
097: { AUDIT_WF_2, null, "2", "2", "${audit-wf-1}",
098: "${audit-wf-2}", null },
099: { AUDIT_WF_2, "a-3", "2", "12", "${audit-wf-1}",
100: "${audit-wf-2}", "${a-3}" },
101: { AUDIT_WF_1, "a-2", "2", "12", "${audit-wf-1}",
102: "${audit-wf-1}", "${a-2}" },
103: { AUDIT_WF_2, null, "1", "3", "${audit-wf-1}",
104: "${audit-wf-2}", null },
105: { AUDIT_WF_2, "a-3", "1", "12", "${audit-wf-1}",
106: "${audit-wf-2}", "${a-3}" },
107: { AUDIT_WF_1, "a-2", "2", "12", "${audit-wf-1}",
108: "${audit-wf-1}", "${a-2}" },
109: { AUDIT_WF_2, null, "2", "3", "${audit-wf-1}",
110: "${audit-wf-2}", null },
111: { AUDIT_WF_2, "a-3", "2", "12", "${audit-wf-1}",
112: "${audit-wf-2}", "${a-3}" },
113: { AUDIT_WF_2, "a-3", "2", "13", "${audit-wf-1}",
114: "${audit-wf-2}", "${a-3}" },
115: { AUDIT_WF_2, "a-4", "2", "12", "${audit-wf-1}",
116: "${audit-wf-2}", "${a-4}" },
117: { AUDIT_WF_2, "a-4", "2", "13", "${audit-wf-1}",
118: "${audit-wf-2}", "${a-4}" },
119: { AUDIT_WF_2, null, "5", "4", "${audit-wf-1}",
120: "${audit-wf-2}", null },
121: { AUDIT_WF_1, "a-2", "2", "13", "${audit-wf-1}",
122: "${audit-wf-1}", "${a-2}" },
123: { AUDIT_WF_1, null, "5", "4", "${audit-wf-1}",
124: "${audit-wf-1}", null } };
125: Map _idMap = new HashMap();
126:
127: public static Test suite() {
128: return junitSuite(AuditTest.class, _tests);
129: }
130:
131: public AuditTest(String string) {
132: super (string, WMClientFactory.RMI);
133: }
134:
135: public Log getLogger() {
136: return _logger;
137: }
138:
139: public void testConnect() throws WMWorkflowException {
140: connect();
141: }
142:
143: public void testAuditing() throws IOException, WMWorkflowException {
144: // Purge audit history
145: _client.deleteAuditEntries(null);
146: log("Cleared audit log");
147:
148: // Import the workflow definition if it does not already exist.
149: WMFilter filter = new WMFilter(
150: XPDLProperties.PROCESS_DEFINITION_ID, WMFilter.EQ,
151: AUDIT_WF_1);
152: WMIterator iter = _client.listProcessDefinitions(filter, true);
153: if (iter.getCount() == 1) {
154: log("Found workflow, ID=" + AUDIT_WF_1);
155: } else {
156: String pkgContent = readFile(TEST_PACKAGE);
157: _client.createPackage(pkgContent, WMClient.XPDL);
158: log("Imported workflow, ID=" + AUDIT_WF_1);
159: }
160:
161: // Toggle the process definition state to generate audit entries.
162: _client.changeProcessDefinitionState(AUDIT_WF_1,
163: WMProcessDefinitionState.DISABLED);
164: _client.changeProcessDefinitionState(AUDIT_WF_1,
165: WMProcessDefinitionState.ENABLED);
166: log("Toggled state for process definition ID=" + AUDIT_WF_1);
167:
168: // Create and start the test process, should generate audit entries.
169: String procInstId1 = _client.createProcessInstance(AUDIT_WF_1,
170: "Audit Test " + System.currentTimeMillis());
171: _idMap.put("${audit-wf-1}", procInstId1);
172: log("Created test process instance, ID=" + procInstId1);
173: _client.startProcess(procInstId1);
174: log("Started test process instance, ID=" + procInstId1);
175:
176: // Find instance of activity a-1, so we have its instance ID.
177: findActivityInstance(procInstId1, "a-1");
178:
179: // Find the work item for a-1 / testuser1
180: filter = new WMFilter(WorkItemAttributes.PROCESS_INSTANCE_ID,
181: WMFilter.EQ, procInstId1);
182: WMWorkItemIterator wii = _client.listWorkItems(filter, false);
183: assertEquals("Failed to find work item for activity a-1;", 1,
184: wii.getCount());
185: WMWorkItem workItem1 = wii.tsNext();
186: String workItemId1 = workItem1.getId();
187: log("Found work item for activity a-1, ID=" + workItemId1);
188:
189: // Mark the a-1 work item complete.
190: _client.completeWorkItem(procInstId1, workItemId1);
191: log("Completed work item for activity a-1, ID=" + workItemId1);
192:
193: // Find instance of activity a-2.
194: WMActivityInstance ai = findActivityInstance(procInstId1, "a-2");
195: String actInstId2 = ai.getId();
196:
197: // Suspend and resume activity a-2; effect should cascade into subflow,
198: // generating multiple audit entries.
199: _client.changeActivityInstanceState(procInstId1, actInstId2,
200: WMActivityInstanceState.OPEN_SUSPENDED);
201: _client.changeActivityInstanceState(procInstId1, actInstId2,
202: WMActivityInstanceState.OPEN_RUNNING);
203: log("Toggled state of activity a-2 instance, ID=" + actInstId2);
204:
205: // Find the subflow instance.
206: filter = new WMFilter("parentActivityInstanceId = "
207: + actInstId2);
208: WMProcessInstanceIterator pii = _client.listProcessInstances(
209: filter, false);
210: assertEquals(
211: "Failed to find instance of subflow " + AUDIT_WF_2, 1,
212: pii.getCount());
213: WMProcessInstance pi = pii.tsNext();
214: String procInstId2 = pi.getId();
215: _idMap.put("${audit-wf-2}", procInstId2);
216: log("Found instance of subflow " + AUDIT_WF_2 + ", ID="
217: + procInstId2);
218:
219: // Find instance of activity a-3.
220: ai = findActivityInstance(procInstId2, "a-3");
221: String actInstId3 = ai.getId();
222:
223: // Mark the activity complete.
224: _client.changeActivityInstanceState(procInstId2, actInstId3,
225: WMActivityInstanceState.CLOSED_COMPLETED);
226: log("Completed activity a-1 instance, ID=" + actInstId3);
227:
228: // Find instance of activity a-4, so we have its instance ID.
229: findActivityInstance(procInstId2, "a-4");
230:
231: // Check that all audit log entries are present.
232: WMAAuditEntryIterator aei = _client
233: .listAuditEntries(new OBEFilter(null, ORDER_BY, 0, 0));
234: log("Found " + aei.getCount() + " audit entries:");
235: assertEquals("Incorrect audit entry count;",
236: _expectedEntries.length, aei.getCount());
237:
238: // Verify the correctness of each audit entry.
239: for (int j = 0; aei.hasNext(); j++) {
240: WMAAuditEntry entry = aei.tsNext();
241: log(entry);
242:
243: // Check the audit entry fields.
244: String prefix = "Incorrect audit entry at index " + j
245: + ". ";
246: assertEquals(prefix + "processDefinitionId: ",
247: _expectedEntries[j][0], entry
248: .getProcessDefinitionId());
249: assertEquals(prefix + "activityDefinitionId: ",
250: _expectedEntries[j][1], entry
251: .getActivityDefinitionId());
252: assertEquals(
253: prefix + "processState: ",
254: _expectedEntries[j][2] == null ? null
255: : WMProcessInstanceState
256: .valueOf(
257: Integer
258: .parseInt(_expectedEntries[j][2]))
259: .toString(), entry
260: .getProcessState());
261: assertEquals(prefix + "eventCode: ", WMAEventCode
262: .valueOf(Integer.parseInt(_expectedEntries[j][3])),
263: entry.getEventCode());
264: assertEquals(prefix + "initialProcessInstanceId: ",
265: getInstanceId(j, 4), entry
266: .getInitialProcessInstanceId());
267: assertEquals(prefix + "currentProcessInstanceId: ",
268: getInstanceId(j, 5), entry
269: .getCurrentProcessInstanceId());
270: assertEquals(prefix + "activityInstanceId: ",
271: getInstanceId(j, 6), entry.getActivityInstanceId());
272: }
273: log("Verified " + _expectedEntries.length + " audit entries");
274: }
275:
276: public void testDisconnect() throws WMWorkflowException {
277: disconnect();
278: }
279:
280: private String getInstanceId(int row, int col) {
281: String key = _expectedEntries[row][col];
282: return key == null ? null : (String) _idMap.get(key);
283: }
284:
285: private WMActivityInstance findActivityInstance(String procInstId,
286: String actDefId) throws WMWorkflowException {
287:
288: WMFilter filter = new WMFilter("processInstanceId = "
289: + procInstId + " AND activityDefinitionId = '"
290: + actDefId + '\'');
291: WMActivityInstanceIterator aii = _client.listActivityInstances(
292: filter, false);
293: assertEquals("Failed to find instance of activity " + actDefId
294: + ';', 1, aii.getCount());
295: WMActivityInstance ai = aii.tsNext();
296: String id = ai.getId();
297: _idMap.put("${" + actDefId + '}', id);
298: log("Found instance of activity " + actDefId + ", ID=" + id);
299: return ai;
300: }
301: }
|