KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > object > change > TCChangeBufferImpl


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.object.change;
5
6 import com.tc.io.TCByteBufferOutputStream;
7 import com.tc.logging.TCLogger;
8 import com.tc.logging.TCLogging;
9 import com.tc.object.TCClass;
10 import com.tc.object.TCObject;
11 import com.tc.object.change.event.ArrayElementChangeEvent;
12 import com.tc.object.change.event.LiteralChangeEvent;
13 import com.tc.object.change.event.LogicalChangeEvent;
14 import com.tc.object.change.event.PhysicalChangeEvent;
15 import com.tc.object.dna.api.DNACursor;
16 import com.tc.object.dna.api.DNAWriter;
17 import com.tc.object.dna.api.LogicalAction;
18 import com.tc.object.dna.api.PhysicalAction;
19 import com.tc.object.dna.impl.DNAEncoding;
20 import com.tc.object.dna.impl.DNAWriterImpl;
21 import com.tc.object.dna.impl.ObjectStringSerializer;
22 import com.tc.object.tx.optimistic.OptimisticTransactionManager;
23 import com.tc.util.Assert;
24 import com.tc.util.concurrent.SetOnceFlag;
25
26 import java.util.Collection JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.LinkedHashMap JavaDoc;
30 import java.util.LinkedList JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33
34 /**
35  * @author orion
36  */

