| /** |
| * 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 |
| * <p> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p> |
| * 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.apache.winegrower.deployer; |
| |
| import static java.util.Arrays.asList; |
| import static java.util.Optional.ofNullable; |
| import static java.util.stream.Collectors.toList; |
| |
| import java.io.File; |
| import java.io.InputStream; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Dictionary; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| import java.util.function.Supplier; |
| import java.util.jar.Manifest; |
| import java.util.stream.Stream; |
| |
| import org.apache.winegrower.service.BundleRegistry; |
| import org.apache.winegrower.service.OSGiServices; |
| import org.apache.winegrower.service.ServiceReferenceImpl; |
| import org.apache.winegrower.service.ServiceRegistrationImpl; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.BundleListener; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.Filter; |
| import org.osgi.framework.FrameworkListener; |
| import org.osgi.framework.FrameworkUtil; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceFactory; |
| import org.osgi.framework.ServiceListener; |
| import org.osgi.framework.ServiceObjects; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.framework.hooks.service.FindHook; |
| import org.slf4j.LoggerFactory; |
| |
| public class BundleContextImpl implements BundleContext { |
| private static final ServiceReference<?>[] EMPTY_REFS = new ServiceReference<?>[0]; |
| private static final Bundle[] EMPTY_BUNDLES = new Bundle[0]; |
| |
| private final Manifest manifest; |
| private final OSGiServices services; |
| private final Supplier<Bundle> bundleSupplier; |
| private final BundleRegistry registry; |
| private final Collection<BundleListener> bundleListeners = new CopyOnWriteArrayList<>(); |
| private final Collection<FrameworkListener> frameworkListeners = new CopyOnWriteArrayList<>(); |
| private final Map<ServiceReference<?>, Object> serviceInstances = new ConcurrentHashMap<>(); |
| |
| BundleContextImpl(final Manifest manifest, final OSGiServices services, final Supplier<Bundle> bundleSupplier, |
| final BundleRegistry registry) { |
| this.manifest = manifest; |
| this.services = services; |
| this.bundleSupplier = bundleSupplier; |
| this.registry = registry; |
| } |
| |
| public BundleRegistry getRegistry() { |
| return registry; |
| } |
| |
| public Collection<BundleListener> getBundleListeners() { |
| return bundleListeners; |
| } |
| |
| public Collection<FrameworkListener> getFrameworkListeners() { |
| return frameworkListeners; |
| } |
| |
| OSGiServices getServices() { |
| return services; |
| } |
| |
| Manifest getManifest() { |
| return manifest; |
| } |
| |
| @Override |
| public String getProperty(final String key) { |
| return System.getProperty(key); |
| } |
| |
| @Override |
| public Bundle getBundle() { |
| return bundleSupplier.get(); |
| } |
| |
| @Override |
| public Bundle installBundle(final String location, final InputStream input) throws BundleException { |
| throw new BundleException("Unsupported operation"); |
| } |
| |
| @Override |
| public Bundle installBundle(final String location) throws BundleException { |
| throw new BundleException("Unsupported operation"); |
| } |
| |
| @Override |
| public Bundle getBundle(final long id) { |
| return ofNullable(registry.getBundles().get(id)) |
| .map(OSGiBundleLifecycle::getBundle) |
| .map(bundle -> { |
| final List<Bundle> bundles = Stream.of(bundle).collect(toList()); |
| invokeBundleFinHooks(bundles); |
| return bundles.isEmpty() ? null : bundle; |
| }) |
| .orElse(null); |
| } |
| |
| @Override |
| public Bundle[] getBundles() { |
| final List<Bundle> bundles = registry.getBundles().values().stream() |
| .map(OSGiBundleLifecycle::getBundle) |
| .collect(toList()); |
| invokeBundleFinHooks(bundles); |
| return bundles.toArray(EMPTY_BUNDLES); |
| } |
| |
| @Override |
| public void addServiceListener(final ServiceListener listener, final String filter) { |
| services.addListener(listener, filter == null ? null : createFilter(filter), this); |
| } |
| |
| @Override |
| public void addServiceListener(final ServiceListener listener) { |
| addServiceListener(listener, null); |
| } |
| |
| @Override |
| public void removeServiceListener(final ServiceListener listener) { |
| services.removeListener(listener); |
| } |
| |
| @Override |
| public void addBundleListener(final BundleListener listener) { |
| bundleListeners.add(listener); |
| } |
| |
| @Override |
| public void removeBundleListener(final BundleListener listener) { |
| bundleListeners.remove(listener); |
| } |
| |
| @Override |
| public void addFrameworkListener(final FrameworkListener listener) { |
| frameworkListeners.add(listener); |
| } |
| |
| @Override |
| public void removeFrameworkListener(final FrameworkListener listener) { |
| frameworkListeners.remove(listener); |
| } |
| |
| @Override |
| public ServiceRegistration<?> registerService(final String[] classes, final Object service, final Dictionary<String, ?> properties) { |
| return services.registerService(classes, service, properties, bundleSupplier.get()); |
| } |
| |
| @Override |
| public ServiceRegistration<?> registerService(final String clazz, final Object service, final Dictionary<String, ?> properties) { |
| return registerService(new String[]{clazz}, service, properties); |
| } |
| |
| @Override |
| public <S> ServiceRegistration<S> registerService(final Class<S> clazz, final S service, final Dictionary<String, ?> properties) { |
| return (ServiceRegistration<S>) registerService(clazz.getName(), service, properties); |
| } |
| |
| @Override |
| public <S> ServiceRegistration<S> registerService(final Class<S> clazz, final ServiceFactory<S> factory, final Dictionary<String, ?> properties) { |
| return (ServiceRegistration<S>) registerService(clazz.getName(), factory, properties); |
| } |
| |
| @Override |
| public ServiceReference<?>[] getServiceReferences(final String clazz, final String filter) { |
| return doGetReferences(clazz, filter, true); |
| } |
| |
| @Override |
| public ServiceReference<?>[] getAllServiceReferences(final String clazz, final String filter) { |
| return doGetReferences(clazz, filter, false); |
| } |
| |
| @Override |
| public ServiceReference<?> getServiceReference(final String clazz) { |
| return Arrays.stream(getAllServiceReferences(clazz, null)) |
| .findFirst().orElse(null); |
| } |
| |
| @Override |
| public <S> ServiceReference<S> getServiceReference(final Class<S> clazz) { |
| return (ServiceReference<S>) getServiceReference(clazz.getName()); |
| } |
| |
| @Override |
| public <S> Collection<ServiceReference<S>> getServiceReferences(final Class<S> clazz, final String filter) { |
| return Arrays.stream(doGetReferences(clazz.getName(), filter, true)) |
| .map(it -> (ServiceReference<S>) it) |
| .collect(toList()); |
| } |
| |
| @Override |
| public <S> S getService(final ServiceReference<S> reference) { |
| final ServiceReferenceImpl ref = ServiceReferenceImpl.class.cast(reference); |
| if (Constants.SCOPE_BUNDLE.equals(ref.getProperty(Constants.SERVICE_SCOPE))) { |
| Object value = serviceInstances.get(ref); |
| if (value == null) { |
| value = ref.getReference(); |
| final Object existing = serviceInstances.putIfAbsent(ref, value); |
| if (existing != null) { |
| value = existing; |
| } |
| } |
| return (S) value; |
| } |
| return (S) ref.getReference(); |
| } |
| |
| @Override |
| public boolean ungetService(final ServiceReference<?> reference) { |
| final ServiceReferenceImpl serviceReference = ServiceReferenceImpl.class.cast(reference); |
| if (Constants.SCOPE_BUNDLE.equals(serviceReference.getProperty(Constants.SERVICE_SCOPE))) { |
| return serviceInstances.remove(serviceReference) != null; |
| } |
| return serviceReference.unget(null); |
| } |
| |
| @Override |
| public <S> ServiceObjects<S> getServiceObjects(final ServiceReference<S> reference) { |
| return new ServiceObjectsImpl<>(ServiceReferenceImpl.class.cast(reference)); |
| } |
| |
| @Override |
| public File getDataFile(final String filename) { |
| return null; |
| } |
| |
| @Override |
| public Filter createFilter(final String filter) { |
| try { |
| return FrameworkUtil.createFilter(filter); |
| } catch (final InvalidSyntaxException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| @Override |
| public Bundle getBundle(final String location) { |
| return bundleSupplier.get(); |
| } |
| |
| private ServiceReference<?>[] doGetReferences(final String clazz, final String filter, final boolean checkAssignable) { |
| final Filter predicate = filter == null ? null : createFilter(filter); |
| final List<ServiceReference> references = services.getServices().stream() |
| .map(ServiceRegistrationImpl.class::cast) |
| .filter(it -> predicate == null || predicate.match(it.getReference())) |
| .filter(it -> clazz == null || Object.class.getName().equals(clazz) || |
| (it.getClasses() != null && matches(clazz, it))) |
| .map(ServiceRegistration::getReference) |
| .collect(toList()); |
| invokeServiceFindHooks(clazz, filter, checkAssignable, references); |
| return references.toArray(EMPTY_REFS); |
| } |
| |
| private boolean matches(final String clazz, final ServiceRegistrationImpl reg) { |
| return asList(reg.getClasses()).contains(clazz) || deepMatches(clazz, reg); |
| } |
| |
| private boolean deepMatches(final String clazz, final ServiceRegistrationImpl reg) { |
| final Class<?> expected; |
| try { |
| expected = Thread.currentThread().getContextClassLoader().loadClass(clazz); |
| } catch (final ClassNotFoundException e) { |
| return false; |
| } |
| return Stream.of(reg.getClasses()) |
| .flatMap(it -> { |
| try { |
| return Stream.of(reg.getReference().getBundle().loadClass(it)); |
| } catch (final ClassNotFoundException e) { |
| return Stream.empty(); |
| } |
| }) |
| .anyMatch(expected::isAssignableFrom); |
| } |
| |
| private void invokeServiceFindHooks(final String clazz, final String filter, |
| final boolean checkAssignable, final List<ServiceReference> references) { |
| final Collection<ServiceReference<FindHook>> findHooks = services.getHooks().getServiceFindHooks(); |
| if (!references.isEmpty() && !findHooks.isEmpty()) { |
| findHooks.forEach(hook -> { |
| final FindHook fh = getService(hook); |
| if (fh != null) { |
| try { |
| fh.find(getBundle().getBundleContext(), clazz, filter, !checkAssignable, Collection.class.cast(references)); |
| } catch (final Throwable th) { |
| LoggerFactory.getLogger(BundleContextImpl.class).warn("Can't call '{}'", hook, th); |
| } finally { |
| ungetService(hook); |
| } |
| } |
| }); |
| } |
| } |
| |
| private void invokeBundleFinHooks(final List<Bundle> bundles) { |
| final Collection<ServiceReference<org.osgi.framework.hooks.bundle.FindHook>> findHooks = services.getHooks().getBundleFindHooks(); |
| if (!bundles.isEmpty() && !findHooks.isEmpty()) { |
| findHooks.forEach(hook -> { |
| final org.osgi.framework.hooks.bundle.FindHook fh = getService(hook); |
| if (fh != null) { |
| try { |
| fh.find(getBundle().getBundleContext(), bundles); |
| } catch (final Throwable th) { |
| LoggerFactory.getLogger(BundleContextImpl.class).warn("Can't call '{}'", hook, th); |
| } finally { |
| ungetService(hook); |
| } |
| } |
| }); |
| } |
| } |
| } |