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 java.io.IOException;
020 import java.util.Map;
021 import java.util.Properties;
022
023 import org.apache.camel.TypeConverter;
024 import org.apache.camel.core.osgi.OsgiCamelContextHelper;
025 import org.apache.camel.core.osgi.OsgiCamelContextPublisher;
026 import org.apache.camel.core.osgi.OsgiFactoryFinderResolver;
027 import org.apache.camel.core.osgi.OsgiTypeConverter;
028 import org.apache.camel.core.osgi.utils.BundleContextUtils;
029 import org.apache.camel.core.osgi.utils.BundleDelegatingClassLoader;
030 import org.apache.camel.impl.DefaultCamelContext;
031 import org.apache.camel.spi.EventNotifier;
032 import org.apache.camel.spi.FactoryFinder;
033 import org.apache.camel.spi.Registry;
034 import org.apache.camel.util.LoadPropertiesException;
035 import org.osgi.framework.BundleContext;
036 import org.osgi.framework.ServiceEvent;
037 import org.osgi.framework.ServiceListener;
038 import org.osgi.framework.ServiceRegistration;
039 import org.osgi.service.blueprint.container.BlueprintContainer;
040 import org.osgi.service.blueprint.container.BlueprintEvent;
041 import org.osgi.service.blueprint.container.BlueprintListener;
042 import org.slf4j.Logger;
043 import org.slf4j.LoggerFactory;
044
045 /**
046 * OSGi Blueprint based {@link org.apache.camel.CamelContext}.
047 */
048 public class BlueprintCamelContext extends DefaultCamelContext implements ServiceListener, BlueprintListener {
049
050 private static final Logger LOG = LoggerFactory.getLogger(BlueprintCamelContext.class);
051
052 private BundleContext bundleContext;
053 private BlueprintContainer blueprintContainer;
054 private ServiceRegistration<?> registration;
055
056 public BlueprintCamelContext() {
057 }
058
059 public BlueprintCamelContext(BundleContext bundleContext, BlueprintContainer blueprintContainer) {
060 this.bundleContext = bundleContext;
061 this.blueprintContainer = blueprintContainer;
062
063 // inject common osgi
064 OsgiCamelContextHelper.osgiUpdate(this, bundleContext);
065
066 // and these are blueprint specific
067 setComponentResolver(new BlueprintComponentResolver(bundleContext));
068 setLanguageResolver(new BlueprintLanguageResolver(bundleContext));
069 setDataFormatResolver(new BlueprintDataFormatResolver(bundleContext));
070 setApplicationContextClassLoader(new BundleDelegatingClassLoader(bundleContext.getBundle()));
071 }
072
073 public BundleContext getBundleContext() {
074 return bundleContext;
075 }
076
077 public void setBundleContext(BundleContext bundleContext) {
078 this.bundleContext = bundleContext;
079 }
080
081 public BlueprintContainer getBlueprintContainer() {
082 return blueprintContainer;
083 }
084
085 public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
086 this.blueprintContainer = blueprintContainer;
087 }
088
089 public void init() throws Exception {
090 LOG.trace("init {}", this);
091
092 // add service listener so we can be notified when blueprint container is done
093 // and we would be ready to start CamelContext
094 bundleContext.addServiceListener(this);
095 // add blueprint listener as service, as we need this for the blueprint container
096 // to support change events when it changes states
097 registration = bundleContext.registerService(BlueprintListener.class, this, null);
098 }
099
100 public void destroy() throws Exception {
101 LOG.trace("destroy {}", this);
102
103 // remove listener and stop this CamelContext
104 try {
105 bundleContext.removeServiceListener(this);
106 } catch (Exception e) {
107 LOG.warn("Error removing ServiceListener " + this + ". This exception is ignored.", e);
108 }
109 if (registration != null) {
110 try {
111 registration.unregister();
112 } catch (Exception e) {
113 LOG.warn("Error unregistering service registration " + registration + ". This exception is ignored.", e);
114 }
115 registration = null;
116 }
117
118 // must stop Camel
119 stop();
120 }
121
122 @Override
123 public Map<String, Properties> findComponents() throws LoadPropertiesException, IOException {
124 return BundleContextUtils.findComponents(bundleContext, this);
125 }
126
127 @Override
128 public String getComponentDocumentation(String componentName) throws IOException {
129 return BundleContextUtils.getComponentDocumentation(bundleContext, this, componentName);
130 }
131
132 @Override
133 public void blueprintEvent(BlueprintEvent event) {
134 // noop as we just needed to enlist the BlueprintListener to have events triggered to serviceChanged method
135 }
136
137 @Override
138 public void serviceChanged(ServiceEvent event) {
139 if (LOG.isDebugEnabled()) {
140 LOG.debug("Service {} changed to {}", event, event.getType());
141 }
142 // look for blueprint container to be registered, and then we can start the CamelContext
143 if (event.getType() == ServiceEvent.REGISTERED
144 && event.getServiceReference().isAssignableTo(bundleContext.getBundle(), "org.osgi.service.blueprint.container.BlueprintContainer")
145 && bundleContext.getBundle().equals(event.getServiceReference().getBundle())) {
146 try {
147 maybeStart();
148 } catch (Exception e) {
149 LOG.error("Error occurred during starting Camel: " + this + " due " + e.getMessage(), e);
150 }
151 }
152 }
153
154 @Override
155 protected TypeConverter createTypeConverter() {
156 // CAMEL-3614: make sure we use a bundle context which imports org.apache.camel.impl.converter package
157 BundleContext ctx = BundleContextUtils.getBundleContext(getClass());
158 if (ctx == null) {
159 ctx = bundleContext;
160 }
161 FactoryFinder finder = new OsgiFactoryFinderResolver(bundleContext).resolveDefaultFactoryFinder(getClassResolver());
162 return new OsgiTypeConverter(ctx, getInjector(), finder);
163 }
164
165 @Override
166 protected Registry createRegistry() {
167 Registry reg = new BlueprintContainerRegistry(getBlueprintContainer());
168 return OsgiCamelContextHelper.wrapRegistry(this, reg, bundleContext);
169 }
170
171 @Override
172 public void start() throws Exception {
173 final ClassLoader original = Thread.currentThread().getContextClassLoader();
174 try {
175 // let's set a more suitable TCCL while starting the context
176 Thread.currentThread().setContextClassLoader(getApplicationContextClassLoader());
177 super.start();
178 } finally {
179 Thread.currentThread().setContextClassLoader(original);
180 }
181 }
182
183 private void maybeStart() throws Exception {
184 LOG.trace("maybeStart: {}", this);
185
186 // allow to regsiter the BluerintCamelContext eager in the OSGi Service Registry, which ex is needed
187 // for unit testing with camel-test-blueprint
188 boolean eager = "true".equalsIgnoreCase(System.getProperty("registerBlueprintCamelContextEager"));
189 if (eager) {
190 for (EventNotifier notifer : getManagementStrategy().getEventNotifiers()) {
191 if (notifer instanceof OsgiCamelContextPublisher) {
192 OsgiCamelContextPublisher publisher = (OsgiCamelContextPublisher) notifer;
193 publisher.registerCamelContext(this);
194 break;
195 }
196 }
197 }
198
199 // for example from unit testing we want to start Camel later and not
200 // when blueprint loading the bundle
201 boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"));
202 if (skip) {
203 LOG.trace("maybeStart: {} is skipping as System property skipStartingCamelContext is set", this);
204 return;
205 }
206
207 if (!isStarted() && !isStarting()) {
208 LOG.debug("Starting {}", this);
209 start();
210 } else {
211 // ignore as Camel is already started
212 LOG.trace("Ignoring maybeStart() as {} is already started", this);
213 }
214 }
215
216 }