001: /*
002: * <copyright>
003: *
004: * Copyright 1997-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.agent;
028:
029: import java.util.HashMap;
030:
031: import org.cougaar.core.mts.MessageAddress;
032:
033: /**
034: * Table of agent addresses on this VM, allowing static
035: * deserialization threads to find their agent's address.
036: * <p>
037: * @note This is a minor security hole, but at least it only
038: * allows access to the agent addresses and not the agents
039: * themselves.
040: */
041: public final class ClusterContextTable {
042:
043: /**
044: * Our map from a MessageAddress to the ClusterContext wrapper.
045: * <p>
046: * Most values are simple MessageAddress wrappers, but the
047: * table can also contain MessageContext wrappers that add
048: * to/from address information.
049: */
050: private static final HashMap contextTable = new HashMap(89);
051:
052: /** find the agent named by the parameter in my local VM.
053: * Anyone caught using this in plugins will be shot.
054: */
055: static ClusterContext findContext(MessageAddress cid) {
056: synchronized (contextTable) {
057: return (ClusterContext) contextTable.get(cid);
058: }
059: }
060:
061: /** Add a context to the context table */
062: static void addContext(final MessageAddress cid) {
063: ClusterContext c = new ClusterContext() {
064: public MessageAddress getMessageAddress() {
065: return cid;
066: }
067: };
068: synchronized (contextTable) {
069: contextTable.put(cid, c);
070: }
071: }
072:
073: /** Remove a context from the context table */
074: static void removeContext(MessageAddress cid) {
075: synchronized (contextTable) {
076: contextTable.remove(cid);
077: }
078: }
079:
080: /** The thread-local "current" context */
081: private static final ThreadLocal theContext = new ThreadLocal() {
082: };
083:
084: /** Internal object for keeping track of contexts. */
085: public static class ContextState {
086: private ClusterContext cc;
087:
088: public ContextState(ClusterContext c) {
089: cc = c;
090: }
091:
092: public final ClusterContext getClusterContext() {
093: return cc;
094: }
095: }
096:
097: /**
098: * A context for message deserialization, which includes the
099: * source and target addresses.
100: */
101: public static final class MessageContext extends ContextState {
102: private MessageAddress from;
103: private MessageAddress to;
104:
105: public MessageContext(ClusterContext c, MessageAddress f,
106: MessageAddress t) {
107: super (c);
108: from = f;
109: to = t;
110: }
111:
112: public final MessageAddress getFromAddress() {
113: return from;
114: }
115:
116: public final MessageAddress getToAddress() {
117: return to;
118: }
119: }
120:
121: public static ContextState getContextState() {
122: return ((ContextState) theContext.get());
123: }
124:
125: public static MessageContext getMessageContext() {
126: Object cs = theContext.get();
127: return (cs instanceof MessageContext) ? ((MessageContext) cs)
128: : null;
129: }
130:
131: public static ClusterContext getClusterContext() {
132: ContextState cs = (ContextState) theContext.get();
133:
134: return (cs != null) ? cs.getClusterContext() : null;
135: }
136:
137: private static final ClusterContext _dummyContext = new ClusterContext.DummyClusterContext();
138:
139: /** May be used by non-society classes to provide an empty context
140: * for deserialization of objects sent from within a society.
141: * The resulting instances may still be "broken" in various ways, wrapping
142: * this call around the deserialization will at least allow
143: * avoiding warning messages. <em>WARNING</em>: This must <em>never</em> be
144: * used within society code.
145: */
146: public static final void withEmptyClusterContext(Runnable thunk) {
147: withClusterContext(_dummyContext, thunk);
148: }
149:
150: /** Convenient shortcut for a safe enterContext - exitContext pair */
151: public static final void withClusterContext(ClusterContext cc,
152: Runnable thunk) {
153: withContextState(new ContextState(cc), thunk);
154: }
155:
156: /** Convenient shortcut for a safe enterContext - exitContext pair */
157: public static final void withClusterContext(MessageAddress ma,
158: Runnable thunk) {
159: ClusterContext cc = findContext(ma);
160: if (cc == null) {
161: throw new IllegalArgumentException("Address \"" + ma
162: + "\" is not an Agent on this node.");
163: } else {
164: withContextState(new ContextState(cc), thunk);
165: }
166: }
167:
168: /** Convenient shortcut for a safe enterContext - exitContext pair */
169: public static final void withMessageContext(MessageAddress ma,
170: MessageAddress from, MessageAddress to, Runnable thunk) {
171: ClusterContext cc = getClusterContext();
172: if (cc == null) {
173: cc = findContext(ma);
174: if (cc != null) {
175: // normal case for 99% of the time
176: withContextState(new MessageContext(cc, from, to),
177: thunk);
178: } else {
179: // target is not on this node
180: //
181: // This is likely due to a race between remote MTS routing and
182: // local agent removal for mobility (bug 1316). The remote MTS
183: // will catch this exception and re-route the message.
184: throw new IllegalArgumentException("Address \"" + ma
185: + "\" is not an Agent on this node.");
186: }
187: } else {
188: MessageAddress oldMA = (MessageAddress) cc
189: .getMessageAddress();
190: if ((ma != null) ? ma.equals(oldMA) : (oldMA == null)) {
191: // valid nesting, but rare in practice
192: withContextState(new MessageContext(cc, from, to),
193: thunk);
194: } else {
195: // unusual nested context, use the dummy context since its
196: // not valid for the nested message to access a different agent
197: //
198: // In agent mobility this occurs when the node transfers agent
199: // state that contains unsent messages (bug 1629 + bug 1634).
200: // When the agent is created it will sent these nested messages
201: // itself.
202: withEmptyClusterContext(thunk);
203: }
204: }
205: }
206:
207: public static final void withContextState(ContextState cs,
208: Runnable thunk) {
209: ContextState old = (ContextState) theContext.get();
210: theContext.set(cs);
211: try {
212: thunk.run();
213: } finally {
214: theContext.set(old);
215: }
216: }
217: }
|