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