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.l1.impl;
006:
007: import com.tc.logging.TCLogger;
008: import com.tc.net.groups.NodeID;
009: import com.tc.object.ObjectID;
010: import com.tc.object.dna.api.DNA;
011: import com.tc.objectserver.l1.api.ClientState;
012: import com.tc.objectserver.l1.api.ClientStateManager;
013: import com.tc.objectserver.managedobject.BackReferences;
014: import com.tc.text.PrettyPrintable;
015: import com.tc.text.PrettyPrinter;
016: import com.tc.util.ObjectIDSet2;
017: import com.tc.util.State;
018:
019: import java.util.Collection;
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.LinkedList;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.Set;
028:
029: /**
030: * @author steve
031: */
032: public class ClientStateManagerImpl implements ClientStateManager {
033:
034: private static final State STARTED = new State("STARTED");
035: private static final State STOPPED = new State("STOPPED");
036:
037: private State state = STARTED;
038:
039: private final Map clientStates;
040: private final TCLogger logger;
041:
042: // for testing
043: public ClientStateManagerImpl(TCLogger logger, Map states) {
044: this .logger = logger;
045: this .clientStates = states;
046: }
047:
048: public ClientStateManagerImpl(TCLogger logger) {
049: this (logger, new HashMap());
050: }
051:
052: public synchronized List createPrunedChangesAndAddObjectIDTo(
053: Collection changes, BackReferences includeIDs, NodeID id,
054: Set objectIDs) {
055: assertStarted();
056: ClientStateImpl clientState = getClientState(id);
057: if (clientState == null) {
058: logger
059: .warn(": createPrunedChangesAndAddObjectIDTo : Client state is NULL (probably due to disconnect) : "
060: + id);
061: return Collections.EMPTY_LIST;
062: }
063: LinkedList prunedChanges = new LinkedList();
064:
065: for (Iterator i = changes.iterator(); i.hasNext();) {
066: DNA dna = (DNA) i.next();
067: if (clientState.containsReference(dna.getObjectID())) {
068: if (dna.isDelta()) {
069: prunedChanges.add(dna);
070: } else {
071: // This new Object must have already been sent as a part of a different lookup. So ignoring this change.
072: }
073: // } else if (clientState.containsParent(dna.getObjectID(), includeIDs)) {
074: // these objects needs to be looked up from the client during apply
075: // objectIDs.add(dna.getObjectID());
076: }
077: }
078: clientState.addReferencedChildrenTo(objectIDs, includeIDs);
079:
080: return prunedChanges;
081: }
082:
083: public synchronized void addReference(NodeID id, ObjectID objectID) {
084: assertStarted();
085: // logger.info("Adding Reference for " + id + " to " + objectID);
086: ClientStateImpl c = getClientState(id);
087: if (c != null) {
088: c.addReference(objectID);
089: } else {
090: logger
091: .warn(": addReference : Client state is NULL (probably due to disconnect) : "
092: + id);
093: }
094: }
095:
096: public synchronized void removeReferences(NodeID id, Set removed) {
097: assertStarted();
098: // logger.info("Removing Reference for " + id + " to " + removed);
099: ClientStateImpl c = getClientState(id);
100: if (c != null) {
101: c.removeReferences(removed);
102: } else {
103: logger
104: .warn(": removeReferences : Client state is NULL (probably due to disconnect) : "
105: + id);
106: }
107: }
108:
109: public synchronized boolean hasReference(NodeID id,
110: ObjectID objectID) {
111: ClientStateImpl c = getClientState(id);
112: if (c != null) {
113: return c.containsReference(objectID);
114: } else {
115: logger
116: .warn(": hasReference : Client state is NULL (probably due to disconnect) : "
117: + id);
118: return false;
119: }
120: }
121:
122: public synchronized void addAllReferencedIdsTo(Set ids) {
123: assertStarted();
124: for (Iterator i = clientStates.values().iterator(); i.hasNext();) {
125: ClientStateImpl s = (ClientStateImpl) i.next();
126: s.addReferencedIdsTo(ids);
127: }
128: }
129:
130: public synchronized void removeReferencedFrom(NodeID id, Set oids) {
131: ClientState cs = getClientState(id);
132: if (cs == null) {
133: logger
134: .warn(": removeReferencedFrom : Client state is NULL (probably due to disconnect) : "
135: + id);
136: return;
137: }
138: Set refs = cs.getReferences();
139: // XXX:: This is a work around for THashSet's poor implementation of removeAll
140: if (oids.size() >= refs.size()) {
141: oids.removeAll(refs);
142: } else {
143: for (Iterator i = oids.iterator(); i.hasNext();) {
144: if (refs.contains(i.next())) {
145: i.remove();
146: }
147: }
148: }
149:
150: }
151:
152: /*
153: * returns newly added references
154: */
155: public synchronized Set addReferences(NodeID id, Set oids) {
156: ClientState cs = getClientState(id);
157: if (cs == null) {
158: logger
159: .warn(": addReferences : Client state is NULL (probably due to disconnect) : "
160: + id);
161: return Collections.EMPTY_SET;
162: }
163: Set refs = cs.getReferences();
164: if (refs.isEmpty()) {
165: refs.addAll(oids);
166: return oids;
167: }
168: Set newReferences = new HashSet();
169: for (Iterator i = oids.iterator(); i.hasNext();) {
170: Object oid = i.next();
171: if (refs.add(oid)) {
172: newReferences.add(oid);
173: }
174: }
175: return newReferences;
176: }
177:
178: public synchronized void shutdownNode(NodeID waitee) {
179: if (!isStarted()) {
180: // it's too late to remove the client from the database. On startup, this guy will fail to reconnect
181: // within the timeout period and be slain.
182: return;
183: }
184: clientStates.remove(waitee);
185: }
186:
187: public synchronized void startupNode(NodeID nodeID) {
188: if (!isStarted()) {
189: return;
190: }
191: Object old = clientStates.put(nodeID, new ClientStateImpl(
192: nodeID));
193: if (old != null) {
194: throw new AssertionError(
195: "Client connected before disconnecting : old Client state = "
196: + old);
197: }
198: }
199:
200: public synchronized void stop() {
201: assertStarted();
202: state = STOPPED;
203: logger.info("ClientStateManager stopped.");
204: }
205:
206: private boolean isStarted() {
207: return state == STARTED;
208: }
209:
210: private void assertStarted() {
211: if (state != STARTED)
212: throw new AssertionError("Not started.");
213: }
214:
215: private ClientStateImpl getClientState(NodeID id) {
216: return (ClientStateImpl) clientStates.get(id);
217: }
218:
219: public synchronized PrettyPrinter prettyPrint(PrettyPrinter out) {
220: PrettyPrinter rv = out;
221: out.println(getClass().getName());
222: out = out.duplicateAndIndent();
223: out.indent().println("client states: ");
224: out = out.duplicateAndIndent();
225: for (Iterator i = clientStates.keySet().iterator(); i.hasNext();) {
226: Object key = i.next();
227: ClientStateImpl st = (ClientStateImpl) clientStates
228: .get(key);
229: out.indent().print(key + "=").visit(st).println();
230: }
231: return rv;
232: }
233:
234: private static class ClientStateImpl implements PrettyPrintable,
235: ClientState {
236: private final NodeID nodeID;
237: private final Set managed = new ObjectIDSet2();
238:
239: public ClientStateImpl(NodeID nodeID) {
240: this .nodeID = nodeID;
241: }
242:
243: public void addReferencedChildrenTo(Set objectIDs,
244: BackReferences includeIDs) {
245: Set parents = includeIDs.getAllParents();
246: parents.retainAll(managed);
247: includeIDs.addReferencedChildrenTo(objectIDs, parents);
248: }
249:
250: public String toString() {
251: return "ClientStateImpl[" + nodeID + ", " + managed + "]";
252: }
253:
254: public Set getReferences() {
255: return managed;
256: }
257:
258: public PrettyPrinter prettyPrint(PrettyPrinter out) {
259: out.println(getClass().getName());
260: out.duplicateAndIndent().indent().print("managed: ").visit(
261: managed);
262: return out;
263: }
264:
265: public void addReference(ObjectID id) {
266: managed.add(id);
267: }
268:
269: public boolean containsReference(ObjectID id) {
270: return managed.contains(id);
271: }
272:
273: public void removeReferences(Set references) {
274: managed.removeAll(references);
275: }
276:
277: public void addReferencedIdsTo(Set ids) {
278: ids.addAll(managed);
279: }
280:
281: public NodeID getNodeID() {
282: return nodeID;
283: }
284: }
285:
286: }
|