summaryrefslogtreecommitdiffstats
path: root/sca-java-1.x/tags/1.6-TUSCANY-3909/osgi-runtime/src/main/java/org/apache/tuscany/sca/osgi/runtime/OSGiBundleActivator.java
blob: b87f70deef24c595b94a49a6aee228aee00eb6c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.tuscany.sca.osgi.runtime;

import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;

import org.apache.tuscany.sca.extensibility.ServiceDiscovery;
import org.apache.tuscany.sca.extensibility.osgi.OSGiServiceDiscoverer;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;

/**
 * OSGi bundle activator, which is run when Tuscany is run inside an OSGi runtime.
 *
 * @version $Rev$ $Date$
 */
public class OSGiBundleActivator implements BundleActivator, BundleListener {

    private static final String TUSCANY_SCA_BUNDLE_PREFIX = "org.apache.tuscany.sca";
    private static final String TUSCANY_3RD_PARTY_BUNDLE_PREFIX = "org.apache.tuscany.sca.3rdparty";
    private OSGiRuntime runtime;
    /*
    private ConcurrentHashMap<Bundle, ClassLoader> serviceDiscoveryClassLoaders =
        new ConcurrentHashMap<Bundle, ClassLoader>();
    */    
    private BundleClassLoader threadContextClassLoader;
    private ClassLoader origTCCL;
    private Bundle thisBundle;

    public void start(BundleContext bundleContext) throws Exception {

        initializeTuscanyClassLoaders(bundleContext);

        runtime = OSGiRuntime.findRuntime();
        runtime.setBundleContext(bundleContext);
        runtime.setContextClassLoader(threadContextClassLoader);
        runtime.initialize();

    }

    public void stop(BundleContext bundleContext) throws Exception {

        // runtime.shutdown();

        if (Thread.currentThread().getContextClassLoader() == threadContextClassLoader)
            Thread.currentThread().setContextClassLoader(origTCCL);
    }

    /**
     * Create a bundle ClassLoader which has visibility of all Tuscany related
     * bundles. Use this ClassLoader as TCCL, and the Tuscany service discovery
     * ClassLoader.
     * 
     * @param bundleContext
     */
    private void initializeTuscanyClassLoaders(BundleContext bundleContext) {

        OSGiServiceDiscoverer discoverer = new OSGiServiceDiscoverer(bundleContext);
        ServiceDiscovery.getInstance().setServiceDiscoverer(discoverer);
        thisBundle = bundleContext.getBundle();

        origTCCL = Thread.currentThread().getContextClassLoader();

        threadContextClassLoader = new BundleClassLoader(thisBundle, origTCCL);

        Thread.currentThread().setContextClassLoader(threadContextClassLoader);

        ClassLoader cl = new BundleClassLoader(thisBundle, null);
        // ServiceDiscovery.getInstance().registerClassLoader(cl);
        // serviceDiscoveryClassLoaders.put(thisBundle, cl);

        Bundle[] bundles = bundleContext.getBundles();
        for (Bundle bundle : bundles) {
            updateBundleClassLoader(bundle);
        }
        bundleContext.addBundleListener(this);

    }

