001: /*
002: *
003: * Copyright 2007 by BBN Technologies Corporation
004: *
005: */
006:
007: package org.cougaar.core.plugin;
008:
009: import java.lang.reflect.Method;
010: import java.util.ArrayList;
011: import java.util.Collection;
012: import java.util.HashMap;
013: import java.util.List;
014: import java.util.Map;
015:
016: import org.cougaar.core.blackboard.IncrementalSubscription;
017: import org.cougaar.util.IsInstanceOf;
018: import org.cougaar.util.UnaryPredicate;
019: import org.cougaar.util.annotations.Cougaar;
020: import org.cougaar.util.annotations.Subscribe;
021:
022: /**
023: * This class provides support for plugins that wish
024: * to use annotations to create and manage blackboard
025: * IncrementalSubscriptions.
026: */
027: public abstract class AnnotatedSubscriptionsPlugin extends
028: ParameterizedPlugin {
029: private final Map<String, IncrementalSubscription> subscriptions = new HashMap<String, IncrementalSubscription>();
030: private final List<Invoker> invokers = new ArrayList<Invoker>();
031:
032: protected void execute() {
033: for (Invoker invoker : invokers) {
034: invoker.execute();
035: }
036: }
037:
038: protected void setupSubscriptions() {
039: for (Method method : getClass().getMethods()) {
040: if (method.isAnnotationPresent(Cougaar.Execute.class)) {
041: Cougaar.Execute annotation = method
042: .getAnnotation(Cougaar.Execute.class);
043: String id;
044: String when = annotation.when();
045: if (!Cougaar.NO_VALUE.equals(when)) {
046: id = when;
047: } else {
048: // Use the type of the first arg as an implicit 'isa'
049: Class<?>[] parameterTypes = method
050: .getParameterTypes();
051: if (parameterTypes.length != 1) {
052: String message = method.getName()
053: + " of class "
054: + getClass().getName()
055: + " has the wrong number of arguments (should be 1)";
056: log.error(message);
057: continue;
058: }
059: id = parameterTypes[0].getName();
060: }
061: IncrementalSubscription subscription = subscriptions
062: .get(id);
063: if (subscription != null) {
064: Invoker invoker = new Invoker(method, annotation,
065: subscription);
066: invokers.add(invoker);
067: } else {
068: Invoker invoker = new Invoker(method, annotation);
069: subscriptions.put(id, invoker.sub);
070: invokers.add(invoker);
071: }
072: }
073: }
074: }
075:
076: public IncrementalSubscription getSubscription(String id) {
077: return subscriptions.get(id);
078: }
079:
080: /**
081: * Keeps the state of one IncrementalSubscription created via annotations.
082: */
083: private class Invoker {
084: /**
085: * The subscription itself.
086: */
087: private final IncrementalSubscription sub;
088:
089: /**
090: * The annotated method which will be invoked on the plugin, passing in each element
091: * of an IncrementalSubscription collection in turn as an argument.
092: */
093: private final Method method;
094:
095: /**
096: * The set of operations, as given in the annotation, which determines
097: * which ncrementalSubscription collection are relevant.
098: */
099: private final Subscribe.ModType[] ops;
100:
101: public Invoker(Method method, Cougaar.Execute annotation,
102: IncrementalSubscription sub) {
103: this .method = method;
104: this .ops = annotation.on();
105: this .sub = sub;
106: }
107:
108: public Invoker(Method method, Cougaar.Execute annotation) {
109: this .method = method;
110: this .ops = annotation.on();
111: this .sub = createIncrementalSubscription(annotation);
112:
113: }
114:
115: @SuppressWarnings("deprecation")
116: private boolean isTesterMethod(Method method, String name,
117: Class<?> argClass) {
118: if (method.isAnnotationPresent(Cougaar.Predicate.class)) {
119: Cougaar.Predicate pred = method
120: .getAnnotation(Cougaar.Predicate.class);
121: if (!name.equals(pred.when())) {
122: return false;
123: }
124: } else if (!name.equals(method.getName())) {
125: return false;
126: }
127: Class<?>[] paramTypes = method.getParameterTypes();
128: if (paramTypes.length != 1) {
129: return false;
130: }
131: if (!argClass.isAssignableFrom(paramTypes[0])) {
132: return false;
133: }
134: Class<?> returnType = method.getReturnType();
135: return returnType == Boolean.class
136: || returnType == boolean.class;
137: }
138:
139: private IncrementalSubscription createIncrementalSubscription(
140: Cougaar.Execute annotation) {
141: String when = annotation.when();
142: UnaryPredicate predicate;
143: if (Cougaar.NO_VALUE.equals(when)) {
144: // Implicit instanceof if no 'when'
145: Class<?> isa = method.getParameterTypes()[0];
146: predicate = new IsInstanceOf(isa);
147: } else {
148: // Construct a UnaryPredicate from the 'when'
149: Class<?> pluginClass = AnnotatedSubscriptionsPlugin.this
150: .getClass();
151: Method[] methods = pluginClass.getMethods();
152: Class<?> argClass = method.getParameterTypes()[0];
153: Method testerMethod = null;
154: for (Method method : methods) {
155: if (isTesterMethod(method, when, argClass)) {
156: testerMethod = method;
157: break;
158: }
159: }
160: if (testerMethod == null) {
161: log.error("Couldn't find method " + when + " ("
162: + argClass + ")");
163: return null;
164: }
165: final Method tester = testerMethod;
166: final Class<?> testerArgClass = tester
167: .getParameterTypes()[0];
168: predicate = new UnaryPredicate() {
169: public boolean execute(Object o) {
170: if (!testerArgClass.isAssignableFrom(o
171: .getClass())) {
172: return false;
173: }
174: try {
175: return (Boolean) tester.invoke(
176: AnnotatedSubscriptionsPlugin.this ,
177: o);
178: } catch (Exception e) {
179: log.error("Test failed", e);
180: return false;
181: }
182: }
183:
184: };
185: }
186: return (IncrementalSubscription) blackboard
187: .subscribe(predicate);
188: }
189:
190: private Collection<?> getCollection(Subscribe.ModType op) {
191: switch (op) {
192: case ADD:
193: return sub.getAddedCollection();
194:
195: case CHANGE:
196: return sub.getChangedCollection();
197:
198: case REMOVE:
199: return sub.getRemovedCollection();
200:
201: default:
202: return null;
203: }
204: }
205:
206: public void execute() {
207: if (sub == null || !sub.hasChanged()) {
208: // failed to make a proper subscription, or no changes
209: return;
210: }
211: for (Subscribe.ModType op : ops) {
212: Collection<?> objects = getCollection(op);
213: if (objects != null) {
214: for (Object object : objects) {
215: try {
216: method.invoke(
217: AnnotatedSubscriptionsPlugin.this ,
218: object);
219: } catch (Exception e) {
220: log
221: .error(
222: "Failed to invoke annotated method",
223: e);
224: }
225: }
226: }
227: }
228: }
229: }
230:
231: }
|