001: /*
002: * This software is OSI Certified Open Source Software.
003: * OSI Certified is a certification mark of the Open Source Initiative. The
004: * license (Mozilla version 1.0) can be read at the MMBase site. See
005: * http://www.MMBase.org/license
006: */
007: package org.mmbase.core.event;
008:
009: import java.io.*;
010: import java.util.*;
011:
012: import org.mmbase.util.HashCodeUtil;
013: import org.mmbase.cache.Cache;
014: import org.mmbase.cache.CacheManager;
015: import org.mmbase.module.core.MMBase;
016: import org.mmbase.util.logging.Logger;
017: import org.mmbase.util.logging.Logging;
018:
019: /**
020: * This class communicates a node event. in case of a change event, it contains
021: * a map of changed values, mapped to their field's name, as well as the
022: * previous values of the changed fields.
023: *
024: * @author Ernst Bunders
025: * @since MMBase-1.8
026: * @version $Id: NodeEvent.java,v 1.36 2008/02/07 16:44:56 michiel Exp $
027: */
028: public class NodeEvent extends Event {
029: private static final Logger log = Logging
030: .getLoggerInstance(NodeEvent.class);
031:
032: private static final long serialVersionUID = 1L;
033:
034: /**
035: * Event type speicfic for MMBase nodes.
036: */
037: public static final int TYPE_RELATION_CHANGE = 3;
038:
039: private final int nodeNumber;
040: private String builderName;
041:
042: private final Map<String, Object> oldValues;
043: private final Map<String, Object> newValues;
044:
045: /**
046: *@param machineName (MMBase) name of the server
047: *@param builderName name of builder of node event is about
048: *@param oldValues map with fields and their values that have been changed by the event
049: *@param newValues map with new values of changed fields
050: *@param eventType the type of event
051: **/
052: public NodeEvent(String machineName, String builderName,
053: int nodeNumber, Map<String, Object> oldValues,
054: Map<String, Object> newValues, int eventType) {
055: super (machineName, eventType);
056: this .builderName = builderName;
057: this .nodeNumber = nodeNumber;
058: this .oldValues = oldValues == null ? Collections.emptyMap()
059: : Collections.unmodifiableMap(new HashMap(oldValues));
060: this .newValues = newValues == null ? Collections.emptyMap()
061: : Collections.unmodifiableMap(new HashMap(newValues));
062: }
063:
064: /**
065: * @param fieldName the field you want to get the old value of
066: * @return an Object containing the old value (in case of change event), or
067: * null if the fieldName was not found in the old value list
068: */
069: public final Object getOldValue(String fieldName) {
070: return oldValues.get(fieldName);
071: }
072:
073: /**
074: * @return a set containing the names of the fields that have changed
075: */
076: public final Set<String> getChangedFields() {
077: switch (getType()) {
078: case TYPE_NEW:
079: return newValues.keySet();
080: case TYPE_CHANGE:
081: //for changed both old and new values are good (similar keys)
082: return newValues.keySet();
083: case TYPE_DELETE:
084: return oldValues.keySet();
085: default:
086: return Collections.emptySet();
087: }
088: }
089:
090: /**
091: * @param fieldName the field you want the new value of (in case of change
092: * event), or null if the fieldName was not found in the new value
093: * list
094: * @return the new value of the field
095: */
096: public final Object getNewValue(String fieldName) {
097: return newValues.get(fieldName);
098: }
099:
100: /**
101: * @return Returns the builderName.
102: */
103: public final String getBuilderName() {
104: return builderName;
105: }
106:
107: /**
108: * @return Returns the nodeNumber.
109: */
110: public final int getNodeNumber() {
111: return nodeNumber;
112: }
113:
114: public String toString() {
115: String changedFields = "";
116: for (Object element : getChangedFields()) {
117: changedFields = changedFields + (String) element + ",";
118: }
119: return "Node event: '" + getEventTypeGuiName(eventType)
120: + "', node: " + nodeNumber + ", nodetype: "
121: + builderName + ", oldValues: " + oldValues
122: + ", newValues: " + newValues + "changedFields: "
123: + getChangedFields();
124: }
125:
126: protected static String getEventTypeGuiName(int eventType) {
127: switch (eventType) {
128: case Event.TYPE_CHANGE:
129: return "node changed";
130: case Event.TYPE_DELETE:
131: return "node deleted";
132: case Event.TYPE_NEW:
133: return "new node";
134: case NodeEvent.TYPE_RELATION_CHANGE:
135: return "relation changed";
136: default:
137: throw new IllegalArgumentException("HELP! event of type "
138: + eventType + " is unknown. This should not happen");
139: }
140: }
141:
142: /**
143: * I think this method is not needed.
144: * @deprecated
145: */
146: /*
147: public NodeEvent clone(String builderName) {
148: NodeEvent clone = (NodeEvent) super.clone();
149: clone.builderName = builderName;
150: return clone;
151: }
152: */
153:
154: /**
155: * For conveneance: conversion of the new event type indication to the old
156: * style
157: *
158: * @param eventType must be c,d,n or r
159: * @return A String describing the type of an event. (like "c" (change), "d" (delete), "n" (new), or "r" (relation change))
160: */
161: public static String newTypeToOldType(int eventType) {
162: switch (eventType) {
163: case Event.TYPE_CHANGE:
164: return "c";
165: case Event.TYPE_DELETE:
166: return "d";
167: case Event.TYPE_NEW:
168: return "n";
169: case NodeEvent.TYPE_RELATION_CHANGE:
170: return "r";
171: default:
172: throw new IllegalArgumentException("HELP! event of type "
173: + eventType + " is unknown. This should not happen");
174: }
175: }
176:
177: /**
178: * For conveneance: conversion of the old event type indication to the new
179: * style
180: *
181: * @param eventType
182: */
183: public static int oldTypeToNewType(String eventType) {
184: if (eventType.length() > 1) {
185: throw new IllegalArgumentException(
186: "HELP! event of type '"
187: + eventType
188: + "' is unknown. This should not happen. (length = "
189: + eventType.length() + ")");
190: }
191: switch (eventType.charAt(0)) {
192: case 'c':
193: return Event.TYPE_CHANGE;
194: case 'd':
195: return Event.TYPE_DELETE;
196: case 'n':
197: return Event.TYPE_NEW;
198: case 'r':
199: return NodeEvent.TYPE_RELATION_CHANGE;
200: default:
201: throw new IllegalArgumentException("HELP! event of type "
202: + eventType + " is unknown. This should not happen");
203: }
204: }
205:
206: /**
207: * utility method: check if a certain field has changed
208: * @param fieldName
209: * @return true if the field of given name is among the changed fields
210: */
211: public boolean hasChanged(String fieldName) {
212: return oldValues.keySet().contains(fieldName)
213: || newValues.keySet().contains(fieldName);
214: }
215:
216: public int hashCode() {
217: int result = 0;
218: result = HashCodeUtil.hashCode(result, eventType);
219: result = HashCodeUtil.hashCode(result, nodeNumber);
220: result = HashCodeUtil.hashCode(result, builderName);
221: return result;
222:
223: }
224:
225: public boolean equals(Object o) {
226: if (o instanceof NodeEvent) {
227: NodeEvent ne = (NodeEvent) o;
228: return eventType == ne.eventType
229: && nodeNumber == ne.nodeNumber
230: && builderName.equals(ne.builderName);
231: } else {
232: return false;
233: }
234: }
235:
236: /**
237: * old values can be different things.
238: * <ul>
239: * <li>if the event type is 'new' this collection is empty.
240: * <li>if the event type is 'changed' this collection contains the old values of the changed fields.
241: * <li>if the event type is 'delete' this collection contains all the values of the node to be deleted.
242: * </ul>
243: * @return a map where the key is a fieldname and the value the field's value
244: */
245: public final Map<String, Object> getOldValues() {
246: return oldValues;
247: }
248:
249: /**
250: * new values can be different things.
251: * <ul>
252: * <li>if the event type is 'new' this collection contains all the fields of the node.
253: * <li>if the event type is 'changed' this collection contains the new values of the changed fields.
254: * <li>if the event type is 'delete' this collection is empty.
255: * </ul>
256: * @return a map where the key is a fieldname and the value the field's value
257: */
258: public final Map<String, Object> getNewValues() {
259: return newValues;
260: }
261:
262: private void readObject(ObjectInputStream in) throws IOException,
263: ClassNotFoundException {
264: in.defaultReadObject();
265: log.debug("deserialized node event for " + nodeNumber);
266: try {
267: int otype = MMBase.getMMBase().getTypeDef().getIntValue(
268: builderName);
269: if (otype != -1) {
270: Cache<Integer, Integer> typeCache = CacheManager
271: .getCache("TypeCache");
272: if (typeCache != null) {
273: Integer cachedType = typeCache.get(nodeNumber);
274: if (cachedType == null) {
275: log.debug("Putting in type cache " + nodeNumber
276: + " -> " + otype);
277: typeCache.put(nodeNumber, otype);
278: } else {
279: if (otype == cachedType.intValue()) {
280: log.debug("Type already cached");
281: } else {
282: log
283: .warn("Type in event not the same as in cache "
284: + otype
285: + " != "
286: + cachedType);
287: }
288: }
289: } else {
290: log.service("No typecache?");
291: }
292: } else {
293: log.service("Builder '" + builderName + "' from "
294: + nodeNumber + " not found. Originating from "
295: + getMachine());
296: }
297: } catch (Exception e) {
298: log.error(e);
299: }
300:
301: }
302:
303: public static void main(String[] args) {
304: //test serializable
305: Map<String, Object> oldv = new HashMap<String, Object>(), newv = new HashMap<String, Object>();
306: oldv.put("een", "veen");
307: oldv.put("twee", "vtwee");
308: newv.putAll(oldv);
309:
310: NodeEvent event = new NodeEvent("local", "builder", 0, oldv,
311: newv, Event.TYPE_CHANGE);
312: System.out.println("event 1: " + event.toString());
313:
314: }
315:
316: }
|