    /**
     * Add an installed bundle to the list of bundles in the service discovery classpath
     * if the installed bundle is a Tuscany bundle using the same Core-SPI bundle.
     * Since the bundle containing this activator is in Tuscany Runtime, this bundle
     * will provide access to Runtime and its dependencies (SPI, SCA-API). Third party
     * bundles don't need to be in the service discovery classpath, since they will be automatically
     * imported by bundles which require them. That leaves Tuscany extension bundles,
     * which are added to the service discovery classpath here.
     * 
     * 3rd party bundle should be explicitly added to TCCL since classes from these
     * bundles use TCCL to load other classes from the bundle.
     * 
     * Load one class from the bundle to check if the new bundle matches this runtime bundle.
     * 
     * @param bundle
     */
    private void updateBundleClassLoader(Bundle bundle) {

        if (bundle.getSymbolicName().startsWith(TUSCANY_SCA_BUNDLE_PREFIX)) {

            // This may be the third party bundle.
            if (bundle.getSymbolicName().startsWith(TUSCANY_3RD_PARTY_BUNDLE_PREFIX)) {

                threadContextClassLoader.addBundle(bundle);
            } else {

                String thisBundleVersion = (String)thisBundle.getHeaders().get("Bundle-Version");
                String bundleVersion = (String)bundle.getHeaders().get("Bundle-Version");

                if (thisBundleVersion == null || bundleVersion == null || thisBundleVersion.equals(bundleVersion)) {

                    if (!threadContextClassLoader.bundles.contains(bundle)) {
                        ClassLoader cl = new BundleClassLoader(bundle, null);
                        // ServiceDiscovery.getInstance().registerClassLoader(cl);
                        // serviceDiscoveryClassLoaders.put(bundle, cl);
                        threadContextClassLoader.addBundle(bundle);
                    }
                }
            }
        } else {
            threadContextClassLoader.addBundle(bundle);
        }
    }

    /**
     * Handle bundle install/uninstall events 
     */
    public void bundleChanged(BundleEvent event) {

        Bundle bundle = event.getBundle();
        if (event.getType() == BundleEvent.UNINSTALLED) {
//            ClassLoader cl = serviceDiscoveryClassLoaders.get(bundle);
//            if (cl != null) {
//                ServiceDiscovery.getInstance().unregisterClassLoader(cl);
//            }
            threadContextClassLoader.removeBundle(bundle);
        } else if (event.getType() == BundleEvent.INSTALLED) {
            updateBundleClassLoader(bundle);
        }
    }

    /**
     * Bundle ClassLoader that searches a bundle classpath consisting of
     * a list of bundles. The parent ClassLoader is searched only if a class
     * cannot be loaded from the bundle classpath. Tuscany bundles are
     * dynamically added and removed from the bundle classpath when the bundles
     * are installed and uninstalled.
     * 
     * No ordering of bundles is maintained at the moment.
     *
     */
    private static class BundleClassLoader extends ClassLoader {

        private HashSet<Bundle> bundles;

        BundleClassLoader(Bundle bundle, ClassLoader parent) {
            super(parent);
            this.bundles = new HashSet<Bundle>();
            bundles.add(bundle);
        }

        private synchronized void addBundle(Bundle bundle) {
            bundles.add(bundle);
        }

        private synchronized void removeBundle(Bundle bundle) {
            if (bundles.contains(bundle))
                bundles.remove(bundle);
        }

        @Override
        protected Class<?> findClass(String className) throws ClassNotFoundException {
            Class<?> clazz = null;
            synchronized (this) {
                for (Bundle bundle : bundles) {
                    try {
                        clazz = bundle.loadClass(className);
                        break;
                    } catch (ClassNotFoundException e) {
                    } catch (NoClassDefFoundError e) {
                    }

                }
            }
            if (clazz != null) {
                return clazz;
            }
            return super.findClass(className);
        }

        @Override
        @SuppressWarnings("unchecked")
        public Enumeration<URL> getResources(String resName) throws IOException {
            HashSet<URL> urlSet = new HashSet<URL>();
            Enumeration<URL> urls = null;
            synchronized (this) {
                for (Bundle bundle : bundles) {
                    urls = bundle.getResources(resName);
                    if (urls != null) {
                        while (urls.hasMoreElements()) {
                            urlSet.add(urls.nextElement());
                        }
                    }
                }
            }
            if (urlSet.size() > 0)
                return Collections.enumeration(urlSet);
            return super.getResources(resName);
        }

        @Override
        public URL getResource(String resName) {
            URL url = null;
            synchronized (this) {
                for (Bundle bundle : bundles) {
                    url = bundle.getResource(resName);
                    if (url != null)
                        return url;
                }
            }
            return super.getResource(resName);
        }

        @Override
        public String toString() {
            return "Tuscany BundleClassLoader " + bundles.iterator().next();
        }

    }
}