001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.xbean.osgi.bundle.util;
021    
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.net.URL;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.Dictionary;
029    import java.util.Enumeration;
030    import java.util.HashSet;
031    import java.util.Iterator;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.Set;
035    import java.util.concurrent.CopyOnWriteArrayList;
036    
037    import org.osgi.framework.Bundle;
038    import org.osgi.framework.BundleContext;
039    import org.osgi.framework.BundleException;
040    import org.osgi.framework.ServiceReference;
041    import org.osgi.framework.Version;
042    import org.osgi.service.packageadmin.ExportedPackage;
043    import org.osgi.service.packageadmin.PackageAdmin;
044    
045    /**
046     * Bundle that delegates ClassLoader operations to a collection of {@link Bundle} objects.
047     *
048     * @version $Rev: 1160127 $ $Date: 2011-08-22 15:04:01 +0800 (Mon, 22 Aug 2011) $
049     */
050    public class DelegatingBundle implements Bundle {
051    
052        private CopyOnWriteArrayList<Bundle> bundles;
053        private Bundle bundle;
054        private BundleContext bundleContext;
055    
056        public DelegatingBundle(Collection<Bundle> bundles) {
057            if (bundles.isEmpty()) {
058                throw new IllegalArgumentException("At least one bundle is required");
059            }
060            this.bundles = new CopyOnWriteArrayList<Bundle>(bundles);
061            // assume first Bundle is the main bundle
062            this.bundle = bundles.iterator().next();
063            this.bundleContext = new DelegatingBundleContext(this, bundle.getBundleContext());
064        }
065    
066        public DelegatingBundle(Bundle bundle) {
067            this(Collections.singletonList(bundle));
068        }
069    
070        public Bundle getMainBundle() {
071            return bundle;
072        }
073    
074        public Class<?> loadClass(String name) throws ClassNotFoundException {
075            try {
076                return bundle.loadClass(name);
077            } catch (ClassNotFoundException ex) {
078                int index = name.lastIndexOf('.');
079                if (index > 0 && bundles.size() > 1) {
080                    // see if there are any bundles exporting the package
081                    String packageName = name.substring(0, index);
082                    Set<Bundle> packageBundles = getPackageBundles(packageName);
083                    if (packageBundles == null) {
084                        // package is NOT exported
085    
086                        Iterator<Bundle> iterator = bundles.iterator();
087                        // skip first bundle
088                        iterator.next();
089                        // attempt to load the class from the remaining bundles
090                        while (iterator.hasNext()) {
091                            Bundle delegate = iterator.next();
092                            try {
093                                return delegate.loadClass(name);
094                            } catch (ClassNotFoundException e) {
095                                // ignore
096                            }
097                        }
098    
099                        throw ex;
100                    } else {
101                        // package is exported
102    
103                        // see if any of our bundles is wired to the exporter
104                        Bundle delegate = findFirstBundle(packageBundles);
105                        if (delegate == null || delegate == bundle) {
106                            // nope. no static wires but might need to check for dynamic wires in the future.
107                            throw ex;
108                        } else {
109                            // yes. attempt to load the class from it
110                            return delegate.loadClass(name);
111                        }
112                    }
113                }  else {
114                    // no package name
115                    throw ex;
116                }
117            }
118        }
119    
120        private Set<Bundle> getPackageBundles(String packageName) {
121            BundleContext context = bundle.getBundleContext();
122            ServiceReference reference = context.getServiceReference(PackageAdmin.class.getName());
123            PackageAdmin packageAdmin = (PackageAdmin) context.getService(reference);
124            Set<Bundle> bundles = null;
125            try {
126                ExportedPackage[] exportedPackages = packageAdmin.getExportedPackages(packageName);
127                if (exportedPackages != null && exportedPackages.length > 0) {
128                    bundles = new HashSet<Bundle>();
129                    for (ExportedPackage exportedPackage : exportedPackages) {
130                        bundles.add(exportedPackage.getExportingBundle());
131                        Bundle[] importingBundles = exportedPackage.getImportingBundles();
132                        if (importingBundles != null) {
133                            for (Bundle importingBundle : importingBundles) {
134                                bundles.add(importingBundle);
135                            }
136                        }
137                    }
138                }
139                return bundles;
140            } finally {
141                context.ungetService(reference);
142            }
143        }
144    
145        private Bundle findFirstBundle(Set<Bundle> packageBundles) {
146            Collection<Bundle> c1 = bundles;
147            Collection<Bundle> c2 = packageBundles;
148    
149            if (bundles instanceof Set<?> && bundles.size() > packageBundles.size()) {
150                c1 = packageBundles;
151                c2 = bundles;
152            }
153    
154            for (Bundle bundle : c1) {
155                if (c2.contains(bundle)) {
156                    return bundle;
157                }
158            }
159    
160            return null;
161        }
162    
163        public void addBundle(Bundle b) {
164            bundles.add(b);
165        }
166    
167        public void removeBundle(Bundle b) {
168            bundles.remove(b);
169        }
170    
171        public URL getResource(String name) {
172            URL resource = null;
173            for (Bundle bundle : bundles) {
174                resource = bundle.getResource(name);
175                if (resource != null) {
176                    return resource;
177                }
178            }
179            return null;
180        }
181    
182        public Enumeration<URL> getResources(String name) throws IOException {
183            ArrayList<URL> allResources = new ArrayList<URL>();
184            for (Bundle bundle : bundles) {
185                Enumeration<URL> e = bundle.getResources(name);
186                addToList(allResources, e);
187            }
188            return Collections.enumeration(allResources);
189        }
190    
191        private static void addToList(List<URL> list, Enumeration<URL> enumeration) {
192            if (enumeration != null) {
193                while (enumeration.hasMoreElements()) {
194                    list.add(enumeration.nextElement());
195                }
196            }
197        }
198    
199        public BundleContext getBundleContext() {
200            return bundleContext;
201        }
202    
203        public Enumeration findEntries(String arg0, String arg1, boolean arg2) {
204            return bundle.findEntries(arg0, arg1, arg2);
205        }
206    
207        public long getBundleId() {
208            return bundle.getBundleId();
209        }
210    
211        public URL getEntry(String arg0) {
212            return bundle.getEntry(arg0);
213        }
214    
215        public Enumeration getEntryPaths(String arg0) {
216            return bundle.getEntryPaths(arg0);
217        }
218    
219        public Dictionary getHeaders() {
220            return bundle.getHeaders();
221        }
222    
223        public Dictionary getHeaders(String arg0) {
224            return bundle.getHeaders(arg0);
225        }
226    
227        public long getLastModified() {
228            return bundle.getLastModified();
229        }
230    
231        public String getLocation() {
232            return bundle.getLocation();
233        }
234    
235        public ServiceReference[] getRegisteredServices() {
236            return bundle.getRegisteredServices();
237        }
238    
239        public ServiceReference[] getServicesInUse() {
240            return bundle.getServicesInUse();
241        }
242    
243        public Map getSignerCertificates(int arg0) {
244            return bundle.getSignerCertificates(arg0);
245        }
246    
247        public int getState() {
248            return bundle.getState();
249        }
250    
251        public String getSymbolicName() {
252            return bundle.getSymbolicName();
253        }
254    
255        public Version getVersion() {
256            return bundle.getVersion();
257        }
258    
259        public boolean hasPermission(Object arg0) {
260            return bundle.hasPermission(arg0);
261        }
262    
263        public void start() throws BundleException {
264            bundle.start();
265        }
266    
267        public void start(int arg0) throws BundleException {
268            bundle.start(arg0);
269        }
270    
271        public void stop() throws BundleException {
272            bundle.stop();
273        }
274    
275        public void stop(int arg0) throws BundleException {
276            bundle.stop(arg0);
277        }
278    
279        public void uninstall() throws BundleException {
280            bundle.uninstall();
281        }
282    
283        public void update() throws BundleException {
284            bundle.update();
285        }
286    
287        public void update(InputStream arg0) throws BundleException {
288            bundle.update(arg0);
289        }
290    
291        public String toString() {
292            return "[DelegatingBundle: " + bundles + "]";
293        }
294    
295    }