001: /*
002: * <copyright>
003: *
004: * Copyright 2002-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.wp.resolver;
028:
029: import java.io.Serializable;
030: import org.cougaar.core.util.UID;
031: import org.cougaar.core.wp.Timestamp;
032:
033: /**
034: * A data response from a successful {@link LookupService} lookup,
035: * or a request parameter to a {@link ModifyService} lease renewal.
036: * <p>
037: * The UID is specific to this view of the data. If the server
038: * changes the data then the server-side UID for the record will
039: * also change.
040: * <p>
041: * This is used by the LookupService in two cases:<ol>
042: * <li>The client passed a null UID, indicating a lookup
043: * where the client lacks a cached Record.</li>
044: * <li>The client passed a non-null UID to validate that
045: * the Record hadn't changed, but the record <i>has</i>
046: * changed and the client must replace the cached
047: * data.</li>
048: * </ol>
049: * <p>
050: * The ttd is relative to the baseTime passed by the lookup
051: * callback method.
052: */
053: public final class Record implements Serializable {
054:
055: private final UID uid;
056: private final long ttd;
057: private final Object data;
058:
059: public Record(UID uid, long ttd, Object data) {
060: this .uid = uid;
061: this .ttd = ttd;
062: this .data = data;
063: }
064:
065: /**
066: * The UID of the Record.
067: * <p>
068: * The UID of the server's record changes whenever the data
069: * changes.
070: */
071: public UID getUID() {
072: return uid;
073: }
074:
075: /**
076: * The expiration "time-to-death" relative to the base timestamp,
077: * or negative if this is a proposed modification.
078: */
079: public long getTTD() {
080: return ttd;
081: }
082:
083: /**
084: * The data itself.
085: * <p>
086: * For a "getAll" this is a Map of AddressEntry objects, and for
087: * a "list" this is a Set of String names. In case of failure
088: * this may be an Exception (e.g. "access denied").
089: * <p>
090: * The client should send its latest view of its entries.
091: * Rebound entries should show their latest value, unbound
092: * entries should be excluded, and already-leased values should be
093: * included.
094: * <p>
095: * The client should send a new Record with a new UID every time
096: * it changes its local data. Renewals and retries should send
097: * the current UID.
098: * <p>
099: * <b>NOTE:</b>
100: * This design currently doesn't support "bind" operations; it
101: * upgrades all binds to rebinds. This was done to expedite the
102: * white pages implementation, but should be revisited in the near
103: * future. The problem with "bind" operations is that they are
104: * difficult to replicate (is "already bound" tested at one server
105: * or all servers?) and difficult to retry if a server is down
106: * (what if one server says "ok" and the other says "already
107: * bound?).
108: * <p>
109: * The proposed implementation is to tag entries with a
110: * "bind-only" flag, then allow any server to reject these
111: * specific entries. If a server rejects any of the "bind-only"
112: * entries, and the request contains rebind or unbind operations,
113: * then the client must send a new Record modification (excluding
114: * the "bind-only" operations) with a new UID. This will
115: * guarantee that duplicate or lost messages won't cause
116: * confusion over bind-only operations.
117: * <p>
118: * The reason for throwing away the failed bind modification is
119: * illustrated in this example: say the record proposed two
120: * actions:<pre>
121: * <i>uid=u/1</i>
122: * bind X=Y
123: * rebind P=Q
124: * </pre>
125: * The request is sent to server A, which attempts to send back
126: * a partial-failure that accepts "P=Q" but rejects "X=Y" due
127: * to a prior binding. At this point the network goes down, so
128: * the client retries at server B, which accepts both actions
129: * and tells the client the new lease. Server B crashes, server
130: * A comes back up, and the client attempts to renew its
131: * lease on uid <i>u/1</i> at server A. Server A renews just
132: * "P=Q", which is the wrong partial record instead of the full
133: * record data.
134: */
135: public Object getData() {
136: return data;
137: }
138:
139: public boolean equals(Object o) {
140: if (o == this ) {
141: return true;
142: }
143: if (!(o instanceof Record)) {
144: return false;
145: }
146: Record r = (Record) o;
147: return (uid == null ? r.uid == null : uid.equals(r.uid))
148: && ttd == r.ttd
149: && (data == null ? r.data == null : data.equals(r.data));
150: }
151:
152: public int hashCode() {
153: return (uid == null ? 0 : uid.hashCode());
154: }
155:
156: public String toString() {
157: if (ttd < 0) {
158: return "(new-record uid=" + uid + " data=" + data + ")";
159: } else {
160: return "(record uid=" + uid + " ttd=" + ttd + " data="
161: + data + ")";
162: }
163: }
164:
165: public String toString(long baseTime, long now) {
166: if (ttd < 0) {
167: return "(new-record uid=" + uid + " data=" + data + ")";
168: } else {
169: long ttl = baseTime + ttd;
170: return "(record uid=" + uid + " ttd=" + ttd + " ttl="
171: + Timestamp.toString(ttl, now) + " data=" + data
172: + ")";
173: }
174: }
175: }
|