| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.camel.core.osgi; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| import org.apache.camel.RuntimeCamelException; |
| import org.apache.camel.Service; |
| import org.apache.camel.spi.BeanRepository; |
| import org.apache.camel.support.LifecycleStrategySupport; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceEvent; |
| import org.osgi.framework.ServiceListener; |
| import org.osgi.framework.ServiceReference; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * The OSGi {@link BeanRepository} support to lookup beans from the OSGi service registry from its bundle context. |
| */ |
| public class OsgiBeanRepository extends LifecycleStrategySupport implements BeanRepository, Service, ServiceListener { |
| private static final Logger LOG = LoggerFactory.getLogger(OsgiBeanRepository.class); |
| private final BundleContext bundleContext; |
| private final Map<ServiceReference<?>, AtomicLong> serviceReferenceUsageMap = new ConcurrentHashMap<>(); |
| |
| public OsgiBeanRepository(BundleContext bc) { |
| bundleContext = bc; |
| bundleContext.addServiceListener(this); |
| } |
| |
| /** |
| * Support to lookup the Object with filter with the (name=NAME) and class type |
| */ |
| public <T> T lookupByNameAndType(String name, Class<T> type) { |
| Object service = null; |
| ServiceReference<?> sr; |
| try { |
| ServiceReference<?>[] refs = bundleContext.getServiceReferences(type.getName(), "(name=" + name + ")"); |
| if (refs != null && refs.length > 0) { |
| // just return the first one |
| sr = refs[0]; |
| incrementServiceUsage(sr); |
| service = bundleContext.getService(sr); |
| } |
| } catch (Exception ex) { |
| throw RuntimeCamelException.wrapRuntimeCamelException(ex); |
| } |
| service = unwrap(service); |
| return type.cast(service); |
| } |
| |
| /** |
| * It's only support to look up the ServiceReference with Class name or service PID |
| */ |
| public Object lookupByName(String name) { |
| Object service = null; |
| ServiceReference<?> sr = bundleContext.getServiceReference(name); |
| if (sr == null) { |
| // trying to lookup service by PID if not found by name |
| String filterExpression = "(" + Constants.SERVICE_PID + "=" + name + ")"; |
| try { |
| ServiceReference<?>[] refs = bundleContext.getServiceReferences((String)null, filterExpression); |
| if (refs != null && refs.length > 0) { |
| // just return the first one |
| sr = refs[0]; |
| } |
| } catch (InvalidSyntaxException ex) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Invalid OSGi service reference filter, skipped lookup by service.pid. Filter expression: {}", filterExpression, ex); |
| } |
| } |
| } |
| if (sr != null) { |
| incrementServiceUsage(sr); |
| service = bundleContext.getService(sr); |
| } |
| service = unwrap(service); |
| return service; |
| } |
| |
| public <T> Map<String, T> findByTypeWithName(Class<T> type) { |
| Map<String, T> result = new HashMap<>(); |
| int count = 0; |
| try { |
| ServiceReference<?>[] refs = bundleContext.getAllServiceReferences(type.getName(), null); |
| if (refs != null) { |
| for (ServiceReference<?> sr : refs) { |
| if (sr != null) { |
| Object service = bundleContext.getService(sr); |
| incrementServiceUsage(sr); |
| if (service != null) { |
| String name = (String)sr.getProperty("name"); |
| service = unwrap(service); |
| if (name != null) { |
| result.put(name, type.cast(service)); |
| } else { |
| // generate a unique name for it |
| result.put(type.getSimpleName() + count, type.cast(service)); |
| count++; |
| } |
| } |
| } |
| } |
| } |
| } catch (Exception ex) { |
| throw RuntimeCamelException.wrapRuntimeCamelException(ex); |
| } |
| return result; |
| } |
| |
| public <T> Set<T> findByType(Class<T> type) { |
| Map<String, T> map = findByTypeWithName(type); |
| return new HashSet<>(map.values()); |
| } |
| |
| @Override |
| public void start() { |
| // noop |
| } |
| |
| @Override |
| public void stop() { |
| // Unget the OSGi service as OSGi uses reference counting |
| // and we should do this as one of the last actions when stopping Camel |
| this.serviceReferenceUsageMap.forEach(this::drainServiceUsage); |
| this.serviceReferenceUsageMap.clear(); |
| } |
| |
| private void drainServiceUsage(ServiceReference<?> serviceReference, AtomicLong serviceUsageCount) { |
| if (serviceUsageCount != null && serviceReference != null) { |
| while (serviceUsageCount.decrementAndGet() >= 0) { |
| this.bundleContext.ungetService(serviceReference); |
| } |
| } |
| } |
| |
| private void incrementServiceUsage(ServiceReference<?> sr) { |
| AtomicLong serviceUsageCount = this.serviceReferenceUsageMap.get(sr); |
| if (serviceUsageCount != null) { |
| serviceUsageCount.incrementAndGet(); |
| } else { |
| this.serviceReferenceUsageMap.merge(sr, new AtomicLong(1), |
| (existingServiceUsageCount, newServiceUsageCount) -> { |
| existingServiceUsageCount.getAndAdd(newServiceUsageCount.get()); |
| return existingServiceUsageCount; |
| }); |
| } |
| } |
| |
| @Override |
| public void serviceChanged(ServiceEvent event) { |
| if (event.getType() == ServiceEvent.UNREGISTERING) { |
| ServiceReference<?> serviceReference = event.getServiceReference(); |
| AtomicLong serviceUsageCount = this.serviceReferenceUsageMap.remove(serviceReference); |
| drainServiceUsage(serviceReference, serviceUsageCount); |
| } |
| } |
| } |