001: /*
002: * <copyright>
003: *
004: * Copyright 2000-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: package org.cougaar.core.component;
027:
028: import java.lang.reflect.InvocationHandler;
029: import java.lang.reflect.InvocationTargetException;
030: import java.lang.reflect.Method;
031: import java.lang.reflect.Proxy;
032: import java.util.Collections;
033: import java.util.Iterator;
034: import java.util.LinkedHashMap;
035: import java.util.List;
036: import java.util.Map;
037:
038: import org.cougaar.util.ChainingIterator;
039:
040: /**
041: * A per-component {@link ServiceBroker} proxy that supports the
042: * {@link ViewService}.
043: */
044: class ViewedServiceBroker implements ExtendedServiceBroker {
045:
046: private final ServiceBroker delegate;
047: private final int myId;
048: private final ComponentDescription myDesc;
049: private final ComponentView view;
050: private final Object lock = new Object();
051: private Map advertisedServices;
052: private Map obtainedServices;
053:
054: public ViewedServiceBroker(ServiceBroker delegate, int id,
055: ComponentDescription cd, ContainerView parentView, Binder b) {
056: this .delegate = delegate;
057: this .myId = id;
058: this .myDesc = cd;
059: this .view = createComponentView(id, cd, parentView, b);
060: }
061:
062: public void addServiceListener(ServiceListener sl) {
063: // tell the listener about our ViewService
064: if (sl instanceof ServiceAvailableListener) {
065: ((ServiceAvailableListener) sl)
066: .serviceAvailable(new ServiceAvailableEvent(this ,
067: ViewService.class));
068: }
069: // RFE: record for auto-remove
070: delegate.addServiceListener(sl);
071: }
072:
073: public void removeServiceListener(ServiceListener sl) {
074: delegate.addServiceListener(sl);
075: }
076:
077: public boolean addService(Class serviceClass,
078: ServiceProvider serviceProvider) {
079: return addService(serviceClass, serviceProvider, myId, myDesc);
080: }
081:
082: public boolean addService(Class serviceClass,
083: ServiceProvider serviceProvider, int providerId,
084: ComponentDescription providerDesc) {
085: if (serviceClass == ViewService.class) {
086: return false;
087: }
088: boolean ret;
089: if (delegate instanceof ExtendedServiceBroker) {
090: ExtendedServiceBroker esb = (ExtendedServiceBroker) delegate;
091: ret = esb.addService(serviceClass, serviceProvider,
092: providerId, providerDesc);
093: } else {
094: ret = delegate.addService(serviceClass, serviceProvider);
095: }
096: if (ret) {
097: synchronized (lock) {
098: if (advertisedServices == null) {
099: advertisedServices = new LinkedHashMap();
100: }
101: // RFE: "put(cl, weakRef(sp))" for auto-revoke
102: advertisedServices.put(serviceClass,
103: createServiceData());
104: }
105: }
106: return ret;
107: }
108:
109: public void revokeService(Class serviceClass,
110: ServiceProvider serviceProvider) {
111: revokeService(serviceClass, serviceProvider, myId, myDesc);
112: }
113:
114: public void revokeService(Class serviceClass,
115: ServiceProvider serviceProvider, int providerId,
116: ComponentDescription providerDesc) {
117: if (serviceClass == ViewService.class) {
118: return;
119: }
120: if (delegate instanceof ExtendedServiceBroker) {
121: ExtendedServiceBroker esb = (ExtendedServiceBroker) delegate;
122: esb.revokeService(serviceClass, serviceProvider,
123: providerId, providerDesc);
124: } else {
125: delegate.revokeService(serviceClass, serviceProvider);
126: }
127: synchronized (lock) {
128: ServiceData sd = (advertisedServices == null ? (null)
129: : ((ServiceData) advertisedServices
130: .get(serviceClass)));
131: if (sd == null) {
132: // shouldn't happen unless the client never did
133: // "addService", or already revoked this service
134: } else {
135: sd.release();
136: }
137: }
138: }
139:
140: public boolean hasService(Class serviceClass) {
141: return (serviceClass == ViewService.class || delegate
142: .hasService(serviceClass));
143: }
144:
145: public Iterator getCurrentServiceClasses() {
146: return new ChainingIterator(new Iterator[] {
147: Collections.singleton(ViewService.class).iterator(),
148: delegate.getCurrentServiceClasses() });
149: }
150:
151: public <T> T getService(Object requestor, Class<T> serviceClass,
152: ServiceRevokedListener srl) {
153: ServiceResult sr = getService(myId, myDesc, requestor,
154: serviceClass, srl, true);
155: return (sr == null ? null : (T) sr.getService());
156: }
157:
158: public ServiceResult getService(int requestorId,
159: ComponentDescription requestorDesc, Object requestor,
160: Class serviceClass, ServiceRevokedListener srl,
161: boolean recordInView) {
162: if (!recordInView) {
163: if (delegate instanceof ExtendedServiceBroker) {
164: ExtendedServiceBroker esb = (ExtendedServiceBroker) delegate;
165: return esb.getService(requestorId, requestorDesc,
166: requestor, serviceClass, srl, false);
167: } else {
168: Object service = delegate.getService(requestor,
169: serviceClass, srl);
170: ServiceResult sr = (service == null ? (null)
171: : new ServiceResult(0, null, service));
172: return sr;
173: }
174: }
175:
176: ServiceResult sr;
177: if (serviceClass == ViewService.class) {
178: // view service, act as if the component provided
179: // this to itself
180: ViewService vs = new ViewService() {
181: public ComponentView getComponentView() {
182: return view;
183: }
184:
185: public String toString() {
186: return "(view of " + view + ")";
187: }
188: };
189: sr = new ServiceResult(myId, myDesc, vs);
190: } else if (delegate instanceof ExtendedServiceBroker) {
191: ExtendedServiceBroker esb = (ExtendedServiceBroker) delegate;
192: sr = esb.getService(requestorId, requestorDesc, requestor,
193: serviceClass, srl, true);
194: } else {
195: Object service = delegate.getService(requestor,
196: serviceClass, srl);
197: sr = (service == null ? (null) : new ServiceResult(0, null,
198: service));
199: }
200: if (sr != null && sr.getService() != null) {
201: boolean containsGetSB = containsGetServiceBroker(serviceClass);
202: if (containsGetSB) {
203: // proxy so we can watch indirectly-advertised services
204: Object service = sr.getService();
205: Object proxy = Proxy.newProxyInstance(service
206: .getClass().getClassLoader(), service
207: .getClass().getInterfaces(),
208: new IndirectSBSProxy(serviceClass, service));
209: sr = new ServiceResult(sr.getProviderId(), sr
210: .getProviderComponentDescription(), proxy);
211: }
212: synchronized (lock) {
213: if (obtainedServices == null) {
214: obtainedServices = new LinkedHashMap();
215: }
216: // RFE: "put(cl, weakRef(ret))" for auto-release
217: obtainedServices.put(serviceClass, createServiceData(sr
218: .getProviderId(), sr
219: .getProviderComponentDescription(),
220: containsGetSB));
221: }
222: }
223: return sr;
224: }
225:
226: public void releaseService(Object requestor, Class serviceClass,
227: Object service) {
228: releaseService(myId, myDesc, requestor, serviceClass, service,
229: true);
230: }
231:
232: public void releaseService(int requestorId,
233: ComponentDescription requestorDesc, Object requestor,
234: Class serviceClass, Object service, boolean recordInView) {
235: if (serviceClass != ViewService.class) {
236: if (delegate instanceof ExtendedServiceBroker) {
237: ExtendedServiceBroker esb = (ExtendedServiceBroker) delegate;
238: esb.releaseService(requestorId, requestorDesc,
239: requestor, serviceClass, service, recordInView);
240: } else {
241: delegate.releaseService(requestor, serviceClass,
242: service);
243: }
244: }
245: if (!recordInView) {
246: return;
247: }
248: synchronized (lock) {
249: ServiceData sd = (obtainedServices == null ? (null)
250: : ((ServiceData) obtainedServices.get(serviceClass)));
251: if (sd == null) {
252: // shouldn't happen unless client never did "getService"
253: // or already released this service. Also, it's possible
254: // that the client obtained multiple service instances...
255: } else {
256: sd.release();
257: }
258: }
259: }
260:
261: //
262: // package-private methods for ContainerSupport use:
263: //
264:
265: ComponentView getComponentView() {
266: return view;
267: }
268:
269: private static final Object counterLock = new Object();
270: private static int counter;
271:
272: static final int nextId() {
273: synchronized (counterLock) {
274: return ++counter;
275: }
276: }
277:
278: //
279: // the rest is private:
280: //
281:
282: // RFE: finalize or equiv auto-cleanup
283:
284: private Map getAdvertisedServices() {
285: synchronized (lock) {
286: return copyServiceViews(advertisedServices);
287: }
288: }
289:
290: private Map getObtainedServices() {
291: synchronized (lock) {
292: return copyServiceViews(obtainedServices);
293: }
294: }
295:
296: private static final Map copyServiceViews(Map orig) {
297: int n = (orig == null ? 0 : orig.size());
298: if (n <= 0) {
299: return Collections.EMPTY_MAP;
300: }
301: Map m = new LinkedHashMap(n);
302: Iterator iter = orig.entrySet().iterator();
303: for (int i = 0; i < n; i++) {
304: Map.Entry me = (Map.Entry) iter.next();
305: Object key = me.getKey();
306: Object value = me.getValue();
307: if (key instanceof Class && value instanceof ServiceData) {
308: m.put(key, ((ServiceData) value).toServiceView());
309: }
310: }
311: m = Collections.unmodifiableMap(m);
312: return m;
313: }
314:
315: private void indirectAddService(Class indirectServiceClass,
316: Class serviceClass, ServiceProvider serviceProvider) {
317: synchronized (lock) {
318: ServiceData sd = (obtainedServices == null ? (null)
319: : ((ServiceData) obtainedServices
320: .get(indirectServiceClass)));
321: if (sd == null) {
322: // this shouldn't happen; they should minimally still
323: // have the indirectServiceClass!
324: } else {
325: sd.findOrMakeIndirect(serviceClass);
326: }
327: }
328: }
329:
330: private void indirectRevokeService(Class indirectServiceClass,
331: Class serviceClass, ServiceProvider serviceProvider) {
332: synchronized (lock) {
333: ServiceData sd = (obtainedServices == null ? (null)
334: : ((ServiceData) obtainedServices
335: .get(indirectServiceClass)));
336: if (sd == null) {
337: // this shouldn't happen; they should minimally still
338: // have the indirectServiceClass!
339: } else {
340: ServiceData sd2 = sd.findOrMakeIndirect(serviceClass);
341: sd2.release();
342: }
343: }
344: }
345:
346: /**
347: * Dynamic proxy that intercepts calls to "get.*ServiceBroker"
348: * methods and replaces the returned ServiceBrokers with
349: * {@link SBProxy}s, so we can track "addService" and
350: * "revokeService" calls.
351: * <p>
352: * The proxy preserves extended interfaces, such as core's
353: * {@link org.cougaar.core.node.NodeControlService}.
354: */
355: private final class IndirectSBSProxy implements InvocationHandler {
356:
357: private final Class indirectServiceClass;
358: private final Object o;
359:
360: public IndirectSBSProxy(Class indirectServiceClass, Object o) {
361: this .indirectServiceClass = indirectServiceClass;
362: this .o = o;
363: }
364:
365: public Object invoke(Object proxy, Method method, Object[] args)
366: throws Throwable {
367: try {
368: Object ret = method.invoke(o, args);
369: if (ret != null && isGetServiceBroker(method)) {
370: // replace ServiceBroker with yet another proxy
371: //
372: // we're forced to hard-code the proxy interfaces
373: // to just "ServiceBroker", since the standard
374: // implementation is a *class* (our container's
375: // "DefaultServiceBroker"), and "getInterfaces()"
376: // on a class returns an empty array.
377: //
378: // The only potential downside is if the service
379: // client attempts to downcast the returned
380: // ServiceBroker, which would fail due to our
381: // proxy.
382: Class[] interfaces = new Class[] { ServiceBroker.class };
383: Object sbProxy = Proxy.newProxyInstance(ret
384: .getClass().getClassLoader(), interfaces,
385: new SBProxy(indirectServiceClass, ret));
386: ret = sbProxy;
387: }
388: return ret;
389: } catch (InvocationTargetException e) {
390: throw e.getTargetException();
391: }
392: }
393: }
394:
395: /**
396: * Dynamic proxy to intercept calls to
397: * {@link ServiceBroker#addService} and
398: * {@link ServiceBroker#revokeService}.
399: */
400: private final class SBProxy implements InvocationHandler {
401: private final Class indirectServiceClass;
402: private final Object o;
403:
404: public SBProxy(Class indirectServiceClass, Object o) {
405: this .indirectServiceClass = indirectServiceClass;
406: this .o = o;
407: }
408:
409: public Object invoke(Object proxy, Method method, Object[] args)
410: throws Throwable {
411: try {
412: Object ret;
413: if (isAddService(method)) {
414: if (o instanceof ExtendedServiceBroker) {
415: ExtendedServiceBroker esb = (ExtendedServiceBroker) o;
416: boolean b = esb.addService(((Class) args[0]),
417: ((ServiceProvider) args[1]), myId,
418: myDesc);
419: ret = Boolean.valueOf(b);
420: } else {
421: ret = method.invoke(o, args);
422: }
423: if (Boolean.TRUE.equals(ret)) {
424: indirectAddService(indirectServiceClass,
425: (Class) args[0],
426: (ServiceProvider) args[1]);
427: }
428: } else if (isRevokeService(method)) {
429: if (o instanceof ExtendedServiceBroker) {
430: ExtendedServiceBroker esb = (ExtendedServiceBroker) o;
431: esb.revokeService(((Class) args[0]),
432: ((ServiceProvider) args[1]), myId,
433: myDesc);
434: ret = null;
435: } else {
436: ret = method.invoke(o, args);
437: }
438: indirectRevokeService(indirectServiceClass,
439: (Class) args[0], (ServiceProvider) args[1]);
440: } else {
441: ret = method.invoke(o, args);
442: }
443: return ret;
444: } catch (InvocationTargetException e) {
445: throw e.getTargetException();
446: }
447: }
448: }
449:
450: // method matching utilities
451: private static boolean containsGetServiceBroker(Class cl) {
452: Method[] methods;
453: try {
454: methods = cl.getMethods();
455: } catch (Exception e) {
456: // security exception?
457: methods = null;
458: }
459: int n = (methods == null ? 0 : methods.length);
460: for (int i = 0; i < n; i++) {
461: Method m = methods[i];
462: if (isGetServiceBroker(m)) {
463: return true;
464: }
465: }
466: return false;
467: }
468:
469: private static boolean isGetServiceBroker(Method m) {
470: if (m != null) {
471: String name = m.getName();
472: if (name.startsWith("get")
473: && name.endsWith("ServiceBroker")) {
474: Class ret = m.getReturnType();
475: if (ret == ServiceBroker.class) {
476: Class[] params = m.getParameterTypes();
477: if (params == null || params.length == 0) {
478: return true;
479: }
480: }
481: }
482: }
483: return false;
484: }
485:
486: private static boolean isAddService(Method m) {
487: return methodMatches(ADD_SERVICE_METHOD, m);
488: }
489:
490: private static boolean isRevokeService(Method m) {
491: return methodMatches(REVOKE_SERVICE_METHOD, m);
492: }
493:
494: // similar to a.equals(b), but skip the class comparison
495: private static boolean methodMatches(Method a, Method b) {
496: // note that names are interned; see Method docs
497: if (a.getName() == b.getName()) {
498: // unlike Method.equals, we must get the copied arrays.
499: // no big deal...
500: Class[] params1 = a.getParameterTypes();
501: Class[] params2 = b.getParameterTypes();
502: if (params1.length == params2.length) {
503: for (int i = 0; i < params1.length; i++) {
504: if (params1[i] != params2[i])
505: return false;
506: }
507: return true;
508: }
509: }
510: return false;
511: }
512:
513: // statics:
514: private static final Method ADD_SERVICE_METHOD;
515: private static final Method REVOKE_SERVICE_METHOD;
516: static {
517: Method m1 = null;
518: Method m2 = null;
519: try {
520: m1 = ServiceBroker.class.getMethod("addService",
521: new Class[] { Class.class, ServiceProvider.class });
522: m2 = ServiceBroker.class.getMethod("revokeService",
523: new Class[] { Class.class, ServiceProvider.class });
524: } catch (Exception e) {
525: // should not happen!
526: e.printStackTrace();
527: }
528: ADD_SERVICE_METHOD = m1;
529: REVOKE_SERVICE_METHOD = m2;
530: }
531:
532: private final ComponentView createComponentView(int id,
533: ComponentDescription cd, ContainerView parentView, Binder b) {
534: if (b instanceof ContainerBinder) {
535: ContainerBinder cb = (ContainerBinder) b;
536: if (cb.isContainer()) {
537: return new ContainerViewImpl(id, cd, parentView, cb);
538: }
539: }
540: return new ComponentViewImpl(id, cd, parentView);
541: }
542:
543: private class ComponentViewImpl implements ComponentView {
544: private final int id;
545: private final long timestamp = System.currentTimeMillis();
546: private final ComponentDescription desc;
547: private final ContainerView parentView;
548:
549: public ComponentViewImpl(int id, ComponentDescription desc,
550: ContainerView parentView) {
551: this .id = id;
552: this .desc = desc;
553: this .parentView = parentView;
554: }
555:
556: public int getId() {
557: return id;
558: }
559:
560: public long getTimestamp() {
561: return timestamp;
562: }
563:
564: public ComponentDescription getComponentDescription() {
565: return desc;
566: }
567:
568: public ContainerView getParentView() {
569: return parentView;
570: }
571:
572: public Map getAdvertisedServices() {
573: return ViewedServiceBroker.this .getAdvertisedServices();
574: }
575:
576: public Map getObtainedServices() {
577: return ViewedServiceBroker.this .getObtainedServices();
578: }
579:
580: public String toString() {
581: return "(component class="
582: + (desc == null ? "<unknown>" : desc.getClassname())
583: + ")";
584: }
585: }
586:
587: private class ContainerViewImpl extends ComponentViewImpl implements
588: ContainerView {
589: private final ContainerBinder myBinder;
590:
591: public ContainerViewImpl(int id, ComponentDescription desc,
592: ContainerView parentView, ContainerBinder myBinder) {
593: super (id, desc, parentView);
594: this .myBinder = myBinder;
595: }
596:
597: public List getChildViews() {
598: return myBinder.getChildViews();
599: }
600:
601: public String toString() {
602: ComponentDescription desc = getComponentDescription();
603: return "(container class="
604: + (desc == null ? "<unknown>" : desc.getClassname())
605: + ")";
606: }
607: }
608:
609: private static ServiceData createServiceData() {
610: return new AdvertisedServiceData();
611: }
612:
613: private static ServiceData createServiceData(int providerId,
614: ComponentDescription providerDesc, boolean containsIndirects) {
615: if (containsIndirects) {
616: return new IndirectServiceData(providerId, providerDesc);
617: } else if (providerId > 0 || providerDesc != null) {
618: return new ObtainedServiceData(providerId, providerDesc);
619: } else {
620: return new AdvertisedServiceData();
621: }
622: }
623:
624: private abstract static class ServiceData {
625: private final int id = nextId();
626: private long timestamp = System.currentTimeMillis();
627:
628: public void release() {
629: timestamp = 0;
630: }
631:
632: public void obtain() {
633: timestamp = System.currentTimeMillis();
634: }
635:
636: public int getId() {
637: return id;
638: }
639:
640: public long getTimestamp() {
641: return timestamp;
642: }
643:
644: public int getProviderId() {
645: return 0;
646: }
647:
648: public ComponentDescription getProviderComponentDescription() {
649: return null;
650: }
651:
652: public Map getIndirectlyAddedServices() {
653: return null;
654: }
655:
656: public ServiceData findOrMakeIndirect(Class cl) {
657: return null;
658: }
659:
660: public ServiceView toServiceView() {
661: final long t = getTimestamp();
662: final int providerId = getProviderId();
663: final ComponentDescription providerDesc = getProviderComponentDescription();
664: final Map m = copyIndirectlyAddedServices();
665: return new ServiceView() {
666: public int getId() {
667: return id;
668: }
669:
670: public long getTimestamp() {
671: return t;
672: }
673:
674: public int getProviderId() {
675: return providerId;
676: }
677:
678: public ComponentDescription getProviderComponentDescription() {
679: return providerDesc;
680: }
681:
682: public Map getIndirectlyAdvertisedServices() {
683: return m;
684: }
685:
686: public String toString() {
687: return "(service-view id=" + id + " timestamp=" + t
688: + " providerId=" + providerId
689: + " providerDesc=" + providerDesc
690: + " indirects=" + m + ")";
691: }
692: };
693: }
694:
695: public String toString() {
696: return toServiceView().toString();
697: }
698:
699: private Map copyIndirectlyAddedServices() {
700: Map m = getIndirectlyAddedServices();
701: int n = (m == null ? 0 : m.size());
702: if (n <= 0) {
703: return null;
704: }
705: Map m2 = new LinkedHashMap(n);
706: Iterator iter = m.entrySet().iterator();
707: for (int i = 0; i < n; i++) {
708: Map.Entry me = (Map.Entry) iter.next();
709: Object key = me.getKey();
710: Object value = me.getValue();
711: if (key instanceof Class
712: && value instanceof ServiceData) {
713: m2.put(key, ((ServiceData) value).toServiceView());
714: }
715: }
716: m2 = Collections.unmodifiableMap(m2);
717: return m2;
718: }
719: }
720:
721: private static class AdvertisedServiceData extends ServiceData {
722: }
723:
724: private static class ObtainedServiceData extends ServiceData {
725: private final int providerId;
726: private final ComponentDescription providerDesc;
727:
728: public ObtainedServiceData(int providerId,
729: ComponentDescription providerDesc) {
730: this .providerId = providerId;
731: this .providerDesc = providerDesc;
732: }
733:
734: public int getProviderId() {
735: return providerId;
736: }
737:
738: public ComponentDescription getProviderComponentDescription() {
739: return providerDesc;
740: }
741: }
742:
743: private static class IndirectServiceData extends
744: ObtainedServiceData {
745: private final Map m = new LinkedHashMap();
746:
747: public IndirectServiceData(int providerId,
748: ComponentDescription providerDesc) {
749: super (providerId, providerDesc);
750: }
751:
752: public Map getIndirectlyAddedServices() {
753: return m;
754: }
755:
756: public ServiceData findOrMakeIndirect(Class cl) {
757: ServiceData ret = (ServiceData) m.get(cl);
758: if (ret == null) {
759: ret = createServiceData();
760: m.put(cl, ret);
761: }
762: return ret;
763: }
764: }
765: }
|