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