001: // Copyright 2004, 2005 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.hivemind.lib.util;
016:
017: import java.util.HashMap;
018: import java.util.Iterator;
019: import java.util.LinkedList;
020: import java.util.Map;
021: import java.util.WeakHashMap;
022:
023: import org.apache.hivemind.service.ClassFabUtils;
024: import org.apache.hivemind.util.Defense;
025:
026: /**
027: * Thread-safe implementation of {@link org.apache.hivemind.lib.util.StrategyRegistry}.
028: *
029: * @author Howard Lewis Ship
030: * @since 1.1
031: */
032:
033: public class StrategyRegistryImpl implements StrategyRegistry {
034: /**
035: * A Map of adaptor objects, keyed on registration Class.
036: */
037:
038: private Map _registrations = new HashMap();
039:
040: /**
041: * A Map of adaptor objects, keyed on subject Class.
042: */
043:
044: private Map _cache = new WeakHashMap();
045:
046: public synchronized void register(Class registrationClass,
047: Object adaptor) {
048: Defense.notNull(registrationClass, "registrationClass");
049: Defense.notNull(adaptor, "adaptor");
050:
051: if (_registrations.containsKey(registrationClass))
052: throw new IllegalArgumentException(UtilMessages
053: .duplicateRegistration(registrationClass));
054:
055: _registrations.put(registrationClass, adaptor);
056:
057: // Can't tell what is and isn't valid in the cache.
058: // Also, normally all registrations occur before any adaptors
059: // are searched for, so this is not a big deal.
060:
061: _cache.clear();
062: }
063:
064: public synchronized Object getStrategy(Class subjectClass) {
065: Defense.notNull(subjectClass, "subjectClass");
066:
067: Object result = _cache.get(subjectClass);
068:
069: if (result != null)
070: return result;
071:
072: result = searchForAdaptor(subjectClass);
073:
074: // Record the result in the cache
075:
076: _cache.put(subjectClass, result);
077:
078: return result;
079: }
080:
081: /**
082: * Searches the registration Map for a match, based on inheritance.
083: * <p>
084: * Searches class inheritance first, then interfaces (in a rather vague order). Really should
085: * match the order from the JVM spec.
086: * <p>
087: * There's a degenerate case where we may check the same interface more than once:
088: * <ul>
089: * <li>Two interfaces, I1 and I2
090: * <li>Two classes, C1 and C2
091: * <li>I2 extends I1
092: * <li>C2 extends C1
093: * <li>C1 implements I1
094: * <li>C2 implements I2
095: * <li>The search will be: C2, C1, I2, I1, I1
096: * <li>I1 is searched twice, because C1 implements it, and I2 extends it
097: * <li>There are other such cases, but none of them cause infinite loops and most are rare (we
098: * could guard against it, but its relatively expensive).
099: * <li>Multiple checks only occur if we don't find a registration
100: * </ul>
101: * <p>
102: * This method is only called from a synchronized block, so it is implicitly synchronized.
103: */
104:
105: private Object searchForAdaptor(Class subjectClass) {
106: LinkedList queue = null;
107: Object result = null;
108:
109: // Step one: work up through the class inheritance.
110:
111: Class searchClass = subjectClass;
112:
113: // Primitive types have null, not Object, as their parent
114: // class.
115:
116: while (searchClass != Object.class && searchClass != null) {
117: result = _registrations.get(searchClass);
118: if (result != null)
119: return result;
120:
121: // Not an exact match. If the search class
122: // implements any interfaces, add them to the queue.
123:
124: Class[] interfaces = searchClass.getInterfaces();
125: int length = interfaces.length;
126:
127: if (queue == null && length > 0)
128: queue = new LinkedList();
129:
130: for (int i = 0; i < length; i++)
131: queue.addLast(interfaces[i]);
132:
133: // Advance up to the next superclass
134:
135: searchClass = getSuperclass(searchClass);
136:
137: }
138:
139: // Ok, the easy part failed, lets start searching
140: // interfaces.
141:
142: if (queue != null) {
143: while (!queue.isEmpty()) {
144: searchClass = (Class) queue.removeFirst();
145:
146: result = _registrations.get(searchClass);
147: if (result != null)
148: return result;
149:
150: // Interfaces can extend other interfaces; add them
151: // to the queue.
152:
153: Class[] interfaces = searchClass.getInterfaces();
154: int length = interfaces.length;
155:
156: for (int i = 0; i < length; i++)
157: queue.addLast(interfaces[i]);
158: }
159: }
160:
161: // Not a match on interface; our last gasp is to check
162: // for a registration for java.lang.Object
163:
164: result = _registrations.get(Object.class);
165: if (result != null)
166: return result;
167:
168: // No match? That's rare ... and an error.
169:
170: throw new IllegalArgumentException(UtilMessages
171: .strategyNotFound(subjectClass));
172: }
173:
174: /**
175: * Returns the superclass of the given class, with a single tweak: If the search class is an
176: * array class, and the component type is an object class (but not Object), then the simple
177: * Object array class is returned. This reflects the fact that an array of any class may be
178: * assignable to <code>Object[]</code>, even though the superclass of an array is always
179: * simply <code>Object</code>.
180: */
181:
182: private Class getSuperclass(Class searchClass) {
183: if (searchClass.isArray()) {
184: Class componentType = searchClass.getComponentType();
185:
186: if (!componentType.isPrimitive()
187: && componentType != Object.class)
188: return Object[].class;
189: }
190:
191: return searchClass.getSuperclass();
192: }
193:
194: public synchronized String toString() {
195: StringBuffer buffer = new StringBuffer();
196: buffer.append("AdaptorRegistry[");
197:
198: Iterator i = _registrations.entrySet().iterator();
199: boolean showSep = false;
200:
201: while (i.hasNext()) {
202: if (showSep)
203: buffer.append(' ');
204:
205: Map.Entry entry = (Map.Entry) i.next();
206:
207: Class registeredClass = (Class) entry.getKey();
208:
209: buffer.append(ClassFabUtils
210: .getJavaClassName(registeredClass));
211: buffer.append("=");
212: buffer.append(entry.getValue());
213:
214: showSep = true;
215: }
216:
217: buffer.append("]");
218:
219: return buffer.toString();
220: }
221: }
|