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 org.apache.camel.TypeConverter;
020import org.apache.camel.core.osgi.OsgiCamelContextHelper;
021import org.apache.camel.core.osgi.OsgiFactoryFinderResolver;
022import org.apache.camel.core.osgi.OsgiTypeConverter;
023import org.apache.camel.core.osgi.utils.BundleContextUtils;
024import org.apache.camel.core.osgi.utils.BundleDelegatingClassLoader;
025import org.apache.camel.impl.DefaultCamelContext;
026import org.apache.camel.spi.FactoryFinder;
027import org.apache.camel.spi.Registry;
028import org.osgi.framework.BundleContext;
029import org.osgi.framework.ServiceEvent;
030import org.osgi.framework.ServiceListener;
031import org.osgi.framework.ServiceRegistration;
032import org.osgi.service.blueprint.container.BlueprintContainer;
033import org.osgi.service.blueprint.container.BlueprintEvent;
034import org.osgi.service.blueprint.container.BlueprintListener;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038public 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}