001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.objectserver.managedobject;
006:
007: import com.tc.io.TCByteBufferOutputStream;
008: import com.tc.logging.TCLogger;
009: import com.tc.logging.TCLogging;
010: import com.tc.object.ObjectID;
011: import com.tc.object.dna.api.DNA;
012: import com.tc.object.dna.api.DNACursor;
013: import com.tc.object.dna.api.DNAEncoding;
014: import com.tc.object.dna.api.DNAException;
015: import com.tc.object.dna.impl.DNAEncodingImpl;
016: import com.tc.object.dna.impl.DNAWriterImpl;
017: import com.tc.object.dna.impl.ObjectDNAWriterImpl;
018: import com.tc.object.dna.impl.ObjectStringSerializer;
019: import com.tc.object.tx.TransactionID;
020: import com.tc.objectserver.api.ObjectInstanceMonitor;
021: import com.tc.objectserver.core.api.ManagedObject;
022: import com.tc.objectserver.core.api.ManagedObjectState;
023: import com.tc.objectserver.impl.ManagedObjectReference;
024: import com.tc.objectserver.managedobject.bytecode.ClassNotCompatableException;
025: import com.tc.objectserver.mgmt.ManagedObjectFacade;
026: import com.tc.text.PrettyPrintable;
027: import com.tc.text.PrettyPrinter;
028: import com.tc.util.Assert;
029: import com.tc.util.Conversion;
030:
031: import gnu.trove.TLinkable;
032:
033: import java.io.IOException;
034: import java.io.ObjectInputStream;
035: import java.io.ObjectOutputStream;
036: import java.io.PrintWriter;
037: import java.io.Serializable;
038: import java.io.StringWriter;
039: import java.util.Set;
040:
041: /**
042: * Responsible for maintaining the state of a shared object. Used for broadcasting new instances of an object as well as
043: * having changes applied to it and keeping track of references for garbage collection. If you add fields to this object
044: * that need to be serialized make sure you add them to the ManagedObjectSerializer
045: *
046: * @author steve TODO:: Remove Cacheable interface from this Object.
047: */
048: public class ManagedObjectImpl implements ManagedObject,
049: ManagedObjectReference, Serializable, PrettyPrintable {
050: private static final TCLogger logger = TCLogging
051: .getLogger(ManagedObjectImpl.class);
052: private static final DNAEncoding DNA_STORAGE_ENCODING = new DNAEncodingImpl(
053: DNAEncoding.STORAGE);
054:
055: private final static byte IS_NEW_OFFSET = 1;
056: private final static byte IS_DIRTY_OFFSET = 2;
057: private final static byte REFERENCED_OFFSET = 4;
058: private final static byte REMOVE_ON_RELEASE_OFFSET = 8;
059: private final static byte PINNED_OFFSET = 16;
060:
061: private final static byte INITIAL_FLAG_VALUE = IS_DIRTY_OFFSET
062: | IS_NEW_OFFSET;
063:
064: final ObjectID id;
065:
066: long version = -1;
067: transient ManagedObjectState state;
068:
069: // TODO::Split this flag into two so that concurrency is maintained
070: private volatile transient byte flags = INITIAL_FLAG_VALUE;
071:
072: // TODO:: Remove Cacheable interface from this Object and remove these two references
073: private transient TLinkable previous;
074: private transient TLinkable next;
075:
076: private int accessed;
077:
078: public ManagedObjectImpl(ObjectID id) {
079: Assert.assertNotNull(id);
080: this .id = id;
081: }
082:
083: /**
084: * This is here for testing, not production use.
085: */
086: public boolean isEqual(ManagedObject moi) {
087: if (this == moi)
088: return true;
089: if (moi instanceof ManagedObjectImpl) {
090: ManagedObjectImpl mo = (ManagedObjectImpl) moi;
091: boolean rv = true;
092: rv &= id.equals(mo.id);
093: rv &= version == mo.version;
094: rv &= state.equals(mo.state);
095: return rv;
096: } else
097: return false;
098: }
099:
100: void setBasicIsNew(boolean b) {
101: setFlag(IS_NEW_OFFSET, b);
102: }
103:
104: private void setBasicIsDirty(boolean b) {
105: setFlag(IS_DIRTY_OFFSET, b);
106: }
107:
108: private synchronized void setFlag(int offset, boolean value) {
109: flags = Conversion.setFlag(flags, offset, value);
110: }
111:
112: private synchronized boolean getFlag(int offset) {
113: return (flags & offset) == offset;
114: }
115:
116: private boolean basicIsNew() {
117: return getFlag(IS_NEW_OFFSET);
118: }
119:
120: private boolean basicIsDirty() {
121: return getFlag(IS_DIRTY_OFFSET);
122: }
123:
124: public boolean isNew() {
125: return basicIsNew();
126: }
127:
128: public boolean isDirty() {
129: return basicIsDirty();
130: }
131:
132: public void setIsDirty(boolean isDirty) {
133: setBasicIsDirty(isDirty);
134: }
135:
136: public ObjectID getID() {
137: return id;
138: }
139:
140: public Set getObjectReferences() {
141: return state.getObjectReferences();
142: }
143:
144: public void addObjectReferencesTo(ManagedObjectTraverser traverser) {
145: state.addObjectReferencesTo(traverser);
146: }
147:
148: public void apply(DNA dna, TransactionID txnID,
149: BackReferences includeIDs,
150: ObjectInstanceMonitor instanceMonitor,
151: boolean ignoreIfOlderDNA) {
152: boolean isNew = isNew();
153: String typeName = dna.getTypeName();
154: long dna_version = dna.getVersion();
155: if (dna_version <= this .version) {
156: if (ignoreIfOlderDNA) {
157: logger.info("Ignoring apply of an old DNA for "
158: + typeName + " id = " + id
159: + " current version = " + this .version
160: + " dna_version = " + dna_version);
161: return;
162: } else {
163: throw new AssertionError(
164: "Recd a DNA with version less than or equal to the current version : "
165: + this .version + " dna_version : "
166: + dna_version);
167: }
168: }
169: if (dna.isDelta() && isNew) {
170: throw new AssertionError(
171: "Newly created Object is applied with a delta DNA ! ManagedObjectImpl = "
172: + this .toString() + " DNA = " + dna
173: + " TransactionID = " + txnID);
174: } else if (!dna.isDelta() && !isNew) {
175: // New DNA applied on old object - a No No for logical objects.
176: throw new AssertionError(
177: "Old Object is applied with a non-delta DNA ! ManagedObjectImpl = "
178: + this .toString() + " DNA = " + dna
179: + " TransactionID = " + txnID);
180: }
181: if (isNew) {
182: instanceMonitor.instanceCreated(typeName);
183: }
184: this .version = dna_version;
185: DNACursor cursor = dna.getCursor();
186:
187: if (state == null) {
188: state = getStateFactory().createState(id,
189: dna.getParentObjectID(), typeName,
190: dna.getDefiningLoaderDescription(), cursor);
191: }
192: try {
193: try {
194: state.apply(id, cursor, includeIDs);
195: } catch (ClassNotCompatableException cnce) {
196: // reinitialize state object and try again
197: reinitializeState(dna.getParentObjectID(), typeName,
198: dna.getDefiningLoaderDescription(), cursor,
199: state);
200: state.apply(id, cursor, includeIDs);
201: }
202: } catch (IOException e) {
203: throw new DNAException(e);
204: }
205: setIsDirty(true);
206: setBasicIsNew(false);
207: }
208:
209: private void reinitializeState(ObjectID pid, String className,
210: String loaderDesc, DNACursor cursor,
211: ManagedObjectState oldState) {
212: state = getStateFactory().recreateState(id, pid, className,
213: loaderDesc, cursor, oldState);
214: }
215:
216: private ManagedObjectStateFactory getStateFactory() {
217: return ManagedObjectStateFactory.getInstance();
218: }
219:
220: private void writeObject(ObjectOutputStream out) throws IOException {
221: if (state == null) {
222: throw new AssertionError("Null state:" + this );
223: }
224: out.defaultWriteObject();
225: out.writeByte(state.getType());
226: state.writeTo(out);
227: }
228:
229: private void readObject(ObjectInputStream in) throws IOException,
230: ClassNotFoundException {
231: in.defaultReadObject();
232: byte type = in.readByte();
233: this .state = getStateFactory().readManagedObjectStateFrom(in,
234: type);
235: this .setBasicIsNew(false);
236: this .setBasicIsDirty(false);
237: }
238:
239: public ManagedObjectState getManagedObjectState() {
240: return state;
241: }
242:
243: /**
244: * Writes the data in the object to the DNA strand supplied.
245: */
246: public void toDNA(TCByteBufferOutputStream out,
247: ObjectStringSerializer serializer) {
248: DNAWriterImpl writer = new ObjectDNAWriterImpl(out, id,
249: getClassname(), serializer, DNA_STORAGE_ENCODING,
250: getLoaderDescription(), version);
251: state.dehydrate(id, writer);
252: writer.finalizeDNA(false);
253: }
254:
255: public String toString() {
256: // XXX: Um... this is gross.
257: StringWriter writer = new StringWriter();
258: PrintWriter pWriter = new PrintWriter(writer);
259: new PrettyPrinter(pWriter).visit(this );
260: return writer.getBuffer().toString();
261: }
262:
263: public PrettyPrinter prettyPrint(PrettyPrinter out) {
264: PrettyPrinter rv = out;
265: out = out.print("ManagedObjectImpl").duplicateAndIndent()
266: .println();
267: out.indent().print(
268: "identityHashCode: " + System.identityHashCode(this ))
269: .println();
270: out.indent().print("id: " + id).println();
271: out.indent().print("className: " + getClassname()).println();
272: out.indent().print("version:" + version).println();
273: out.indent().print("state: ").visit(state).println();
274: out.indent().print("isDirty:" + this .basicIsDirty());
275: out.indent().print("isNew:" + this .basicIsNew());
276: out.indent().print("isReferenced:" + this .isReferenced())
277: .println();
278: out.indent().print(
279: "next: " + (this .getNext() != null) + " prev: "
280: + (this .getPrevious() != null));
281: return rv;
282: }
283:
284: public ManagedObjectFacade createFacade(int limit) {
285: return state.createFacade(id, getClassname(), limit);
286: }
287:
288: /**
289: * This is public for testing
290: */
291: public String getClassname() {
292: return state == null ? "UNKNOWN" : state.getClassName();
293: }
294:
295: public String getLoaderDescription() {
296: return state == null ? "UNKNOWN" : state.getLoaderDescription();
297: }
298:
299: public ManagedObjectReference getReference() {
300: return this ;
301: }
302:
303: /*********************************************************************************************************************
304: * ManagedObjectReference Interface
305: */
306: public ManagedObject getObject() {
307: return this ;
308: }
309:
310: public boolean isRemoveOnRelease() {
311: return getFlag(REMOVE_ON_RELEASE_OFFSET);
312: }
313:
314: public void markReference() {
315: setFlag(REFERENCED_OFFSET, true);
316: }
317:
318: public void unmarkReference() {
319: setFlag(REFERENCED_OFFSET, false);
320: }
321:
322: public boolean isReferenced() {
323: return getFlag(REFERENCED_OFFSET);
324: }
325:
326: /**
327: * Pins this reference in the cache.
328: */
329: public void pin() {
330: setFlag(PINNED_OFFSET, true);
331: }
332:
333: public void unpin() {
334: setFlag(PINNED_OFFSET, false);
335: }
336:
337: /**
338: * Determines whether or not this reference is pinned in the ObjectManager's cache. This allows the object manager to
339: * lookup multiple objects one at a time without evicting them from the cache.
340: */
341: public boolean isPinned() {
342: return getFlag(PINNED_OFFSET);
343: }
344:
345: /*********************************************************************************************************************
346: * ManagedObjectReference::Cacheable interface XXX:: Most of these methods are not synchronized (except when accessing
347: * the flag field which can be accessed from multiple threads) 'coz it is expected that these are called under the
348: * scope of a bigger sync block (from evictionPolicy)
349: */
350:
351: public void setRemoveOnRelease(boolean removeOnRelease) {
352: setFlag(REMOVE_ON_RELEASE_OFFSET, removeOnRelease);
353: }
354:
355: public void markAccessed() {
356: accessed++;
357: }
358:
359: public void clearAccessed() {
360: accessed = 0;
361: }
362:
363: public boolean recentlyAccessed() {
364: return accessed > 0;
365: }
366:
367: public int accessCount(int factor) {
368: accessed = accessed / factor;
369: return accessed;
370: }
371:
372: public ObjectID getObjectID() {
373: return getID();
374: }
375:
376: public TLinkable getNext() {
377: return next;
378: }
379:
380: public TLinkable getPrevious() {
381: return previous;
382: }
383:
384: public void setNext(TLinkable next) {
385: this .next = next;
386: }
387:
388: public void setPrevious(TLinkable previous) {
389: this .previous = previous;
390: }
391:
392: public synchronized boolean canEvict() {
393: return !(isPinned() || isReferenced() || isNew());
394: }
395:
396: public long getVersion() {
397: return version;
398: }
399:
400: }
|