001: package org.drools.audit;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.util.ArrayList;
020: import java.util.Iterator;
021: import java.util.List;
022:
023: import org.drools.EventManager;
024: import org.drools.FactHandle;
025: import org.drools.WorkingMemory;
026: import org.drools.audit.event.ActivationLogEvent;
027: import org.drools.audit.event.ILogEventFilter;
028: import org.drools.audit.event.LogEvent;
029: import org.drools.audit.event.ObjectLogEvent;
030: import org.drools.audit.event.RuleFlowGroupLogEvent;
031: import org.drools.audit.event.RuleFlowLogEvent;
032: import org.drools.common.InternalFactHandle;
033: import org.drools.common.InternalWorkingMemory;
034: import org.drools.event.ActivationCancelledEvent;
035: import org.drools.event.ActivationCreatedEvent;
036: import org.drools.event.AfterActivationFiredEvent;
037: import org.drools.event.AgendaEventListener;
038: import org.drools.event.AgendaGroupPoppedEvent;
039: import org.drools.event.AgendaGroupPushedEvent;
040: import org.drools.event.BeforeActivationFiredEvent;
041: import org.drools.event.ObjectInsertedEvent;
042: import org.drools.event.ObjectUpdatedEvent;
043: import org.drools.event.ObjectRetractedEvent;
044: import org.drools.event.RuleFlowCompletedEvent;
045: import org.drools.event.RuleFlowEventListener;
046: import org.drools.event.RuleFlowGroupActivatedEvent;
047: import org.drools.event.RuleFlowGroupDeactivatedEvent;
048: import org.drools.event.RuleFlowStartedEvent;
049: import org.drools.event.WorkingMemoryEventListener;
050: import org.drools.rule.Declaration;
051: import org.drools.spi.Activation;
052: import org.drools.spi.Tuple;
053:
054: /**
055: * A logger of events generated by a working memory.
056: * It listens to the events generated by the working memory and
057: * creates associated log event (containing a snapshot of the
058: * state of the working event at that time).
059: *
060: * Filters can be used to filter out unwanted events.
061: *
062: * Subclasses of this class should implement the logEventCreated(LogEvent)
063: * method and store this information, like for example log to file
064: * or database.
065: *
066: * @author <a href="mailto:kris_verlaenen@hotmail.com">Kris Verlaenen </a>
067: */
068: public abstract class WorkingMemoryLogger implements
069: WorkingMemoryEventListener, AgendaEventListener,
070: RuleFlowEventListener {
071:
072: private final List filters = new ArrayList();
073: private EventManager eventManager;
074:
075: /**
076: * Creates a new working memory logger for the given working memory.
077: *
078: * @param workingMemory
079: */
080: public WorkingMemoryLogger(final EventManager eventManager) {
081: this .eventManager = eventManager;
082: this .eventManager
083: .addEventListener((WorkingMemoryEventListener) this );
084: this .eventManager.addEventListener((AgendaEventListener) this );
085: this .eventManager
086: .addEventListener((RuleFlowEventListener) this );
087: }
088:
089: /**
090: * This method is invoked every time a new log event is created.
091: * Subclasses should implement this method and store the event,
092: * like for example log to a file or database.
093: *
094: * @param logEvent
095: */
096: public abstract void logEventCreated(LogEvent logEvent);
097:
098: /**
099: * This method is invoked every time a new log event is created.
100: * It filters out unwanted events.
101: *
102: * @param logEvent
103: */
104: private void filterLogEvent(final LogEvent logEvent) {
105: final Iterator iterator = this .filters.iterator();
106: while (iterator.hasNext()) {
107: final ILogEventFilter filter = (ILogEventFilter) iterator
108: .next();
109: // do nothing if one of the filters doesn't accept the event
110: if (!filter.acceptEvent(logEvent)) {
111: return;
112: }
113: }
114: // if all the filters accepted the event, signal the creation
115: // of the event
116: logEventCreated(logEvent);
117: }
118:
119: /**
120: * Adds the given filter to the list of filters for this event log.
121: * A log event must be accepted by all the filters to be entered in
122: * the event log.
123: *
124: * @param filter The filter that should be added.
125: */
126: public void addFilter(final ILogEventFilter filter) {
127: if (filter == null) {
128: throw new NullPointerException();
129: }
130: this .filters.add(filter);
131: }
132:
133: /**
134: * Removes the given filter from the list of filters for this event log.
135: * If the given filter was not a filter of this event log, nothing
136: * happens.
137: *
138: * @param filter The filter that should be removed.
139: */
140: public void removeFilter(final ILogEventFilter filter) {
141: this .filters.remove(filter);
142: }
143:
144: /**
145: * Clears all filters of this event log.
146: */
147: public void clearFilters() {
148: this .filters.clear();
149: }
150:
151: /**
152: * @see org.drools.event.WorkingMemoryEventListener
153: */
154: public void objectInserted(final ObjectInsertedEvent event) {
155: filterLogEvent(new ObjectLogEvent(LogEvent.INSERTED,
156: ((InternalFactHandle) event.getFactHandle()).getId(),
157: event.getObject().toString()));
158: }
159:
160: /**
161: * @see org.drools.event.WorkingMemoryEventListener
162: */
163: public void objectUpdated(final ObjectUpdatedEvent event) {
164: filterLogEvent(new ObjectLogEvent(LogEvent.UPDATED,
165: ((InternalFactHandle) event.getFactHandle()).getId(),
166: event.getObject().toString()));
167: }
168:
169: /**
170: * @see org.drools.event.WorkingMemoryEventListener
171: */
172: public void objectRetracted(final ObjectRetractedEvent event) {
173: filterLogEvent(new ObjectLogEvent(LogEvent.RETRACTED,
174: ((InternalFactHandle) event.getFactHandle()).getId(),
175: event.getOldObject().toString()));
176: }
177:
178: /**
179: * @see org.drools.event.AgendaEventListener
180: */
181: public void activationCreated(final ActivationCreatedEvent event,
182: final WorkingMemory workingMemory) {
183: filterLogEvent(new ActivationLogEvent(
184: LogEvent.ACTIVATION_CREATED, getActivationId(event
185: .getActivation()), event.getActivation()
186: .getRule().getName(), extractDeclarations(event
187: .getActivation(), workingMemory), event
188: .getActivation().getRule().getRuleFlowGroup()));
189: }
190:
191: /**
192: * @see org.drools.event.AgendaEventListener
193: */
194: public void activationCancelled(
195: final ActivationCancelledEvent event,
196: final WorkingMemory workingMemory) {
197: filterLogEvent(new ActivationLogEvent(
198: LogEvent.ACTIVATION_CANCELLED, getActivationId(event
199: .getActivation()), event.getActivation()
200: .getRule().getName(), extractDeclarations(event
201: .getActivation(), workingMemory), event
202: .getActivation().getRule().getRuleFlowGroup()));
203: }
204:
205: /**
206: * @see org.drools.event.AgendaEventListener
207: */
208: public void beforeActivationFired(
209: final BeforeActivationFiredEvent event,
210: final WorkingMemory workingMemory) {
211: filterLogEvent(new ActivationLogEvent(
212: LogEvent.BEFORE_ACTIVATION_FIRE, getActivationId(event
213: .getActivation()), event.getActivation()
214: .getRule().getName(), extractDeclarations(event
215: .getActivation(), workingMemory), event
216: .getActivation().getRule().getRuleFlowGroup()));
217: }
218:
219: /**
220: * @see org.drools.event.AgendaEventListener
221: */
222: public void afterActivationFired(
223: final AfterActivationFiredEvent event,
224: final WorkingMemory workingMemory) {
225: filterLogEvent(new ActivationLogEvent(
226: LogEvent.AFTER_ACTIVATION_FIRE, getActivationId(event
227: .getActivation()), event.getActivation()
228: .getRule().getName(), extractDeclarations(event
229: .getActivation(), workingMemory), event
230: .getActivation().getRule().getRuleFlowGroup()));
231: }
232:
233: /**
234: * Creates a string representation of the declarations of an activation.
235: * This is a list of name-value-pairs for each of the declarations in the
236: * tuple of the activation. The name is the identifier (=name) of the
237: * declaration, and the value is a toString of the value of the
238: * parameter, followed by the id of the fact between parentheses.
239: *
240: * @param activation The activation from which the declarations should be extracted
241: * @return A String represetation of the declarations of the activation.
242: */
243: private String extractDeclarations(final Activation activation,
244: final WorkingMemory workingMemory) {
245: final StringBuffer result = new StringBuffer();
246: final Tuple tuple = activation.getTuple();
247: final Declaration[] declarations = activation.getRule()
248: .getDeclarations();
249: for (int i = 0, length = declarations.length; i < length; i++) {
250: final Declaration declaration = declarations[i];
251: final FactHandle handle = tuple.get(declaration);
252: if (handle instanceof InternalFactHandle) {
253: final InternalFactHandle handleImpl = (InternalFactHandle) handle;
254: if (handleImpl.getId() == -1) {
255: // This handle is now invalid, probably due to an fact retraction
256: continue;
257: }
258: final Object value = declaration.getValue(
259: (InternalWorkingMemory) workingMemory,
260: workingMemory.getObject(handle));
261:
262: result.append(declaration.getIdentifier());
263: result.append("=");
264: if (value == null) {
265: // this should never occur
266: result.append("null");
267: } else {
268: result.append(value);
269: result.append("(");
270: result.append(handleImpl.getId());
271: result.append(")");
272: }
273: }
274: if (i < declarations.length - 1) {
275: result.append("; ");
276: }
277: }
278: return result.toString();
279: }
280:
281: /**
282: * Returns a String that can be used as unique identifier for an
283: * activation. Since the activationId is the same for all assertions
284: * that are created during a single insert, update or retract, the
285: * key of the tuple of the activation is added too (which is a set
286: * of fact handle ids).
287: *
288: * @param activation The activation for which a unique id should be generated
289: * @return A unique id for the activation
290: */
291: private static String getActivationId(final Activation activation) {
292: final StringBuffer result = new StringBuffer(activation
293: .getRule().getName());
294: result.append(" [");
295: final Tuple tuple = activation.getTuple();
296: final FactHandle[] handles = tuple.getFactHandles();
297: for (int i = 0; i < handles.length; i++) {
298: result.append(((InternalFactHandle) handles[i]).getId());
299: if (i < handles.length - 1) {
300: result.append(", ");
301: }
302: }
303: return result.append("]").toString();
304: }
305:
306: public void agendaGroupPopped(final AgendaGroupPoppedEvent event,
307: final WorkingMemory workingMemory) {
308: // we don't audit this yet
309: }
310:
311: public void agendaGroupPushed(final AgendaGroupPushedEvent event,
312: final WorkingMemory workingMemory) {
313: // we don't audit this yet
314: }
315:
316: public void ruleFlowStarted(RuleFlowStartedEvent event,
317: WorkingMemory workingMemory) {
318: filterLogEvent(new RuleFlowLogEvent(
319: LogEvent.RULEFLOW_CREATED,
320: event.getRuleFlowProcessInstance().getProcess().getId(),
321: event.getRuleFlowProcessInstance().getProcess()
322: .getName()));
323: }
324:
325: public void ruleFlowCompleted(RuleFlowCompletedEvent event,
326: WorkingMemory workingMemory) {
327: filterLogEvent(new RuleFlowLogEvent(
328: LogEvent.RULEFLOW_COMPLETED, event
329: .getRuleFlowProcessInstance().getProcess()
330: .getId(), event.getRuleFlowProcessInstance()
331: .getProcess().getName()));
332: }
333:
334: public void ruleFlowGroupActivated(
335: RuleFlowGroupActivatedEvent event,
336: WorkingMemory workingMemory) {
337: filterLogEvent(new RuleFlowGroupLogEvent(
338: LogEvent.RULEFLOW_GROUP_ACTIVATED, event
339: .getRuleFlowGroup().getName(), event
340: .getRuleFlowGroup().size()));
341: }
342:
343: public void ruleFlowGroupDeactivated(
344: RuleFlowGroupDeactivatedEvent event,
345: WorkingMemory workingMemory) {
346: filterLogEvent(new RuleFlowGroupLogEvent(
347: LogEvent.RULEFLOW_GROUP_DEACTIVATED, event
348: .getRuleFlowGroup().getName(), event
349: .getRuleFlowGroup().size()));
350: }
351:
352: }
|