37 public class TCChangeBufferImpl implements TCChangeBuffer {
38   private static final TCLogger logger = TCLogging.getLogger(TCChangeBuffer.class);
39
40   private final SetOnceFlag dnaCreated = new SetOnceFlag();
41   private final TCObject tcObject;
42
43   private final int type;
44   private final Map JavaDoc physicalEvents;
45   private final List JavaDoc logicalEvents;
46   private final Map JavaDoc arrayEvents;
47   private final List JavaDoc literalValueChangedEvents;
48
49   public TCChangeBufferImpl(TCObject object) {
50     this.tcObject = object;
51
52     // This stuff is slightly yucky, but the "null"-ness of these event collections is relevant for determining whether
53
// physical updates to logical classes should be ignore or not
54
TCClass clazz = tcObject.getTCClass();
55     if (clazz.isIndexed()) {
56       type = ARRAY;
57       physicalEvents = null;
58       literalValueChangedEvents = null;
59       logicalEvents = null;
60       arrayEvents = new LinkedHashMap JavaDoc();
61     } else if (clazz.isLogical()) {
62       type = LOGICAL;
63       physicalEvents = null;
64       literalValueChangedEvents = null;
65       logicalEvents = new LinkedList JavaDoc();
66       arrayEvents = null;
67     } else {
68       type = PHYSICAL;
69       physicalEvents = new HashMap JavaDoc();
70       literalValueChangedEvents = new LinkedList JavaDoc();
71       logicalEvents = null;
72       arrayEvents = null;
73     }
74   }
75
76   public void writeTo(TCByteBufferOutputStream output, ObjectStringSerializer serializer, DNAEncoding encoding) {
77     // NOTE: This method releases the change events to conserve memory
78

79     if (dnaCreated.attemptSet()) {
80       boolean commitNew = tcObject.getAndResetNew();
81
82       TCClass tcClass = tcObject.getTCClass();
83       String JavaDoc className = tcClass.getExtendingClassName();
84       String JavaDoc loaderDesc = tcClass.getDefiningLoaderDescription();
85       DNAWriter writer = new DNAWriterImpl(output, tcObject.getObjectID(), className, serializer, encoding, loaderDesc,
86                                            !commitNew);
87
88       if (commitNew) {
89         tcObject.dehydrate(writer);
90       } else {
91         if (arrayEvents != null) {
92           writeEventsToDNA(arrayEvents.values(), writer);
93         }
94         if (physicalEvents != null) {
95           writeEventsToDNA(physicalEvents.values(), writer);
96         }
97         if (logicalEvents != null) {
98           writeEventsToDNA(logicalEvents, writer);
99         }
100         if (literalValueChangedEvents != null) {
101           writeEventsToDNA(literalValueChangedEvents, writer);
102         }
103       }
104
105       writer.finalizeDNA();
106       return;
107     }
108
109     throw new IllegalStateException JavaDoc("DNA already created");
110   }
111
112   private void writeEventsToDNA(Collection JavaDoc events, DNAWriter writer) {
113     if (events.size() > 0) {
114       for (Iterator JavaDoc iter = events.iterator(); iter.hasNext();) {
115         TCChangeBufferEvent event = (TCChangeBufferEvent) iter.next();
116         event.write(writer);
117       }
118     }
119   }
120
121   public void literalValueChanged(Object JavaDoc newValue) {
122     literalValueChangedEvents.add(new LiteralChangeEvent(newValue));
123   }
124
125   public void fieldChanged(String JavaDoc classname, String JavaDoc fieldname, Object JavaDoc newValue, int index) {
126     Assert.eval(newValue != null);
127
128     if (index >= 0) {
129       // We could add some form of threshold for array change events where instead of encoding the individual updates,
130
// we could just send the data for the entire array (like we do when a managed array is brand new)
131

132       // could use a better collection that maintain put order for repeated additions
133
Integer JavaDoc key = new Integer JavaDoc(index);
134       arrayEvents.remove(key);
135       arrayEvents.put(key, new ArrayElementChangeEvent(index, newValue));
136     } else {
137       if (logicalEvents != null) {
138         // ignore physical updates to classes that are logically managed (This happens for things like THash which is a
139
// superclass of THashMap)
140
if (logger.isDebugEnabled()) {
141           logger.debug("Ignoring physical field change for " + classname + "." + fieldname + " since "
142                        + tcObject.getTCClass().getName() + " is logically managed");
143         }
144         return;
145       }
146
147       // XXX: only fully qualify fieldnames when necessary (ie. when a variable name is shadowed)
148
// XXX: When and if this change is made, you also want to look at GenericTCField
149
// fieldname = classname + "." + fieldname;
150

151       // Assert.eval(fieldname.indexOf('.') >= 0);
152
// this will replace any existing event for this field. Last change made to any field within the same TXN wins
153
physicalEvents.put(fieldname, new PhysicalChangeEvent(fieldname, newValue));
154     }
155   }
156
157   public void arrayChanged(int startPos, Object JavaDoc array, int newLength) {
158     // could use a better collection that maintain put order for repeated additions
159
Integer JavaDoc key = new Integer JavaDoc(-startPos); // negative int is used for sub-arrays
160
ArrayElementChangeEvent oldEvent = (ArrayElementChangeEvent) arrayEvents.remove(key);
161     if (oldEvent != null) {
162       Object JavaDoc oldArray = oldEvent.getValue();
163       int oldLength = oldEvent.getLength();
164       if (oldLength > newLength) {
165         System.arraycopy(array, 0, oldArray, 0, newLength);
166         array = oldArray;
167       }
168     }
169     arrayEvents.put(key, new ArrayElementChangeEvent(startPos, array, newLength));
170   }
171
172   public void logicalInvoke(int method, Object JavaDoc[] parameters) {
173     // TODO: It might be useful (if it doesn't take too much CPU) to collapse logical operations. For instance,
174
// if a put() is followed by a remove() on the same key we don't need to send anything. Or if multiple put()s are
175
// done, only the last one matters
176

177     logicalEvents.add(new LogicalChangeEvent(method, parameters));
178   }
179
180   public TCObject getTCObject() {
181     return tcObject;
182   }
183
184   public int getTotalEventCount() {
185     int eventCount = 0;
186     if (physicalEvents != null) {
187       eventCount += physicalEvents.size();
188     }
189     if (literalValueChangedEvents != null) {
190       eventCount += literalValueChangedEvents.size();
191     }
192     if (logicalEvents != null) {
193       eventCount += logicalEvents.size();
194     }
195     if (arrayEvents != null) {
196       eventCount += arrayEvents.size();
197     }
198     return eventCount;
199   }
200
201   public int getType() {
202     return type;
203   }
204
205   public void accept(TCChangeBufferEventVisitor visitor) {
206     switch (type) {
207       case LOGICAL:
208         for (Iterator JavaDoc it = logicalEvents.iterator(); it.hasNext();) {
209           visitor.visitLogicalEvent((LogicalChangeEvent) it.next());
210         }
211         break;
212
213       case PHYSICAL:
214         if (literalValueChangedEvents != null && literalValueChangedEvents.size() > 0) { throw new AssertionError JavaDoc(
215                                                                                                                   "Changes to literal roots are not supported in OptimisticTransaction."); }
216         for (Iterator JavaDoc it = physicalEvents.values().iterator(); it.hasNext();) {
217           visitor.visitPhysicalChangeEvent((PhysicalChangeEvent) it.next());
218         }
219         break;
220
221       case ARRAY:
222         for (Iterator JavaDoc it = arrayEvents.values().iterator(); it.hasNext();) {
223           visitor.visitArrayElementChangeEvent((ArrayElementChangeEvent) it.next());
224         }
225         break;
226
227       default:
228         throw new AssertionError JavaDoc("Unknown event type " + type);
229     }
230   }
231
232   public DNACursor getDNACursor(OptimisticTransactionManager transactionManager) {
233     switch (type) {
234       case PHYSICAL:
235         if (literalValueChangedEvents != null && literalValueChangedEvents.size() > 0) { throw new AssertionError JavaDoc(
236                                                                                                                   "Changes to literal roots are not supported in OptimisticTransaction."); }
237         return new AbstractDNACursor(physicalEvents.values(), transactionManager) {
238           Object JavaDoc createNextAction(Object JavaDoc object) {
239             PhysicalChangeEvent pe = (PhysicalChangeEvent) object;
240             return new PhysicalAction(pe.getFieldName(), convertToParameter(pe.getNewValue()), pe.isReference());
241           }
242         };
243
244       case LOGICAL:
245         return new AbstractDNACursor(logicalEvents, transactionManager) {
246           Object JavaDoc createNextAction(Object JavaDoc object) {
247             LogicalChangeEvent le = (LogicalChangeEvent) object;
248             Object JavaDoc[] p = new Object JavaDoc[le.getParameters().length];
249             for (int i = 0; i < le.getParameters().length; i++) {
250               p[i] = convertToParameter(le.getParameters()[i]);
251             }
252             return new LogicalAction(le.getMethodID(), p);
253           }
254         };
255
256       case ARRAY:
257         return new AbstractDNACursor(arrayEvents.values(), transactionManager) {
258           Object JavaDoc createNextAction(Object JavaDoc object) {
259             ArrayElementChangeEvent ae = (ArrayElementChangeEvent) object;
260             if (ae.isSubarray()) {
261               return new PhysicalAction(ae.getValue(), ae.getIndex());
262             } else {
263               return new PhysicalAction(ae.getIndex(), convertToParameter(ae.getValue()), ae.isReference());
264             }
265           }
266         };
267
268       default:
269         throw new AssertionError JavaDoc("Unknown event type " + type);
270     }
271   }
272
273   private static abstract class AbstractDNACursor implements DNACursor {
274     private final OptimisticTransactionManager transactionManager;
275     private final Iterator JavaDoc iterator;
276     private final int size;
277
278     private Object JavaDoc currentAction = null;
279
280     public AbstractDNACursor(Collection JavaDoc values, OptimisticTransactionManager transactionManager) {
281       this.transactionManager = transactionManager;
282       this.iterator = values.iterator();
283       this.size = values.size();
284     }
285
286     public boolean next() {
287       boolean hasNext = iterator.hasNext();
288       if (hasNext) {
289         this.currentAction = createNextAction(iterator.next());
290       }
291       return hasNext;
292     }
293
294     abstract Object JavaDoc createNextAction(Object JavaDoc object);
295
296     public boolean next(DNAEncoding encoding) {
297       return next();
298     }
299
300     public int getActionCount() {
301       return size;
302     }
303
304     public Object JavaDoc getAction() {
305       return currentAction;
306     }
307
308     public LogicalAction getLogicalAction() {
309       return (LogicalAction) currentAction;
310     }
311
312     public PhysicalAction getPhysicalAction() {
313       return (PhysicalAction) currentAction;
314     }
315
316     public void reset() throws UnsupportedOperationException JavaDoc {
317       throw new UnsupportedOperationException JavaDoc("This operation is not supported by this class.");
318     }
319
320     protected Object JavaDoc convertToParameter(Object JavaDoc object) {
321       return transactionManager.convertToParameter(object);
322     }
323   }
324
325 }
326
Popular Tags