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 }