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