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