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.tx;
006:
007: import com.tc.object.tx.ServerTransactionID;
008: import com.tc.properties.TCPropertiesImpl;
009: import com.tc.text.PrettyPrintable;
010: import com.tc.text.PrettyPrinter;
011: import com.tc.util.Assert;
012: import com.tc.util.State;
013:
014: import java.util.Collection;
015: import java.util.Collections;
016: import java.util.ConcurrentModificationException;
017: import java.util.HashMap;
018: import java.util.Iterator;
019: import java.util.Map;
020: import java.util.NoSuchElementException;
021: import java.util.Map.Entry;
022:
023: public final class TxnObjectGrouping implements PrettyPrintable {
024: private static final int MAX_OBJECTS = TCPropertiesImpl
025: .getProperties().getInt(
026: "l2.objectmanager.maxObjectsInTxnObjGrouping");
027: private static final int MAX_TXNS = TCPropertiesImpl
028: .getProperties().getInt(
029: "l2.objectmanager.maxTxnsInTxnObjectGrouping");
030:
031: private static final State APPLY_PENDING = new State(
032: "APPLY_PENDING");
033: private static final State COMMIT_PENDING = new State(
034: "COMMIT_PENDING");
035:
036: private final ServerTransactionID txID;
037: private Map txns;
038: private Map objects;
039: private final Map newRootsMap;
040: private int pendingApplys;
041: private boolean isActive = true;
042:
043: public TxnObjectGrouping(ServerTransactionID sTxID, Map newRootsMap) {
044: this .txID = sTxID;
045: this .newRootsMap = newRootsMap;
046: this .txns = new HashMap();
047: this .txns.put(sTxID, APPLY_PENDING);
048: this .pendingApplys = 1;
049: this .objects = Collections.EMPTY_MAP;
050: }
051:
052: public TxnObjectGrouping(Map lookedupObjects) {
053: this .txID = ServerTransactionID.NULL_ID;
054: this .newRootsMap = Collections.EMPTY_MAP;
055: this .txns = Collections.EMPTY_MAP;
056: objects = lookedupObjects;
057: this .pendingApplys = 0;
058: }
059:
060: public boolean applyComplete(ServerTransactionID txnId) {
061: Object o = txns.put(txnId, COMMIT_PENDING);
062: Assert.assertTrue(o == APPLY_PENDING);
063: --pendingApplys;
064: return (pendingApplys == 0);
065: }
066:
067: public ServerTransactionID getServerTransactionID() {
068: return txID;
069: }
070:
071: public Map getObjects() {
072: return objects;
073: }
074:
075: public Map getNewRoots() {
076: return newRootsMap;
077: }
078:
079: /*
080: * This method has a side effect of setting references in Old grouping to null. This is done to avoid adding huge
081: * collections to smaller collections for performance reasons.
082: */
083: public void merge(TxnObjectGrouping oldGrouping) {
084: Assert.assertTrue(this .txID != ServerTransactionID.NULL_ID
085: && oldGrouping.isActive());
086: if (txns.size() >= oldGrouping.txns.size()) {
087: txns.putAll(oldGrouping.txns);
088: } else {
089: Map temp = txns;
090: txns = oldGrouping.txns;
091: txns.putAll(temp);
092: }
093: if (objects.size() >= oldGrouping.objects.size()) {
094: objects.putAll(oldGrouping.objects);
095: } else {
096: Map temp = objects;
097: objects = oldGrouping.objects;
098: objects.putAll(temp);
099: }
100: newRootsMap.putAll(oldGrouping.newRootsMap);
101: pendingApplys += oldGrouping.pendingApplys;
102:
103: // Setting these references to null so that we disable any futher access to these through old grouping
104: oldGrouping.txns = null;
105: oldGrouping.objects = null;
106: oldGrouping.isActive = false;
107: }
108:
109: public boolean isActive() {
110: return isActive;
111: }
112:
113: public boolean limitReached() {
114: return txns.size() > MAX_TXNS
115: || (txns.size() > 1 && objects.size() > MAX_OBJECTS);
116: }
117:
118: public int hashCode() {
119: return txID.hashCode();
120: }
121:
122: public boolean equals(Object o) {
123: if (o instanceof TxnObjectGrouping) {
124: TxnObjectGrouping other = (TxnObjectGrouping) o;
125: return (txID.equals(other.txID));
126: }
127: return false;
128: }
129:
130: public Iterator getApplyPendingTxnsIterator() {
131: return new ApplyPendingTxnsIterator();
132: }
133:
134: public Collection getTxnIDs() {
135: return txns.keySet();
136: }
137:
138: public PrettyPrinter prettyPrint(PrettyPrinter out) {
139: out.println("TransactionGrouping@"
140: + System.identityHashCode(this ));
141: out.indent().println("txnID: ").visit(txID).println();
142: out.indent().println("txns: ").visit(txns).println();
143: out.indent().println("objects: ").visit(objects.keySet())
144: .println();
145: out.indent().println("pendingApplys: " + pendingApplys)
146: .println();
147: out.indent().println("newRootsMap: ").visit(newRootsMap)
148: .println();
149: return out;
150: }
151:
152: public String toString() {
153: StringBuffer out = new StringBuffer();
154: out.append(
155: "TransactionGrouping@" + System.identityHashCode(this ))
156: .append("\n");
157: out.append("\t").append("txnID: ").append(txID).append("\n");
158: out.append("\t").append("txns: ").append(txns).append("\n");
159: out.append("\t").append("objects: ").append(objects.keySet())
160: .append("\n");
161: out.append("\t").append("pendingApplys: ")
162: .append(pendingApplys).append("\n");
163: out.append("\t").append("newRootsMap: ").append(newRootsMap)
164: .append("\n");
165: return out.toString();
166: }
167:
168: public String shortDescription() {
169: return "TxnGrouping[txns = " + txns.size() + ", objects = "
170: + objects.size() + ", pendingApplys = " + pendingApplys
171: + ", roots = " + newRootsMap.size() + "]";
172: }
173:
174: private final class ApplyPendingTxnsIterator implements Iterator {
175:
176: int remaining = pendingApplys;
177: int save = pendingApplys;
178: Iterator pointer = txns.entrySet().iterator();
179:
180: public boolean hasNext() {
181: return (remaining > 0);
182: }
183:
184: public Object next() {
185: if (remaining <= 0) {
186: throw new NoSuchElementException();
187: }
188: if (save != pendingApplys) {
189: throw new ConcurrentModificationException();
190: }
191: remaining--;
192: while (pointer.hasNext()) {
193: Map.Entry e = (Entry) pointer.next();
194: if (e.getValue() == APPLY_PENDING) {
195: return e.getKey();
196: }
197: }
198: throw new AssertionError("Shouldnt reach here");
199: }
200:
201: public void remove() {
202: throw new UnsupportedOperationException();
203: }
204:
205: }
206: }
|