/** * Copyright OPS4J * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ops4j.pax.wicket.internal.injection.registry; import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.ops4j.pax.wicket.spi.FutureProxyTargetLocator; import org.ops4j.pax.wicket.spi.ProxyTarget; import org.ops4j.pax.wicket.spi.ReleasableProxyTarget; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class locates the ProxyTarget through the OSGi service registry. It will locate an arbitary service by the * service class if the bean name is not specified. If a bean name is specified, it tries to locate a Declarative * Component with the given name and service interface * * @author Christoph Läubrich * @version $Id: $Id */ public class OSGiServiceRegistryProxyTargetLocator implements FutureProxyTargetLocator { private static final Logger LOGGER = LoggerFactory.getLogger(OSGiServiceRegistryProxyTargetLocator.class); private static final long serialVersionUID = -5726156325232163363L; private final BundleContext bundleContext; private final String serviceInterface; private final Class<?> parent; private final String filterString; /** * <p>Constructor for OSGiServiceRegistryProxyTargetLocator.</p> * * @param pageClass a {@link java.lang.Class} object. * @param serviceClass a {@link java.lang.Class} object. * @param callingContext a {@link org.osgi.framework.BundleContext} object. * @param baseFilter a {@link org.osgi.framework.Filter} object. */ public OSGiServiceRegistryProxyTargetLocator(BundleContext callingContext, Filter baseFilter, Class<?> serviceClass, Class<?> pageClass) { bundleContext = callingContext; this.filterString = getFilterString(baseFilter); this.parent = pageClass; serviceInterface = serviceClass.getName(); } /** * <p>locateProxyTarget.</p> * * @return a {@link org.ops4j.pax.wicket.spi.ReleasableProxyTarget} object. */ public ReleasableProxyTarget locateProxyTarget() { ServiceReference<?>[] references = fetchReferences(); if (references != null) { // Sort the references... Arrays.sort(references); // Fetch the first (if any)... for (final ServiceReference<?> reference : references) { final Object service = bundleContext.getService(reference); if (service == null) { // The service is gone while we where iterating over the service references... continue; } // And return a releasable proxy target... return new ReleasableProxyTargetImplementation(service, reference); } } throw new IllegalStateException("can't find any service matching objectClass = " + serviceInterface + " and filter = " + filterString); } /** * <p>fetchReferences.</p> * * @return an array of {@link org.osgi.framework.ServiceReference} objects. */ public ServiceReference<?>[] fetchReferences() { try { LOGGER.debug("Try to locate a suitable service for objectClass = " + serviceInterface + " and filter = " + filterString); return bundleContext.getAllServiceReferences(serviceInterface, filterString); } catch (InvalidSyntaxException e) { LOGGER.error("Creation of filter failed: {}", e.getMessage(), e); throw new RuntimeException("Creation of filter failed", e); } } private String getFilterString(Filter baseFilter) { if (baseFilter != null) { return baseFilter.toString(); } else { return null; } } /** * A releasable Proxy Target for a specific Service Reference * * @author Christoph Läubrich */ private final class ReleasableProxyTargetImplementation implements ReleasableProxyTarget { /** * the service object */ private Object service; /** * the reference which produced it */ private final ServiceReference<?> reference; private ReleasableProxyTarget delegatingProxy; /** * @param service * @param reference */ private ReleasableProxyTargetImplementation(Object service, ServiceReference<?> reference) { this.service = service; this.reference = reference; } public synchronized Object getTarget() throws IllegalStateException { if (service == null) { // The service was released before, try to reaquire it... LOGGER.debug("Try to reaquire service..."); Object reaquiredService = bundleContext.getService(reference); if (reaquiredService != null) { // Successfull reaquired! LOGGER.debug("reaquire service was successfull"); service = reaquiredService; } else { LOGGER.debug("reaquire service was not successfull, try to relocate to a different service..."); // Try to find a new one... ReleasableProxyTarget newProxyTarget = locateProxyTarget(); // If we are here a new ProxyTarget was bound delegatingProxy = newProxyTarget; // Fetch the target from the delegate service = newProxyTarget.getTarget(); } } return service; } public synchronized ProxyTarget releaseTarget() { // When releasing, we unget the service so we don't keep stale references... service = null; if (delegatingProxy != null) { // return the delegate proxy for further usage... return delegatingProxy.releaseTarget(); } else { try { bundleContext.ungetService(reference); } catch (RuntimeException e) { // Sometimes a RuntimeException might occur here, we catch it to not prevent any other // cleanup actions LOGGER.trace("RuntimeException while ungetting service", e); } return this; } } } /** * <p>Getter for the field <code>parent</code>.</p> * * @return a {@link java.lang.Class} object. */ public Class<?> getParent() { return parent; } /** {@inheritDoc} */ public ProxyTarget locateProxyTarget(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { String filter = filterString; if (filter == null) { filter = String.format("(%s=%s)", Constants.OBJECTCLASS, serviceInterface); } else { filter = String.format("(&(%s=%s)%s)", Constants.OBJECTCLASS, serviceInterface, filter); } try { final ServiceTracker<Object, Object> tracker = new ServiceTracker<Object, Object>(bundleContext, bundleContext.createFilter(filter), null); tracker.open(); final Object service = tracker.waitForService(unit.toMillis(timeout)); if (service == null) { throw new TimeoutException("no service for filter = " + filter + " was avaiable in time"); } return new ReleasableProxyTarget() { public Object getTarget() { return service; } public ProxyTarget releaseTarget() { tracker.close(); return null; } }; } catch (InvalidSyntaxException e) { throw new RuntimeException("filter creation failed", e); } } }