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: 937957 $ $Date: 2010-04-26 10:00:08 +0200 (Mon, 26 Apr 2010) $
038     */
039    public class BundleUtils {
040    
041        public static boolean canStart(Bundle bundle) {
042            return (bundle.getState() != Bundle.UNINSTALLED) && (bundle.getState() != Bundle.STARTING) && (!isFragment(bundle));
043        }
044    
045        public static boolean canStop(Bundle bundle) {
046            return (bundle.getState() != Bundle.UNINSTALLED) && (bundle.getState() != Bundle.STOPPING) && (!isFragment(bundle));
047        }
048    
049        public static boolean canUninstall(Bundle bundle) {
050            return bundle.getState() != Bundle.UNINSTALLED;
051        }
052    
053        public static boolean isFragment(Bundle bundle) {
054            Dictionary headers = bundle.getHeaders();
055            return (headers != null && headers.get(Constants.FRAGMENT_HOST) != null);
056        }
057    
058        /**
059         * Returns bundle (if any) associated with current thread's context classloader.
060         *
061         * @param unwrap if true and if the bundle associated with the context classloader is a
062         *        {@link DelegatingBundle}, this function will return the main application bundle
063         *        backing with the {@link DelegatingBundle}. Otherwise, the bundle associated with
064         *        the context classloader is returned as is. See {@link BundleClassLoader#getBundle(boolean)}
065         *        for more information.
066         * @return The bundle associated with the current thread's context classloader. Might be null.
067         */
068        public static Bundle getContextBundle(boolean unwrap) {
069            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
070            if (classLoader instanceof BundleClassLoader) {
071                return ((BundleClassLoader) classLoader).getBundle(unwrap);
072            } else if (classLoader instanceof BundleReference) {
073                return ((BundleReference) classLoader).getBundle();
074            } else {
075                return null;
076            }
077        }
078    
079        /**
080         * Works like {@link Bundle#getEntryPaths(String)} but also returns paths
081         * in attached fragment bundles.
082         *
083         * @param bundle
084         * @param name
085         * @return
086         */
087        public static Enumeration<String> getEntryPaths(Bundle bundle, String name) {
088            Enumeration<URL> entries = bundle.findEntries(name, null, false);
089            if (entries == null) {
090                return null;
091            }
092            LinkedHashSet<String> paths = new LinkedHashSet<String>();
093            while (entries.hasMoreElements()) {
094                URL url = entries.nextElement();
095                String path = url.getPath();
096                if (path.startsWith("/")) {
097                    path = path.substring(1);
098                }
099                paths.add(path);
100            }
101            return Collections.enumeration(paths);
102        }
103    
104        /**
105         * Works like {@link Bundle#getEntry(String)} but also checks
106         * attached fragment bundles for the given entry.
107         *
108         * @param bundle
109         * @param name
110         * @return
111         */
112        public static URL getEntry(Bundle bundle, String name) {
113            if (name.equals("/")) {
114                return bundle.getEntry(name);
115            } else if (name.endsWith("/")) {
116                name = name.substring(0, name.length() - 1);
117            }
118            String path;
119            String pattern;
120            int pos = name.lastIndexOf("/");
121            if (pos == -1) {
122                path = "/";
123                pattern = name;
124            } else if (pos == 0) {
125                path = "/";
126                pattern = name.substring(1);
127            } else {
128                path = name.substring(0, pos);
129                pattern = name.substring(pos + 1);
130            }
131            Enumeration<URL> entries = bundle.findEntries(path, pattern, false);
132            if (entries != null && entries.hasMoreElements()) {
133                return entries.nextElement();
134            } else {
135                return null;
136            }
137        }
138    
139        public static LinkedHashSet<Bundle> getWiredBundles(Bundle bundle) {
140            ServiceReference reference = bundle.getBundleContext().getServiceReference(PackageAdmin.class.getName());
141            PackageAdmin packageAdmin = (PackageAdmin) bundle.getBundleContext().getService(reference);
142            try {
143                return getWiredBundles(packageAdmin, bundle);
144            } finally {
145                bundle.getBundleContext().ungetService(reference);
146            }
147        }
148    
149        public static LinkedHashSet<Bundle> getWiredBundles(PackageAdmin packageAdmin, Bundle bundle) {
150            BundleDescription description = new BundleDescription(bundle.getHeaders());
151            // handle static wire via Import-Package
152            List<BundleDescription.ImportPackage> imports = description.getExternalImports();
153            LinkedHashSet<Bundle> wiredBundles = new LinkedHashSet<Bundle>();
154            for (BundleDescription.ImportPackage packageImport : imports) {
155                ExportedPackage[] exports = packageAdmin.getExportedPackages(packageImport.getName());
156                Bundle wiredBundle = getWiredBundle(bundle, exports);
157                if (wiredBundle != null) {
158                    wiredBundles.add(wiredBundle);
159                }
160            }
161            // handle dynamic wire via DynamicImport-Package
162            if (!description.getDynamicImportPackage().isEmpty()) {
163                for (Bundle b : bundle.getBundleContext().getBundles()) {
164                    if (!wiredBundles.contains(b)) {
165                        ExportedPackage[] exports = packageAdmin.getExportedPackages(b);
166                        Bundle wiredBundle = getWiredBundle(bundle, exports);
167                        if (wiredBundle != null) {
168                            wiredBundles.add(wiredBundle);
169                        }
170                    }
171                }
172            }
173            return wiredBundles;
174        }
175    
176        private static Bundle getWiredBundle(Bundle bundle, ExportedPackage[] exports) {
177            if (exports != null) {
178                for (ExportedPackage exportedPackage : exports) {
179                    Bundle[] importingBundles = exportedPackage.getImportingBundles();
180                    if (importingBundles != null) {
181                        for (Bundle importingBundle : importingBundles) {
182                            if (importingBundle == bundle) {
183                                return exportedPackage.getExportingBundle();
184                            }
185                        }
186                    }
187                }
188            }
189            return null;
190        }
191    }