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.TypeConverter; 025import org.apache.camel.blueprint.handler.CamelNamespaceHandler; 026import org.apache.camel.core.osgi.OsgiCamelContextHelper; 027import org.apache.camel.core.osgi.OsgiCamelContextPublisher; 028import org.apache.camel.core.osgi.OsgiFactoryFinderResolver; 029import org.apache.camel.core.osgi.OsgiTypeConverter; 030import org.apache.camel.core.osgi.utils.BundleContextUtils; 031import org.apache.camel.core.osgi.utils.BundleDelegatingClassLoader; 032import org.apache.camel.impl.DefaultCamelContext; 033import org.apache.camel.spi.EventNotifier; 034import org.apache.camel.spi.FactoryFinder; 035import org.apache.camel.spi.ModelJAXBContextFactory; 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 } 076 077 @Override 078 protected ModelJAXBContextFactory createModelJAXBContextFactory() { 079 // must use classloader of the namespace handler 080 return new BlueprintModelJAXBContextFactory(CamelNamespaceHandler.class.getClassLoader()); 081 } 082 083 public BundleContext getBundleContext() { 084 return bundleContext; 085 } 086 087 public void setBundleContext(BundleContext bundleContext) { 088 this.bundleContext = bundleContext; 089 } 090 091 public BlueprintContainer getBlueprintContainer() { 092 return blueprintContainer; 093 } 094 095 public void setBlueprintContainer(BlueprintContainer blueprintContainer) { 096 this.blueprintContainer = blueprintContainer; 097 } 098 099 public void init() throws Exception { 100 LOG.trace("init {}", this); 101 102 // add service listener so we can be notified when blueprint container is done 103 // and we would be ready to start CamelContext 104 bundleContext.addServiceListener(this); 105 // add blueprint listener as service, as we need this for the blueprint container 106 // to support change events when it changes states 107 registration = bundleContext.registerService(BlueprintListener.class, this, null); 108 } 109 110 public void destroy() throws Exception { 111 LOG.trace("destroy {}", this); 112 113 // remove listener and stop this CamelContext 114 try { 115 bundleContext.removeServiceListener(this); 116 } catch (Exception e) { 117 LOG.warn("Error removing ServiceListener " + this + ". This exception is ignored.", e); 118 } 119 if (registration != null) { 120 try { 121 registration.unregister(); 122 } catch (Exception e) { 123 LOG.warn("Error unregistering service registration " + registration + ". This exception is ignored.", e); 124 } 125 registration = null; 126 } 127 128 // must stop Camel 129 stop(); 130 } 131 132 @Override 133 public Map<String, Properties> findComponents() throws LoadPropertiesException, IOException { 134 return BundleContextUtils.findComponents(bundleContext, this); 135 } 136 137 @Override 138 public String getComponentDocumentation(String componentName) throws IOException { 139 return BundleContextUtils.getComponentDocumentation(bundleContext, this, componentName); 140 } 141 142 @Override 143 public void blueprintEvent(BlueprintEvent event) { 144 if (LOG.isDebugEnabled()) { 145 String eventTypeString; 146 147 switch (event.getType()) { 148 case BlueprintEvent.CREATING: 149 eventTypeString = "CREATING"; 150 break; 151 case BlueprintEvent.CREATED: 152 eventTypeString = "CREATED"; 153 break; 154 case BlueprintEvent.DESTROYING: 155 eventTypeString = "DESTROYING"; 156 break; 157 case BlueprintEvent.DESTROYED: 158 eventTypeString = "DESTROYED"; 159 break; 160 case BlueprintEvent.GRACE_PERIOD: 161 eventTypeString = "GRACE_PERIOD"; 162 break; 163 case BlueprintEvent.WAITING: 164 eventTypeString = "WAITING"; 165 break; 166 case BlueprintEvent.FAILURE: 167 eventTypeString = "FAILURE"; 168 break; 169 default: 170 eventTypeString = "UNKNOWN"; 171 break; 172 } 173 174 LOG.debug("Received BlueprintEvent[ replay={} type={} bundle={}] %s", event.isReplay(), eventTypeString, event.getBundle().getSymbolicName(), event.toString()); 175 } 176 177 if (!event.isReplay() && this.getBundleContext().getBundle().getBundleId() == event.getBundle().getBundleId()) { 178 if (event.getType() == BlueprintEvent.CREATED) { 179 try { 180 LOG.info("Attempting to start Camel Context {}", this.getName()); 181 this.maybeStart(); 182 } catch (Exception startEx) { 183 LOG.error("Error occurred during starting Camel Context " + this.getName(), startEx); 184 } 185 } else if (event.getType() == BlueprintEvent.DESTROYING) { 186 try { 187 LOG.info("Stopping Camel Context {}", this.getName()); 188 this.stop(); 189 } catch (Exception stopEx) { 190 LOG.error("Error occurred during stopping Camel Context " + this.getName(), stopEx); 191 } 192 193 } 194 } 195 196 } 197 198 @Override 199 public void serviceChanged(ServiceEvent event) { 200 if (LOG.isDebugEnabled()) { 201 String eventTypeString; 202 203 switch (event.getType()) { 204 case ServiceEvent.REGISTERED: 205 eventTypeString = "REGISTERED"; 206 break; 207 case ServiceEvent.MODIFIED: 208 eventTypeString = "MODIFIED"; 209 break; 210 case ServiceEvent.UNREGISTERING: 211 eventTypeString = "UNREGISTERING"; 212 break; 213 case ServiceEvent.MODIFIED_ENDMATCH: 214 eventTypeString = "MODIFIED_ENDMATCH"; 215 break; 216 default: 217 eventTypeString = "UNKNOWN"; 218 break; 219 } 220 221 LOG.debug("Service {} changed to {}", event.toString(), eventTypeString); 222 } 223 } 224 225 @Override 226 protected TypeConverter createTypeConverter() { 227 // CAMEL-3614: make sure we use a bundle context which imports org.apache.camel.impl.converter package 228 BundleContext ctx = BundleContextUtils.getBundleContext(getClass()); 229 if (ctx == null) { 230 ctx = bundleContext; 231 } 232 FactoryFinder finder = new OsgiFactoryFinderResolver(bundleContext).resolveDefaultFactoryFinder(getClassResolver()); 233 return new OsgiTypeConverter(ctx, this, getInjector(), finder); 234 } 235 236 @Override 237 protected Registry createRegistry() { 238 Registry reg = new BlueprintContainerRegistry(getBlueprintContainer()); 239 return OsgiCamelContextHelper.wrapRegistry(this, reg, bundleContext); 240 } 241 242 @Override 243 public void start() throws Exception { 244 final ClassLoader original = Thread.currentThread().getContextClassLoader(); 245 try { 246 // let's set a more suitable TCCL while starting the context 247 Thread.currentThread().setContextClassLoader(getApplicationContextClassLoader()); 248 super.start(); 249 } catch (Exception e) { 250 routeDefinitionValid.set(false); 251 throw e; 252 } finally { 253 Thread.currentThread().setContextClassLoader(original); 254 } 255 } 256 257 private void maybeStart() throws Exception { 258 LOG.trace("maybeStart: {}", this); 259 260 if(!routeDefinitionValid.get()){ 261 LOG.trace("maybeStart: {} is skipping since CamelRoute definition is not correct.", this); 262 return; 263 } 264 265 // allow to register the BluerintCamelContext eager in the OSGi Service Registry, which ex is needed 266 // for unit testing with camel-test-blueprint 267 boolean eager = "true".equalsIgnoreCase(System.getProperty("registerBlueprintCamelContextEager")); 268 if (eager) { 269 for (EventNotifier notifier : getManagementStrategy().getEventNotifiers()) { 270 if (notifier instanceof OsgiCamelContextPublisher) { 271 OsgiCamelContextPublisher publisher = (OsgiCamelContextPublisher) notifier; 272 publisher.registerCamelContext(this); 273 break; 274 } 275 } 276 } 277 278 // for example from unit testing we want to start Camel later and not 279 // when blueprint loading the bundle 280 boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext")); 281 if (skip) { 282 LOG.trace("maybeStart: {} is skipping as System property skipStartingCamelContext is set", this); 283 return; 284 } 285 286 if (!isStarted() && !isStarting()) { 287 LOG.debug("Starting {}", this); 288 start(); 289 } else { 290 // ignore as Camel is already started 291 LOG.trace("Ignoring maybeStart() as {} is already started", this); 292 } 293 } 294 295}