/****************************************************************************** * Copyright (c) 2006, 2010 VMware Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0 * is available at http://www.opensource.org/licenses/apache2.0.php. * You may elect to redistribute this code under either of these licenses. * * Contributors: * VMware Inc. *****************************************************************************/ package org.eclipse.gemini.blueprint.context.support; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.PrivilegedAction; import org.eclipse.gemini.blueprint.util.OsgiFilterUtils; import org.eclipse.gemini.blueprint.util.OsgiServiceReferenceUtils; import org.eclipse.gemini.blueprint.util.internal.ClassUtils; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; /** * Utility class for easy, but reliable, tracking of OSGi services. It does service tracking internally but wraps the * logic into a proxy. Note that this class uses raw JDK proxies and thus is usable only with interfaces. This allows to * create simple proxies with minimal class dependencies. * * <p/> This class can be seen as a much shorter, less featured version of * {@link org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceProxyFactoryBean} . It is intended for the * bootstrap areas of the project where no classloading or listeners are required. * * @author Costin Leau * */ abstract class TrackingUtil { /** * JDK Proxy invocation handler that delegates all calls to services found in the OSGi space at the time of the * call, falling back to a given object. * * @author Costin Leau */ private static class OsgiServiceHandler implements InvocationHandler { private final Object fallbackObject; private final BundleContext context; private final String filterClassName; private final String filter; private final boolean securityOn; /** * flag used to bypass the OSGi space if the context becomes unavailable */ private volatile boolean bundleContextInvalidated = false; public OsgiServiceHandler(Object fallbackObject, BundleContext bundleContext, String filterClass, String filter) { this.fallbackObject = fallbackObject; this.context = bundleContext; this.filterClassName = filterClass; this.filter = filter; this.securityOn = (System.getSecurityManager() != null); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // fast dispatch if (method.getName().equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); } else if (method.getName().equals("hashCode")) { // Use hashCode of Session proxy. return new Integer(System.identityHashCode(proxy)); } Object target = null; if (!bundleContextInvalidated) { try { if (securityOn) { target = AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return getTarget(context, filter); } }); } else { target = getTarget(context, filter); } } catch (IllegalStateException ise) { // context has been invalidated bundleContextInvalidated = true; } } if (target == null) { target = fallbackObject; } // re-route call to the target try { Object result = method.invoke(target, args); return result; } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } private Object getTarget(BundleContext context, String filter) { ServiceReference ref = OsgiServiceReferenceUtils.getServiceReference(context, filterClassName, filter); return (ref != null ? context.getService(ref) : null); } } /** * Returns a proxy that on each call seeks the relevant OSGi service and delegates the method invocation to it. In * case no service is found, the fallback object is used. * * <p/> Since JDK proxies are used to create services only interfaces are used. * * @param classes array of classes used during proxy weaving * @param filter OSGi filter (can be null) * @param classLoader class loader to use - normally classes.getClassLoader() * @param context bundle context used for searching the services * @param fallbackObject object to fall back onto if no OSGi service is found. * @return the proxy doing the lookup on each method invocation */ static Object getService(Class<?>[] classes, String filter, ClassLoader classLoader, BundleContext context, Object fallbackObject) { // mold the proxy String flt = OsgiFilterUtils.unifyFilter(classes, filter); return Proxy.newProxyInstance(classLoader, classes, new OsgiServiceHandler(fallbackObject, context, ClassUtils .getParticularClass(classes).getName(), flt)); } }