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.net.URL;
023    import java.util.Collections;
024    import java.util.Dictionary;
025    import java.util.Enumeration;
026    import java.util.LinkedHashSet;
027    import java.util.List;
028    
029    import org.osgi.framework.Bundle;
030    import org.osgi.framework.BundleReference;
031    import org.osgi.framework.Constants;
032    import org.osgi.framework.ServiceReference;
033    import org.osgi.service.packageadmin.ExportedPackage;
034    import org.osgi.service.packageadmin.PackageAdmin;
035    
036    /**
037     * @version $Rev: 1133254 $ $Date: 2011-06-08 13:59:12 +0800 (Wed, 08 Jun 2011) $
038     */
039    public class BundleUtils {
040    
041        /**
042         *  Based on the constant field values, if it is bigger than the RESOLVED status value, the bundle has been resolved by the framework
043         * @param bundle
044         * @return true if the bundle is resolved, or false if not.
045         */
046        public static boolean isResolved(Bundle bundle) {
047            return bundle.getState() >= Bundle.RESOLVED;
048        }
049    
050        /**
051         * resolve method will try to load the Object.class, the behavior triggers a resolved request to the OSGI framework.
052         * @param bundle
053         */
054        public static void resolve(Bundle bundle) {
055            if (isFragment(bundle)) {
056                return;
057            }
058            try {
059                bundle.loadClass(Object.class.getName());
060            } catch (Exception e) {
061            }
062        }
063    
064        /**
065         * If the bundle fulfills the conditions below, it could be started
066         * a. Not in the UNINSTALLED status.
067         * b. Not in the STARTING status.
068         * c. Not a fragment bundle.
069         * @param bundle
070         * @return
071         */
072        public static boolean canStart(Bundle bundle) {
073            return (bundle.getState() != Bundle.UNINSTALLED) && (bundle.getState() != Bundle.STARTING) && (!isFragment(bundle));
074        }
075    
076        /**
077         * If the bundle fulfills the conditions below, it could be stopped
078         * a. Not in the UNINSTALLED status.
079         * b. Not in the STOPPING status.
080         * c. Not a fragment bundle.
081         * @param bundle
082         * @return
083         */
084        public static boolean canStop(Bundle bundle) {
085            return (bundle.getState() != Bundle.UNINSTALLED) && (bundle.getState() != Bundle.STOPPING) && (!isFragment(bundle));
086        }
087    
088        /**
089         * If the bundle fulfills the conditions below, it could be un-installed
090         * a. Not in the UNINSTALLED status.
091         * @param bundle
092         * @return
093         */
094        public static boolean canUninstall(Bundle bundle) {
095            return bundle.getState() != Bundle.UNINSTALLED;
096        }
097    
098        public static boolean isFragment(Bundle bundle) {
099            Dictionary headers = bundle.getHeaders();
100            return (headers != null && headers.get(Constants.FRAGMENT_HOST) != null);
101        }
102    
103        /**
104         * Returns bundle (if any) associated with current thread's context classloader.
105         * Invoking this method is equivalent to getBundle(Thread.currentThread().getContextClassLoader(), unwrap)
106         */
107        public static Bundle getContextBundle(boolean unwrap) {
108            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
109            return classLoader == null ? null : getBundle(classLoader, unwrap);
110        }
111    
112        /**
113         *  Returns bundle (if any) associated with the classloader.
114         * @param classLoader 
115         * @param unwrap if true and if the bundle associated with the context classloader is a
116         *        {@link DelegatingBundle}, this function will return the main application bundle
117         *        backing the {@link DelegatingBundle}. Otherwise, the bundle associated with
118         *        the context classloader is returned as is. See {@link BundleClassLoader#getBundle(boolean)}
119         *        for more information.
120         * @return The bundle associated with the classloader. Might be null.
121         */
122        public static Bundle getBundle(ClassLoader classLoader, boolean unwrap) {
123            if (classLoader instanceof DelegatingBundleReference) {
124                return ((DelegatingBundleReference) classLoader).getBundle(unwrap);
125            } else if (classLoader instanceof BundleReference) {
126                return ((BundleReference) classLoader).getBundle();
127            } else {
128                return null;
129            }
130        }
131    
132        /**
133         * If the given bundle is a {@link DelegatingBundle} this function will return the main 
134         * application bundle backing the {@link DelegatingBundle}. Otherwise, the bundle
135         * passed in is returned as is.
136         */
137        public static Bundle unwrapBundle(Bundle bundle) {
138            if (bundle instanceof DelegatingBundle) {
139                return ((DelegatingBundle) bundle).getMainBundle();
140            }
141            return bundle;
142        }
143        
144        /**
145         * Works like {@link Bundle#getEntryPaths(String)} but also returns paths
146         * in attached fragment bundles.
147         *
148         * @param bundle
149         * @param name
150         * @return
151         */
152        public static Enumeration<String> getEntryPaths(Bundle bundle, String name) {
153            Enumeration<URL> entries = bundle.findEntries(name, null, false);
154            if (entries == null) {
155                return null;
156            }
157            LinkedHashSet<String> paths = new LinkedHashSet<String>();
158            while (entries.hasMoreElements()) {
159                URL url = entries.nextElement();
160                String path = url.getPath();
161                if (path.startsWith("/")) {
162                    path = path.substring(1);
163                }
164                paths.add(path);
165            }
166            return Collections.enumeration(paths);
167        }
168    
169        /**
170         * Works like {@link Bundle#getEntry(String)} but also checks
171         * attached fragment bundles for the given entry.
172         *
173         * @param bundle
174         * @param name
175         * @return
176         */
177        public static URL getEntry(Bundle bundle, String name) {
178            if (name.equals("/")) {
179                return bundle.getEntry(name);
180            } else if (name.endsWith("/")) {
181                name = name.substring(0, name.length() - 1);
182            }
183            String path;
184            String pattern;
185            int pos = name.lastIndexOf("/");
186            if (pos == -1) {
187                path = "/";
188                pattern = name;
189            } else if (pos == 0) {
190                path = "/";
191                pattern = name.substring(1);
192            } else {
193                path = name.substring(0, pos);
194                pattern = name.substring(pos + 1);
195            }
196            Enumeration<URL> entries = bundle.findEntries(path, pattern, false);
197            if (entries != null && entries.hasMoreElements()) {
198                return entries.nextElement();
199            } else {
200                return null;
201            }
202        }
203    
204        public static LinkedHashSet<Bundle> getWiredBundles(Bundle bundle) {
205            ServiceReference reference = bundle.getBundleContext().getServiceReference(PackageAdmin.class.getName());
206            PackageAdmin packageAdmin = (PackageAdmin) bundle.getBundleContext().getService(reference);
207            try {
208                return getWiredBundles(packageAdmin, bundle);
209            } finally {
210                bundle.getBundleContext().ungetService(reference);
211            }
212        }
213    
214        public static LinkedHashSet<Bundle> getWiredBundles(PackageAdmin packageAdmin, Bundle bundle) {
215            BundleDescription description = new BundleDescription(bundle.getHeaders());
216            // handle static wire via Import-Package
217            List<BundleDescription.ImportPackage> imports = description.getExternalImports();
218            LinkedHashSet<Bundle> wiredBundles = new LinkedHashSet<Bundle>();
219            for (BundleDescription.ImportPackage packageImport : imports) {
220                ExportedPackage[] exports = packageAdmin.getExportedPackages(packageImport.getName());
221                Bundle wiredBundle = getWiredBundle(bundle, exports);
222                if (wiredBundle != null) {
223                    wiredBundles.add(wiredBundle);
224                }
225            }
226            // handle dynamic wire via DynamicImport-Package
227            if (!description.getDynamicImportPackage().isEmpty()) {
228                for (Bundle b : bundle.getBundleContext().getBundles()) {
229                    if (!wiredBundles.contains(b)) {
230                        ExportedPackage[] exports = packageAdmin.getExportedPackages(b);
231                        Bundle wiredBundle = getWiredBundle(bundle, exports);
232                        if (wiredBundle != null) {
233                            wiredBundles.add(wiredBundle);
234                        }
235                    }
236                }
237            }
238            return wiredBundles;
239        }
240    
241        static Bundle getWiredBundle(Bundle bundle, ExportedPackage[] exports) {
242            if (exports != null) {
243                for (ExportedPackage exportedPackage : exports) {
244                    Bundle[] importingBundles = exportedPackage.getImportingBundles();
245                    if (importingBundles != null) {
246                        for (Bundle importingBundle : importingBundles) {
247                            if (importingBundle == bundle) {
248                                return exportedPackage.getExportingBundle();
249                            }
250                        }
251                    }
252                }
253            }
254            return null;
255        }
256    }