001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.blueprint; 018 019import java.io.IOException; 020import java.util.Map; 021import java.util.Properties; 022 023import org.apache.camel.TypeConverter; 024import org.apache.camel.core.osgi.OsgiCamelContextHelper; 025import org.apache.camel.core.osgi.OsgiCamelContextPublisher; 026import org.apache.camel.core.osgi.OsgiFactoryFinderResolver; 027import org.apache.camel.core.osgi.OsgiTypeConverter; 028import org.apache.camel.core.osgi.utils.BundleContextUtils; 029import org.apache.camel.core.osgi.utils.BundleDelegatingClassLoader; 030import org.apache.camel.impl.DefaultCamelContext; 031import org.apache.camel.spi.EventNotifier; 032import org.apache.camel.spi.FactoryFinder; 033import org.apache.camel.spi.Registry; 034import org.apache.camel.util.LoadPropertiesException; 035import org.osgi.framework.BundleContext; 036import org.osgi.framework.ServiceEvent; 037import org.osgi.framework.ServiceListener; 038import org.osgi.framework.ServiceRegistration; 039import org.osgi.service.blueprint.container.BlueprintContainer; 040import org.osgi.service.blueprint.container.BlueprintEvent; 041import org.osgi.service.blueprint.container.BlueprintListener; 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045/** 046 * OSGi Blueprint based {@link org.apache.camel.CamelContext}. 047 */ 048public class BlueprintCamelContext extends DefaultCamelContext implements ServiceListener, BlueprintListener { 049 050 private static final Logger LOG = LoggerFactory.getLogger(BlueprintCamelContext.class); 051 052 private BundleContext bundleContext; 053 private BlueprintContainer blueprintContainer; 054 private ServiceRegistration<?> registration; 055 056 public BlueprintCamelContext() { 057 } 058 059 public BlueprintCamelContext(BundleContext bundleContext, BlueprintContainer blueprintContainer) { 060 this.bundleContext = bundleContext; 061 this.blueprintContainer = blueprintContainer; 062 063 // inject common osgi 064 OsgiCamelContextHelper.osgiUpdate(this, bundleContext); 065 066 // and these are blueprint specific 067 setComponentResolver(new BlueprintComponentResolver(bundleContext)); 068 setLanguageResolver(new BlueprintLanguageResolver(bundleContext)); 069 setDataFormatResolver(new BlueprintDataFormatResolver(bundleContext)); 070 setApplicationContextClassLoader(new BundleDelegatingClassLoader(bundleContext.getBundle())); 071 } 072 073 public BundleContext getBundleContext() { 074 return bundleContext; 075 } 076 077 public void setBundleContext(BundleContext bundleContext) { 078 this.bundleContext = bundleContext; 079 } 080 081 public BlueprintContainer getBlueprintContainer() { 082 return blueprintContainer; 083 } 084 085 public void setBlueprintContainer(BlueprintContainer blueprintContainer) { 086 this.blueprintContainer = blueprintContainer; 087 } 088 089 public void init() throws Exception { 090 LOG.trace("init {}", this); 091 092 // add service listener so we can be notified when blueprint container is done 093 // and we would be ready to start CamelContext 094 bundleContext.addServiceListener(this); 095 // add blueprint listener as service, as we need this for the blueprint container 096 // to support change events when it changes states 097 registration = bundleContext.registerService(BlueprintListener.class, this, null); 098 } 099 100 public void destroy() throws Exception { 101 LOG.trace("destroy {}", this); 102 103 // remove listener and stop this CamelContext 104 try { 105 bundleContext.removeServiceListener(this); 106 } catch (Exception e) { 107 LOG.warn("Error removing ServiceListener " + this + ". This exception is ignored.", e); 108 } 109 if (registration != null) { 110 try { 111 registration.unregister(); 112 } catch (Exception e) { 113 LOG.warn("Error unregistering service registration " + registration + ". This exception is ignored.", e); 114 } 115 registration = null; 116 } 117 118 // must stop Camel 119 stop(); 120 } 121 122 @Override 123 public Map<String, Properties> findComponents() throws LoadPropertiesException, IOException { 124 return BundleContextUtils.findComponents(bundleContext, this); 125 } 126 127 @Override 128 public String getComponentDocumentation(String componentName) throws IOException { 129 return BundleContextUtils.getComponentDocumentation(bundleContext, this, componentName); 130 } 131 132 @Override 133 public void blueprintEvent(BlueprintEvent event) { 134 // noop as we just needed to enlist the BlueprintListener to have events triggered to serviceChanged method 135 } 136 137 @Override 138 public void serviceChanged(ServiceEvent event) { 139 if (LOG.isDebugEnabled()) { 140 LOG.debug("Service {} changed to {}", event, event.getType()); 141 } 142 // look for blueprint container to be registered, and then we can start the CamelContext 143 if (event.getType() == ServiceEvent.REGISTERED 144 && event.getServiceReference().isAssignableTo(bundleContext.getBundle(), "org.osgi.service.blueprint.container.BlueprintContainer") 145 && bundleContext.getBundle().equals(event.getServiceReference().getBundle())) { 146 try { 147 maybeStart(); 148 } catch (Exception e) { 149 LOG.error("Error occurred during starting Camel: " + this + " due " + e.getMessage(), e); 150 } 151 } 152 } 153 154 @Override 155 protected TypeConverter createTypeConverter() { 156 // CAMEL-3614: make sure we use a bundle context which imports org.apache.camel.impl.converter package 157 BundleContext ctx = BundleContextUtils.getBundleContext(getClass()); 158 if (ctx == null) { 159 ctx = bundleContext; 160 } 161 FactoryFinder finder = new OsgiFactoryFinderResolver(bundleContext).resolveDefaultFactoryFinder(getClassResolver()); 162 return new OsgiTypeConverter(ctx, getInjector(), finder); 163 } 164 165 @Override 166 protected Registry createRegistry() { 167 Registry reg = new BlueprintContainerRegistry(getBlueprintContainer()); 168 return OsgiCamelContextHelper.wrapRegistry(this, reg, bundleContext); 169 } 170 171 @Override 172 public void start() throws Exception { 173 final ClassLoader original = Thread.currentThread().getContextClassLoader(); 174 try { 175 // let's set a more suitable TCCL while starting the context 176 Thread.currentThread().setContextClassLoader(getApplicationContextClassLoader()); 177 super.start(); 178 } finally { 179 Thread.currentThread().setContextClassLoader(original); 180 } 181 } 182 183 private void maybeStart() throws Exception { 184 LOG.trace("maybeStart: {}", this); 185 186 // allow to register the BluerintCamelContext eager in the OSGi Service Registry, which ex is needed 187 // for unit testing with camel-test-blueprint 188 boolean eager = "true".equalsIgnoreCase(System.getProperty("registerBlueprintCamelContextEager")); 189 if (eager) { 190 for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) { 191 if (notifier instanceof OsgiCamelContextPublisher) { 192 OsgiCamelContextPublisher publisher = (OsgiCamelContextPublisher) notifier; 193 publisher.registerCamelContext(this); 194 break; 195 } 196 } 197 } 198 199 // for example from unit testing we want to start Camel later and not 200 // when blueprint loading the bundle 201 boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext")); 202 if (skip) { 203 LOG.trace("maybeStart: {} is skipping as System property skipStartingCamelContext is set", this); 204 return; 205 } 206 207 if (!isStarted() && !isStarting()) { 208 LOG.debug("Starting {}", this); 209 start(); 210 } else { 211 // ignore as Camel is already started 212 LOG.trace("Ignoring maybeStart() as {} is already started", this); 213 } 214 } 215 216}