| /** |
| * 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.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.camel.CamelContext; |
| import org.apache.camel.Exchange; |
| import org.apache.camel.LoggingLevel; |
| import org.apache.camel.NoTypeConversionAvailableException; |
| import org.apache.camel.RuntimeCamelException; |
| import org.apache.camel.TypeConverter; |
| import org.apache.camel.TypeConverterExists; |
| import org.apache.camel.TypeConverters; |
| import org.apache.camel.impl.DefaultPackageScanClassResolver; |
| import org.apache.camel.impl.converter.DefaultTypeConverter; |
| import org.apache.camel.spi.FactoryFinder; |
| import org.apache.camel.spi.Injector; |
| import org.apache.camel.spi.TypeConverterLoader; |
| import org.apache.camel.spi.TypeConverterRegistry; |
| import org.apache.camel.support.service.ServiceHelper; |
| import org.apache.camel.support.service.ServiceSupport; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.util.tracker.ServiceTracker; |
| import org.osgi.util.tracker.ServiceTrackerCustomizer; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public class OsgiTypeConverter extends ServiceSupport implements TypeConverter, TypeConverterRegistry, |
| ServiceTrackerCustomizer<TypeConverterLoader, Object> { |
| private static final Logger LOG = LoggerFactory.getLogger(OsgiTypeConverter.class); |
| |
| private final BundleContext bundleContext; |
| private final CamelContext camelContext; |
| private final Injector injector; |
| private final FactoryFinder factoryFinder; |
| private final ServiceTracker<TypeConverterLoader, Object> tracker; |
| private volatile DefaultTypeConverter delegate; |
| |
| public OsgiTypeConverter(BundleContext bundleContext, CamelContext camelContext, Injector injector, FactoryFinder factoryFinder) { |
| this.bundleContext = bundleContext; |
| this.camelContext = camelContext; |
| this.injector = injector; |
| this.factoryFinder = factoryFinder; |
| this.tracker = new ServiceTracker<>(bundleContext, TypeConverterLoader.class.getName(), this); |
| } |
| |
| public Object addingService(ServiceReference<TypeConverterLoader> serviceReference) { |
| LOG.trace("AddingService: {}, Bundle: {}", serviceReference, serviceReference.getBundle()); |
| TypeConverterLoader loader = bundleContext.getService(serviceReference); |
| try { |
| LOG.debug("loading type converter from bundle: {}", serviceReference.getBundle().getSymbolicName()); |
| if (delegate != null) { |
| loader.load(delegate); |
| } |
| } catch (Throwable t) { |
| throw new RuntimeCamelException("Error loading type converters from service: " + serviceReference + " due: " + t.getMessage(), t); |
| } |
| |
| return loader; |
| } |
| |
| public void modifiedService(ServiceReference<TypeConverterLoader> serviceReference, Object o) { |
| } |
| |
| public void removedService(ServiceReference<TypeConverterLoader> serviceReference, Object o) { |
| LOG.trace("RemovedService: {}, Bundle: {}", serviceReference, serviceReference.getBundle()); |
| try { |
| ServiceHelper.stopService(this.delegate); |
| } catch (Exception e) { |
| // ignore |
| LOG.debug("Error stopping service due: " + e.getMessage() + ". This exception will be ignored.", e); |
| } |
| // It can force camel to reload the type converter again |
| this.delegate = null; |
| |
| // TODO: reloading all type converters when one service is removed is suboptimal... |
| } |
| |
| @Override |
| protected void doStart() throws Exception { |
| this.tracker.open(); |
| } |
| |
| @Override |
| protected void doStop() throws Exception { |
| this.tracker.close(); |
| ServiceHelper.stopService(this.delegate); |
| this.delegate = null; |
| } |
| |
| public boolean allowNull() { |
| return getDelegate().allowNull(); |
| } |
| |
| public <T> T convertTo(Class<T> type, Object value) { |
| return getDelegate().convertTo(type, value); |
| } |
| |
| public <T> T convertTo(Class<T> type, Exchange exchange, Object value) { |
| return getDelegate().convertTo(type, exchange, value); |
| } |
| |
| public <T> T mandatoryConvertTo(Class<T> type, Object value) throws NoTypeConversionAvailableException { |
| return getDelegate().mandatoryConvertTo(type, value); |
| } |
| |
| public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) throws NoTypeConversionAvailableException { |
| return getDelegate().mandatoryConvertTo(type, exchange, value); |
| } |
| |
| public <T> T tryConvertTo(Class<T> type, Exchange exchange, Object value) { |
| return getDelegate().tryConvertTo(type, exchange, value); |
| } |
| |
| public <T> T tryConvertTo(Class<T> type, Object value) { |
| return getDelegate().tryConvertTo(type, value); |
| } |
| |
| public void addTypeConverter(Class<?> toType, Class<?> fromType, TypeConverter typeConverter) { |
| getDelegate().addTypeConverter(toType, fromType, typeConverter); |
| } |
| |
| public void addTypeConverters(TypeConverters typeConverters) { |
| getDelegate().addTypeConverters(typeConverters); |
| } |
| |
| public boolean removeTypeConverter(Class<?> toType, Class<?> fromType) { |
| return getDelegate().removeTypeConverter(toType, fromType); |
| } |
| |
| public void addFallbackTypeConverter(TypeConverter typeConverter, boolean canPromote) { |
| getDelegate().addFallbackTypeConverter(typeConverter, canPromote); |
| } |
| |
| public TypeConverter lookup(Class<?> toType, Class<?> fromType) { |
| return getDelegate().lookup(toType, fromType); |
| } |
| |
| public List<Class<?>[]> listAllTypeConvertersFromTo() { |
| return getDelegate().listAllTypeConvertersFromTo(); |
| } |
| |
| public void setInjector(Injector injector) { |
| getDelegate().setInjector(injector); |
| } |
| |
| public Injector getInjector() { |
| return getDelegate().getInjector(); |
| } |
| |
| public Statistics getStatistics() { |
| return getDelegate().getStatistics(); |
| } |
| |
| public int size() { |
| return getDelegate().size(); |
| } |
| |
| public LoggingLevel getTypeConverterExistsLoggingLevel() { |
| return getDelegate().getTypeConverterExistsLoggingLevel(); |
| } |
| |
| public void setTypeConverterExistsLoggingLevel(LoggingLevel loggingLevel) { |
| getDelegate().setTypeConverterExistsLoggingLevel(loggingLevel); |
| } |
| |
| public TypeConverterExists getTypeConverterExists() { |
| return getDelegate().getTypeConverterExists(); |
| } |
| |
| public void setTypeConverterExists(TypeConverterExists typeConverterExists) { |
| getDelegate().setTypeConverterExists(typeConverterExists); |
| } |
| |
| public synchronized DefaultTypeConverter getDelegate() { |
| if (delegate == null) { |
| delegate = createRegistry(); |
| } |
| return delegate; |
| } |
| |
| protected DefaultTypeConverter createRegistry() { |
| // base the osgi type converter on the default type converter |
| DefaultTypeConverter answer = new DefaultTypeConverter(new DefaultPackageScanClassResolver() { |
| @Override |
| public Set<ClassLoader> getClassLoaders() { |
| // we don't need any classloaders as we use OSGi service tracker instead |
| return Collections.emptySet(); |
| } |
| }, injector, factoryFinder, false); |
| |
| // inject CamelContext |
| answer.setCamelContext(camelContext); |
| |
| try { |
| // only load the core type converters, as OSGi activator will keep track on bundles |
| // being installed/uninstalled and load type converters as part of that process |
| answer.loadCoreTypeConverters(); |
| } catch (Exception e) { |
| throw new RuntimeCamelException("Error loading CoreTypeConverter due: " + e.getMessage(), e); |
| } |
| |
| // Load the type converters the tracker has been tracking |
| // Here we need to use the ServiceReference to check the ranking |
| ServiceReference<TypeConverterLoader>[] serviceReferences = this.tracker.getServiceReferences(); |
| if (serviceReferences != null) { |
| ArrayList<ServiceReference<TypeConverterLoader>> servicesList = |
| new ArrayList<>(Arrays.asList(serviceReferences)); |
| // Just make sure we install the high ranking fallback converter at last |
| Collections.sort(servicesList); |
| for (ServiceReference<TypeConverterLoader> sr : servicesList) { |
| try { |
| LOG.debug("loading type converter from bundle: {}", sr.getBundle().getSymbolicName()); |
| ((TypeConverterLoader)this.tracker.getService(sr)).load(answer); |
| } catch (Throwable t) { |
| throw new RuntimeCamelException("Error loading type converters from service: " + sr + " due: " + t.getMessage(), t); |
| } |
| } |
| } |
| |
| LOG.trace("Created TypeConverter: {}", answer); |
| return answer; |
| } |
| } |