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