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 */ 017 package org.apache.camel.blueprint; 018 019 import org.apache.camel.TypeConverter; 020 import org.apache.camel.core.osgi.OsgiCamelContextHelper; 021 import org.apache.camel.core.osgi.OsgiFactoryFinderResolver; 022 import org.apache.camel.core.osgi.OsgiTypeConverter; 023 import org.apache.camel.core.osgi.utils.BundleContextUtils; 024 import org.apache.camel.core.osgi.utils.BundleDelegatingClassLoader; 025 import org.apache.camel.impl.DefaultCamelContext; 026 import org.apache.camel.spi.FactoryFinder; 027 import org.apache.camel.spi.Registry; 028 import org.osgi.framework.BundleContext; 029 import org.osgi.framework.ServiceEvent; 030 import org.osgi.framework.ServiceListener; 031 import org.osgi.framework.ServiceRegistration; 032 import org.osgi.service.blueprint.container.BlueprintContainer; 033 import org.osgi.service.blueprint.container.BlueprintEvent; 034 import org.osgi.service.blueprint.container.BlueprintListener; 035 import org.slf4j.Logger; 036 import org.slf4j.LoggerFactory; 037 038 public class BlueprintCamelContext extends DefaultCamelContext implements ServiceListener, BlueprintListener { 039 040 private static final transient Logger LOG = LoggerFactory.getLogger(BlueprintCamelContext.class); 041 042 private BundleContext bundleContext; 043 private BlueprintContainer blueprintContainer; 044 private ServiceRegistration<?> registration; 045 046 public BlueprintCamelContext() { 047 } 048 049 public BlueprintCamelContext(BundleContext bundleContext, BlueprintContainer blueprintContainer) { 050 this.bundleContext = bundleContext; 051 this.blueprintContainer = blueprintContainer; 052 053 // inject common osgi 054 OsgiCamelContextHelper.osgiUpdate(this, bundleContext); 055 056 // and these are blueprint specific 057 setComponentResolver(new BlueprintComponentResolver(bundleContext)); 058 setLanguageResolver(new BlueprintLanguageResolver(bundleContext)); 059 setDataFormatResolver(new BlueprintDataFormatResolver(bundleContext)); 060 setApplicationContextClassLoader(new BundleDelegatingClassLoader(bundleContext.getBundle())); 061 } 062 063 public BundleContext getBundleContext() { 064 return bundleContext; 065 } 066 067 public void setBundleContext(BundleContext bundleContext) { 068 this.bundleContext = bundleContext; 069 } 070 071 public BlueprintContainer getBlueprintContainer() { 072 return blueprintContainer; 073 } 074 075 public void setBlueprintContainer(BlueprintContainer blueprintContainer) { 076 this.blueprintContainer = blueprintContainer; 077 } 078 079 public void init() throws Exception { 080 LOG.trace("init {}", this); 081 082 // add service listener so we can be notified when blueprint container is done 083 // and we would be ready to start CamelContext 084 bundleContext.addServiceListener(this); 085 // add blueprint listener as service, as we need this for the blueprint container 086 // to support change events when it changes states 087 registration = bundleContext.registerService(BlueprintListener.class, this, null); 088 } 089 090 public void destroy() throws Exception { 091 LOG.trace("destroy {}", this); 092 093 // remove listener and stop this CamelContext 094 try { 095 bundleContext.removeServiceListener(this); 096 } catch (Exception e) { 097 LOG.warn("Error removing ServiceListener " + this + ". This exception is ignored.", e); 098 } 099 if (registration != null) { 100 try { 101 registration.unregister(); 102 } catch (Exception e) { 103 LOG.warn("Error unregistering service registration " + registration + ". This exception is ignored.", e); 104 } 105 registration = null; 106 } 107 108 // must stop Camel 109 stop(); 110 } 111 112 113 @Override 114 public void blueprintEvent(BlueprintEvent event) { 115 // noop as we just needed to enlist the BlueprintListener to have events triggered to serviceChanged method 116 } 117 118 @Override 119 public void serviceChanged(ServiceEvent event) { 120 if (LOG.isDebugEnabled()) { 121 LOG.debug("Service {} changed to {}", event, event.getType()); 122 } 123 // look for blueprint container to be registered, and then we can start the CamelContext 124 if (event.getType() == ServiceEvent.REGISTERED 125 && event.getServiceReference().isAssignableTo(bundleContext.getBundle(), "org.osgi.service.blueprint.container.BlueprintContainer") 126 && bundleContext.getBundle().equals(event.getServiceReference().getBundle())) { 127 try { 128 maybeStart(); 129 } catch (Exception e) { 130 LOG.error("Error occurred during starting Camel: " + this + " due " + e.getMessage(), e); 131 } 132 } 133 } 134 135 @Override 136 protected TypeConverter createTypeConverter() { 137 // CAMEL-3614: make sure we use a bundle context which imports org.apache.camel.impl.converter package 138 BundleContext ctx = BundleContextUtils.getBundleContext(getClass()); 139 if (ctx == null) { 140 ctx = bundleContext; 141 } 142 FactoryFinder finder = new OsgiFactoryFinderResolver(bundleContext).resolveDefaultFactoryFinder(getClassResolver()); 143 return new OsgiTypeConverter(ctx, getInjector(), finder); 144 } 145 146 @Override 147 protected Registry createRegistry() { 148 Registry reg = new BlueprintContainerRegistry(getBlueprintContainer()); 149 return OsgiCamelContextHelper.wrapRegistry(this, reg, bundleContext); 150 } 151 152 private void maybeStart() throws Exception { 153 LOG.trace("maybeStart: {}", this); 154 155 if (!isStarted() && !isStarting()) { 156 final ClassLoader original = Thread.currentThread().getContextClassLoader(); 157 try { 158 // let's set a more suitable TCCL while starting the context 159 Thread.currentThread().setContextClassLoader(getApplicationContextClassLoader()); 160 LOG.debug("Starting {}", this); 161 start(); 162 } finally { 163 Thread.currentThread().setContextClassLoader(original); 164 } 165 } else { 166 // ignore as Camel is already started 167 LOG.trace("Ignoring maybeStart() as {} is already started", this); 168 } 169 } 170 }