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    package org.apache.felix.framework;
020    
021    import org.apache.felix.framework.searchpolicy.*;
022    import org.apache.felix.moduleloader.*;
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.lang.reflect.Constructor;
026    import java.lang.reflect.Method;
027    import java.lang.reflect.Proxy;
028    import java.net.MalformedURLException;
029    import java.net.URL;
030    import java.net.URLStreamHandler;
031    import java.security.ProtectionDomain;
032    import java.security.SecureClassLoader;
033    import java.util.ArrayList;
034    import java.util.Arrays;
035    
036    import java.util.Enumeration;
037    import java.util.HashMap;
038    import java.util.HashSet;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Set;
042    import java.util.Vector;
043    import org.apache.felix.framework.Felix.FelixResolver;
044    import org.apache.felix.framework.cache.JarContent;
045    import org.apache.felix.framework.util.CompoundEnumeration;
046    import org.apache.felix.framework.util.FelixConstants;
047    import org.apache.felix.framework.util.SecureAction;
048    import org.apache.felix.framework.util.SecurityManagerEx;
049    import org.apache.felix.framework.util.Util;
050    import org.apache.felix.framework.util.manifestparser.Capability;
051    import org.apache.felix.framework.util.manifestparser.ManifestParser;
052    import org.apache.felix.framework.util.manifestparser.R4Library;
053    import org.apache.felix.framework.util.manifestparser.Requirement;
054    import org.osgi.framework.Bundle;
055    import org.osgi.framework.BundleException;
056    import org.osgi.framework.BundleReference;
057    import org.osgi.framework.Constants;
058    import org.osgi.framework.InvalidSyntaxException;
059    import org.osgi.framework.Version;
060    
061    public class ModuleImpl implements IModule
062    {
063        private final Logger m_logger;
064        private final Map m_configMap;
065        private final FelixResolver m_resolver;
066        private final String m_id;
067        private final IContent m_content;
068        private final Map m_headerMap;
069        private final URLStreamHandler m_streamHandler;
070    
071        private final String m_manifestVersion;
072        private final boolean m_isExtension;
073        private final String m_symbolicName;
074        private final Version m_version;
075    
076        private final ICapability[] m_capabilities;
077        private ICapability[] m_cachedCapabilities = null;
078        private final IRequirement[] m_requirements;
079        private IRequirement[] m_cachedRequirements = null;
080        private final IRequirement[] m_dynamicRequirements;
081        private IRequirement[] m_cachedDynamicRequirements = null;
082        private final R4Library[] m_nativeLibraries;
083        private final int m_declaredActivationPolicy;
084        private final String[] m_activationIncludes;
085        private final String[] m_activationExcludes;
086    
087        private final Bundle m_bundle;
088    
089        private IModule[] m_fragments = null;
090        private IWire[] m_wires = null;
091        private IModule[] m_dependentHosts = new IModule[0];
092        private IModule[] m_dependentImporters = new IModule[0];
093        private IModule[] m_dependentRequirers = new IModule[0];
094        private volatile boolean m_isResolved = false;
095    
096        private IContent[] m_contentPath;
097        private IContent[] m_fragmentContents = null;
098        private ModuleClassLoader m_classLoader;
099        private boolean m_isActivationTriggered = false;
100        private ProtectionDomain m_protectionDomain = null;
101        private static SecureAction m_secureAction = new SecureAction();
102    
103        // Bundle-specific class loader for boot delegation.
104        private final ClassLoader m_bootClassLoader;
105        // Default class loader for boot delegation.
106        private final static ClassLoader m_defBootClassLoader;
107    
108        // Statically define the default class loader for boot delegation.
109        static
110        {
111            ClassLoader cl = null;
112            try
113            {
114                Constructor ctor = m_secureAction.getDeclaredConstructor(
115                    SecureClassLoader.class, new Class[] { ClassLoader.class });
116                m_secureAction.setAccesssible(ctor);
117                cl = (ClassLoader) m_secureAction.invoke(ctor, new Object[] { null });
118            }
119            catch (Throwable ex)
120            {
121                // On Android we get an exception if we set the parent class loader
122                // to null, so we will work around that case by setting the parent
123                // class loader to the system class loader in getClassLoader() below.
124                cl = null;
125                System.err.println("Problem creating boot delegation class loader: " + ex);
126            }
127            m_defBootClassLoader = cl;
128        }
129    
130        // Boot delegation packages.
131        private final String[] m_bootPkgs;
132        private final boolean[] m_bootPkgWildcards;
133    
134        // Boolean flag to enable/disable implicit boot delegation.
135        private final boolean m_implicitBootDelegation;
136    
137        // Re-usable security manager for accessing class context.
138        private static SecurityManagerEx m_sm = new SecurityManagerEx();
139    
140        // Thread local to detect class loading cycles.
141        private final ThreadLocal m_cycleCheck = new ThreadLocal();
142    
143        // Thread local to keep track of deferred activation.
144        private static final ThreadLocal m_deferredActivation = new ThreadLocal();
145    
146        /**
147         * This constructor is used by the extension manager, since it needs
148         * a constructor that does not throw an exception.
149         * @param logger
150         * @param bundle
151         * @param id
152         * @param bootPkgs
153         * @param bootPkgWildcards
154         * @throws org.osgi.framework.BundleException
155         */
156        public ModuleImpl(
157            Logger logger, Bundle bundle, String id,
158            String[] bootPkgs, boolean[] bootPkgWildcards)
159        {
160            m_logger = logger;
161            m_configMap = null;
162            m_resolver = null;
163            m_bundle = bundle;
164            m_id = id;
165            m_headerMap = null;
166            m_content = null;
167            m_streamHandler = null;
168            m_bootPkgs = bootPkgs;
169            m_bootPkgWildcards = bootPkgWildcards;
170            m_manifestVersion = null;
171            m_symbolicName = null;
172            m_isExtension = false;
173            m_version = null;
174            m_capabilities = null;
175            m_requirements = null;
176            m_dynamicRequirements = null;
177            m_nativeLibraries = null;
178            m_declaredActivationPolicy = EAGER_ACTIVATION;
179            m_activationExcludes = null;
180            m_activationIncludes = null;
181            m_implicitBootDelegation = false;
182            m_bootClassLoader = m_defBootClassLoader;
183        }
184    
185        public ModuleImpl(
186            Logger logger, Map configMap, FelixResolver resolver,
187            Bundle bundle, String id, Map headerMap, IContent content,
188            URLStreamHandler streamHandler, String[] bootPkgs,
189            boolean[] bootPkgWildcards)
190            throws BundleException
191        {
192            m_logger = logger;
193            m_configMap = configMap;
194            m_resolver = resolver;
195            m_bundle = bundle;
196            m_id = id;
197            m_headerMap = headerMap;
198            m_content = content;
199            m_streamHandler = streamHandler;
200            m_bootPkgs = bootPkgs;
201            m_bootPkgWildcards = bootPkgWildcards;
202    
203            m_implicitBootDelegation =
204                (m_configMap.get(FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP) == null)
205                || Boolean.valueOf(
206                    (String) m_configMap.get(
207                        FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP)).booleanValue();
208    
209            ClassLoader bootLoader = m_defBootClassLoader;
210            Object map = m_configMap.get(FelixConstants.BOOT_CLASSLOADERS_PROP);
211            if (map instanceof Map)
212            {
213                Object l = ((Map) map).get(bundle);
214                if (l instanceof ClassLoader)
215                {
216                    bootLoader = (ClassLoader) l;
217                }
218            }
219            m_bootClassLoader = bootLoader;
220    
221            ManifestParser mp = new ManifestParser(m_logger, m_configMap, this, m_headerMap);
222    
223            // Record some of the parsed metadata. Note, if this is an extension
224            // bundle it's exports are removed, since they will be added to the
225            // system bundle directly later on.
226            m_manifestVersion = mp.getManifestVersion();
227            m_version = mp.getBundleVersion();
228            m_capabilities = mp.isExtension() ? null : mp.getCapabilities();
229            m_requirements = mp.getRequirements();
230            m_dynamicRequirements = mp.getDynamicRequirements();
231            m_nativeLibraries = mp.getLibraries();
232            m_declaredActivationPolicy = mp.getActivationPolicy();
233            m_activationExcludes = (mp.getActivationExcludeDirective() == null)
234                ? null
235                : ManifestParser.parseDelimitedString(mp.getActivationExcludeDirective(), ",");
236            m_activationIncludes = (mp.getActivationIncludeDirective() == null)
237                ? null
238                : ManifestParser.parseDelimitedString(mp.getActivationIncludeDirective(), ",");
239            m_symbolicName = mp.getSymbolicName();
240            m_isExtension = mp.isExtension();
241        }
242    
243        //
244        // Metadata access methods.
245        //
246    
247        public Map getHeaders()
248        {
249            return m_headerMap;
250        }
251    
252        public boolean isExtension()
253        {
254            return m_isExtension;
255        }
256    
257        public String getSymbolicName()
258        {
259            return m_symbolicName;
260        }
261    
262        public String getManifestVersion()
263        {
264            return m_manifestVersion;
265        }
266    
267        public Version getVersion()
268        {
269            return m_version;
270        }
271    
272        public synchronized ICapability[] getCapabilities()
273        {
274            if (m_cachedCapabilities == null)
275            {
276                List capList = (m_capabilities == null)
277                    ? new ArrayList() : new ArrayList(Arrays.asList(m_capabilities));
278                for (int fragIdx = 0;
279                    (m_fragments != null) && (fragIdx < m_fragments.length);
280                    fragIdx++)
281                {
282                    ICapability[] caps = m_fragments[fragIdx].getCapabilities();
283                    for (int capIdx = 0;
284                        (caps != null) && (capIdx < caps.length);
285                        capIdx++)
286                    {
287                        if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
288                        {
289                            capList.add(
290                                new Capability(
291                                    this,
292                                    caps[capIdx].getNamespace(),
293                                    ((Capability) caps[capIdx]).getDirectives(),
294                                    ((Capability) caps[capIdx]).getAttributes()));
295                        }
296                    }
297                }
298                m_cachedCapabilities = (ICapability[])
299                    capList.toArray(new ICapability[capList.size()]);
300            }
301            return m_cachedCapabilities;
302        }
303    
304        public synchronized IRequirement[] getRequirements()
305        {
306            if (m_cachedRequirements == null)
307            {
308                List allReqs = new ArrayList();
309                Map pkgMap = new HashMap();
310                Map rbMap = new HashMap();
311                for (int i = 0; (m_requirements != null) && i < m_requirements.length; i++)
312                {
313                    if (m_requirements[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
314                    {
315                        pkgMap.put(
316                            ((Requirement) m_requirements[i]).getTargetName(),
317                            m_requirements[i]);
318                    }
319                    else if (m_requirements[i].getNamespace().equals(ICapability.MODULE_NAMESPACE))
320                    {
321                        rbMap.put(
322                            ((Requirement) m_requirements[i]).getTargetName(),
323                            m_requirements[i]);
324                    }
325                    else
326                    {
327                        allReqs.add(m_requirements[i]);
328                    }
329                }
330    
331                // Aggregate host and fragment bundle and package requirements.
332                for (int fragIdx = 0;
333                    (m_fragments != null) && (fragIdx < m_fragments.length);
334                    fragIdx++)
335                {
336                    IRequirement[] reqs = m_fragments[fragIdx].getRequirements();
337                    for (int reqIdx = 0;
338                        (reqs != null) && (reqIdx < reqs.length);
339                        reqIdx++)
340                    {
341                        if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
342                        {
343                            // If the current fragment requirement overlaps a previously
344                            // added requirement, then calculate a new intersecting requirement.
345                            Requirement req = (Requirement) pkgMap.get(
346                                ((Requirement) reqs[reqIdx]).getTargetName());
347                            if (req != null)
348                            {
349                                req = FelixResolverState.calculateVersionIntersection(
350                                    req, (Requirement) reqs[reqIdx]);
351                            }
352                            else
353                            {
354                                req = (Requirement) reqs[reqIdx];
355                            }
356                            pkgMap.put(req.getTargetName(), req);
357                        }
358                        else if (reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
359                        {
360                            // If the current fragment requirement overlaps a previously
361                            // added requirement, then calculate a new intersecting requirement.
362                            Requirement req = (Requirement) pkgMap.get(
363                                ((Requirement) reqs[reqIdx]).getTargetName());
364                            if (req != null)
365                            {
366                                req = FelixResolverState.calculateVersionIntersection(
367                                    req, (Requirement) reqs[reqIdx]);
368                            }
369                            else
370                            {
371                                req = (Requirement) reqs[reqIdx];
372                            }
373                            rbMap.put(req.getTargetName(), req);
374                        }
375                    }
376                }
377                allReqs.addAll(pkgMap.values());
378                allReqs.addAll(rbMap.values());
379                m_cachedRequirements = (IRequirement[])
380                    allReqs.toArray(new IRequirement[allReqs.size()]);
381            }
382            return m_cachedRequirements;
383        }
384    
385        public synchronized IRequirement[] getDynamicRequirements()
386        {
387            if (m_cachedDynamicRequirements == null)
388            {
389                List reqList = (m_dynamicRequirements == null)
390                    ? new ArrayList() : new ArrayList(Arrays.asList(m_dynamicRequirements));
391                for (int fragIdx = 0;
392                    (m_fragments != null) && (fragIdx < m_fragments.length);
393                    fragIdx++)
394                {
395                    IRequirement[] reqs = m_fragments[fragIdx].getDynamicRequirements();
396                    for (int reqIdx = 0;
397                        (reqs != null) && (reqIdx < reqs.length);
398                        reqIdx++)
399                    {
400                        if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
401                        {
402                            reqList.add(reqs[reqIdx]);
403                        }
404                    }
405                }
406                m_cachedDynamicRequirements = (IRequirement[])
407                    reqList.toArray(new IRequirement[reqList.size()]);
408            }
409            return m_cachedDynamicRequirements;
410        }
411    
412        public synchronized R4Library[] getNativeLibraries()
413        {
414            R4Library[] result = null;
415            if (m_isResolved)
416            {
417                List nativeList = (m_nativeLibraries == null)
418                    ? new ArrayList() : new ArrayList(Arrays.asList(m_nativeLibraries));
419                for (int fragIdx = 0;
420                    (m_fragments != null) && (fragIdx < m_fragments.length);
421                    fragIdx++)
422                {
423                    R4Library[] libs = m_fragments[fragIdx].getNativeLibraries();
424                    for (int reqIdx = 0;
425                        (libs != null) && (reqIdx < libs.length);
426                        reqIdx++)
427                    {
428                        nativeList.add(libs[reqIdx]);
429                    }
430                }
431    
432                // We need to return null here if we don't have any libraries, since a
433                // zero-length array is used to indicate that matching native libraries
434                // could not be found when resolving the bundle.
435                result = (nativeList.size() == 0)
436                    ? null
437                    : (R4Library[]) nativeList.toArray(new R4Library[nativeList.size()]);
438            }
439            else
440            {
441                result = m_nativeLibraries;
442            }
443    
444            return result;
445        }
446    
447        public int getDeclaredActivationPolicy()
448        {
449            return m_declaredActivationPolicy;
450        }
451    
452        synchronized boolean isActivationTriggered()
453        {
454            return m_isActivationTriggered;
455        }
456    
457        boolean isActivationTrigger(String pkgName)
458        {
459            if ((m_activationIncludes == null) && (m_activationExcludes == null))
460            {
461                return true;
462            }
463    
464            // If there are no include filters then all classes are included
465            // by default, otherwise try to find one match.
466            boolean included = (m_activationIncludes == null);
467            for (int i = 0;
468                (!included) && (m_activationIncludes != null) && (i < m_activationIncludes.length);
469                i++)
470            {
471                included = m_activationIncludes[i].equals(pkgName);
472            }
473    
474            // If there are no exclude filters then no classes are excluded
475            // by default, otherwise try to find one match.
476            boolean excluded = false;
477            for (int i = 0;
478                (!excluded) && (m_activationExcludes != null) && (i < m_activationExcludes.length);
479                i++)
480            {
481                excluded = m_activationExcludes[i].equals(pkgName);
482            }
483            return included && !excluded;
484        }
485    
486        //
487        // Run-time data access.
488        //
489    
490        public Bundle getBundle()
491        {
492            return m_bundle;
493        }
494    
495        public String getId()
496        {
497            return m_id;
498        }
499    
500        public synchronized IWire[] getWires()
501        {
502            return m_wires;
503        }
504    
505        public synchronized void setWires(IWire[] wires)
506        {
507            // Remove module from old wire modules' dependencies,
508            // since we are no longer dependent on any the moduels
509            // from the old wires.
510            for (int i = 0; (m_wires != null) && (i < m_wires.length); i++)
511            {
512                if (m_wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
513                {
514                    ((ModuleImpl) m_wires[i].getExporter()).removeDependentRequirer(this);
515                }
516                else if (m_wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
517                {
518                    ((ModuleImpl) m_wires[i].getExporter()).removeDependentImporter(this);
519                }
520            }
521    
522            m_wires = wires;
523    
524            // Add ourself as a dependent to the new wires' modules.
525            for (int i = 0; (m_wires != null) && (i < m_wires.length); i++)
526            {
527                if (m_wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
528                {
529                    ((ModuleImpl) m_wires[i].getExporter()).addDependentRequirer(this);
530                }
531                else if (m_wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
532                {
533                    ((ModuleImpl) m_wires[i].getExporter()).addDependentImporter(this);
534                }
535            }
536        }
537    
538        public boolean isResolved()
539        {
540            return m_isResolved;
541        }
542    
543        public void setResolved()
544        {
545            m_isResolved = true;
546        }
547    
548        //
549        // Content access methods.
550        //
551    
552        public IContent getContent()
553        {
554            return m_content;
555        }
556    
557        private synchronized IContent[] getContentPath()
558        {
559            if (m_contentPath == null)
560            {
561                try
562                {
563                    m_contentPath = initializeContentPath();
564                }
565                catch (Exception ex)
566                {
567                    m_logger.log(Logger.LOG_ERROR, "Unable to get module class path.", ex);
568                }
569            }
570            return m_contentPath;
571        }
572    
573        private IContent[] initializeContentPath() throws Exception
574        {
575            List contentList = new ArrayList();
576            calculateContentPath(this, m_content, contentList, true);
577            for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
578            {
579                calculateContentPath(m_fragments[i], m_fragmentContents[i], contentList, false);
580            }
581            return (IContent[]) contentList.toArray(new IContent[contentList.size()]);
582        }
583    
584        private List calculateContentPath(
585            IModule module, IContent content, List contentList, boolean searchFragments)
586            throws Exception
587        {
588            // Creating the content path entails examining the bundle's
589            // class path to determine whether the bundle JAR file itself
590            // is on the bundle's class path and then creating content
591            // objects for everything on the class path.
592    
593            // Create a list to contain the content path for the specified content.
594            List localContentList = new ArrayList();
595    
596            // Find class path meta-data.
597            String classPath = (String) module.getHeaders().get(FelixConstants.BUNDLE_CLASSPATH);
598            // Parse the class path into strings.
599            String[] classPathStrings = ManifestParser.parseDelimitedString(
600                classPath, FelixConstants.CLASS_PATH_SEPARATOR);
601    
602            if (classPathStrings == null)
603            {
604                classPathStrings = new String[0];
605            }
606    
607            // Create the bundles class path.
608            for (int i = 0; i < classPathStrings.length; i++)
609            {
610                // Remove any leading slash, since all bundle class path
611                // entries are relative to the root of the bundle.
612                classPathStrings[i] = (classPathStrings[i].startsWith("/"))
613                    ? classPathStrings[i].substring(1)
614                    : classPathStrings[i];
615    
616                // Check for the bundle itself on the class path.
617                if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
618                {
619                    localContentList.add(content);
620                }
621                else
622                {
623                    // Try to find the embedded class path entry in the current
624                    // content.
625                    IContent embeddedContent = content.getEntryAsContent(classPathStrings[i]);
626                    // If the embedded class path entry was not found, it might be
627                    // in one of the fragments if the current content is the bundle,
628                    // so try to search the fragments if necessary.
629                    for (int fragIdx = 0;
630                        searchFragments && (embeddedContent == null)
631                            && (m_fragmentContents != null) && (fragIdx < m_fragmentContents.length);
632                        fragIdx++)
633                    {
634                        embeddedContent = m_fragmentContents[fragIdx].getEntryAsContent(classPathStrings[i]);
635                    }
636                    // If we found the embedded content, then add it to the
637                    // class path content list.
638                    if (embeddedContent != null)
639                    {
640                        localContentList.add(embeddedContent);
641                    }
642                    else
643                    {
644    // TODO: FRAMEWORK - Per the spec, this should fire a FrameworkEvent.INFO event;
645    //       need to create an "Eventer" class like "Logger" perhaps.
646                        m_logger.log(Logger.LOG_INFO,
647                            "Class path entry not found: "
648                            + classPathStrings[i]);
649                    }
650                }
651            }
652    
653            // If there is nothing on the class path, then include
654            // "." by default, as per the spec.
655            if (localContentList.size() == 0)
656            {
657                localContentList.add(content);
658            }
659    
660            // Now add the local contents to the global content list and return it.
661            contentList.addAll(localContentList);
662            return contentList;
663        }
664    
665        public Class getClassByDelegation(String name) throws ClassNotFoundException
666        {
667            // We do not call getClassLoader().loadClass() for arrays because
668            // it does not correctly handle array types, which is necessary in
669            // cases like deserialization using a wrapper class loader.
670            if ((name != null) && (name.length() > 0) && (name.charAt(0) == '['))
671            {
672                return Class.forName(name, false, getClassLoader());
673            }
674            return getClassLoader().loadClass(name);
675        }
676    
677        public URL getResourceByDelegation(String name)
678        {
679            try
680            {
681                return (URL) findClassOrResourceByDelegation(name, false);
682            }
683            catch (ClassNotFoundException ex)
684            {
685                // This should never be thrown because we are loading resources.
686            }
687            catch (ResourceNotFoundException ex)
688            {
689                m_logger.log(
690                    Logger.LOG_DEBUG,
691                    ex.getMessage());
692            }
693            return null;
694        }
695    
696        private Object findClassOrResourceByDelegation(String name, boolean isClass)
697            throws ClassNotFoundException, ResourceNotFoundException
698        {
699            Object result = null;
700    
701            Set requestSet = (Set) m_cycleCheck.get();
702            if (requestSet == null)
703            {
704                requestSet = new HashSet();
705                m_cycleCheck.set(requestSet);
706            }
707            if (requestSet.add(name))
708            {
709                try
710                {
711                    // First, try to resolve the originating module.
712                    m_resolver.resolve(this);
713    
714                    // Get the package of the target class/resource.
715                    String pkgName = (isClass)
716                        ? Util.getClassPackage(name)
717                        : Util.getResourcePackage(name);
718    
719                    // Delegate any packages listed in the boot delegation
720                    // property to the parent class loader.
721                    if (shouldBootDelegate(pkgName))
722                    {
723                        try
724                        {
725                            // Get the appropriate class loader for delegation.
726                            ClassLoader parent = (m_classLoader == null)
727                                ? determineParentClassLoader() : m_classLoader.getParent();
728                            parent = (parent == null) ? m_bootClassLoader : parent;
729                            result = (isClass)
730                                ? (Object) parent.loadClass(name)
731                                : (Object) parent.getResource(name);
732                            // If this is a java.* package, then always terminate the
733                            // search; otherwise, continue to look locally if not found.
734                            if (pkgName.startsWith("java.") || (result != null))
735                            {
736                                return result;
737                            }
738                        }
739                        catch (ClassNotFoundException ex)
740                        {
741                            // If this is a java.* package, then always terminate the
742                            // search; otherwise, continue to look locally if not found.
743                            if (pkgName.startsWith("java."))
744                            {
745                                throw ex;
746                            }
747                        }
748                    }
749    
750                    // Look in the module's imports. Note that the search may
751                    // be aborted if this method throws an exception, otherwise
752                    // it continues if a null is returned.
753                    result = searchImports(name, isClass);
754    
755                    // If not found, try the module's own class path.
756                    if (result == null)
757                    {
758                        result = (isClass)
759                            ? (Object) getClassLoader().findClass(name)
760                            : (Object) getResourceLocal(name);
761    
762                        // If still not found, then try the module's dynamic imports.
763                        if (result == null)
764                        {
765                            result = searchDynamicImports(name, pkgName, isClass);
766                        }
767                    }
768                }
769                catch (ResolveException ex)
770                {
771                    if (isClass)
772                    {
773                        // We do not use the resolve exception as the
774                        // cause of the exception, since this would
775                        // potentially leak internal module information.
776                        throw new ClassNotFoundException(
777                            name + ": cannot resolve package "
778                            + ex.getRequirement());
779                    }
780                    else
781                    {
782                        // The spec states that if the bundle cannot be resolved, then
783                        // only the local bundle's resources should be searched. So we
784                        // will ask the module's own class path.
785                        URL url = getResourceLocal(name);
786                        if (url != null)
787                        {
788                            return url;
789                        }
790    
791                        // We need to throw a resource not found exception.
792                        throw new ResourceNotFoundException(
793                            name + ": cannot resolve package "
794                            + ex.getRequirement());
795                    }
796                }
797                finally
798                {
799                    requestSet.remove(name);
800                }
801            }
802            else
803            {
804                // If a cycle is detected, we should return null to break the
805                // cycle. This should only ever be return to internal class
806                // loading code and not to the actual instigator of the class load.
807                return null;
808            }
809    
810            if (result == null)
811            {
812                if (isClass)
813                {
814                    throw new ClassNotFoundException(name);
815                }
816                else
817                {
818                    throw new ResourceNotFoundException(name);
819                }
820            }
821    
822            return result;
823        }
824    
825        private URL getResourceLocal(String name)
826        {
827            URL url = null;
828    
829            // Remove leading slash, if present, but special case
830            // "/" so that it returns a root URL...this isn't very
831            // clean or meaninful, but the Spring guys want it.
832            if (name.equals("/"))
833            {
834                // Just pick a class path index since it doesn't really matter.
835                url = createURL(1, name);
836            }
837            else if (name.startsWith("/"))
838            {
839                name = name.substring(1);
840            }
841    
842            // Check the module class path.
843            IContent[] contentPath = getContentPath();
844            for (int i = 0;
845                (url == null) &&
846                (i < contentPath.length); i++)
847            {
848                if (contentPath[i].hasEntry(name))
849                {
850                    url = createURL(i + 1, name);
851                }
852            }
853    
854            return url;
855        }
856    
857        public Enumeration getResourcesByDelegation(String name)
858        {
859            Set requestSet = (Set) m_cycleCheck.get();
860            if (requestSet == null)
861            {
862                requestSet = new HashSet();
863                m_cycleCheck.set(requestSet);
864            }
865            if (!requestSet.contains(name))
866            {
867                requestSet.add(name);
868                try
869                {
870                    Enumeration urls = findResourcesByDelegation(name);
871                    return (urls.hasMoreElements()) ? urls : null;
872                }
873                finally
874                {
875                    requestSet.remove(name);
876                }
877            }
878    
879            return null;
880        }
881    
882        private Enumeration findResourcesByDelegation(String name)
883        {
884            Enumeration urls = null;
885            List completeUrlList = new ArrayList();
886    
887            // First, try to resolve the originating module.
888            try
889            {
890                m_resolver.resolve(this);
891            }
892            catch (ResolveException ex)
893            {
894                // The spec states that if the bundle cannot be resolved, then
895                // only the local bundle's resources should be searched. So we
896                // will ask the module's own class path.
897                urls = getResourcesLocal(name);
898                return urls;
899            }
900    
901            // Get the package of the target class/resource.
902            String pkgName = Util.getResourcePackage(name);
903    
904            // Delegate any packages listed in the boot delegation
905            // property to the parent class loader.
906            if (shouldBootDelegate(pkgName))
907            {
908                try
909                {
910                    // Get the appropriate class loader for delegation.
911                    ClassLoader parent = (m_classLoader == null)
912                        ? determineParentClassLoader() : m_classLoader.getParent();
913                    parent = (parent == null) ? m_bootClassLoader : parent;
914                    urls = parent.getResources(name);
915                }
916                catch (IOException ex)
917                {
918                    // This shouldn't happen and even if it does, there
919                    // is nothing we can do, so just ignore it.
920                }
921                // If this is a java.* package, then always terminate the
922                // search; otherwise, continue to look locally.
923                if (pkgName.startsWith("java."))
924                {
925                    return urls;
926                }
927    
928                completeUrlList.add(urls);
929            }
930    
931            // Look in the module's imports.
932            // We delegate to the module's wires for the resources.
933            // If any resources are found, this means that the package of these
934            // resources is imported, we must not keep looking since we do not
935            // support split-packages.
936    
937            // Note that the search may be aborted if this method throws an
938            // exception, otherwise it continues if a null is returned.
939            IWire[] wires = getWires();
940            for (int i = 0; (wires != null) && (i < wires.length); i++)
941            {
942                if (wires[i] instanceof R4Wire)
943                {
944                    try
945                    {
946                        // If we find the class or resource, then return it.
947                        urls = wires[i].getResources(name);
948                    }
949                    catch (ResourceNotFoundException ex)
950                    {
951                        urls = null;
952                    }
953                    if (urls != null)
954                    {
955                        completeUrlList.add(urls);
956                        return new CompoundEnumeration((Enumeration[])
957                            completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
958                    }
959                }
960            }
961    
962            // See whether we can get the resource from the required bundles and
963            // regardless of whether or not this is the case continue to the next
964            // step potentially passing on the result of this search (if any).
965            for (int i = 0; (wires != null) && (i < wires.length); i++)
966            {
967                if (wires[i] instanceof R4WireModule)
968                {
969                    try
970                    {
971                        // If we find the class or resource, then add it.
972                        urls = wires[i].getResources(name);
973                    }
974                    catch (ResourceNotFoundException ex)
975                    {
976                        urls = null;
977                    }
978                    if (urls != null)
979                    {
980                        completeUrlList.add(urls);
981                    }
982                }
983            }
984    
985            // Try the module's own class path. If we can find the resource then
986            // return it together with the results from the other searches else
987            // try to look into the dynamic imports.
988            urls = getResourcesLocal(name);
989            if ((urls != null) && (urls.hasMoreElements()))
990            {
991                completeUrlList.add(urls);
992            }
993            else
994            {
995                // If not found, then try the module's dynamic imports.
996                // At this point, the module's imports were searched and so was the
997                // the module's content. Now we make an attempt to load the
998                // class/resource via a dynamic import, if possible.
999                IWire wire = null;
1000                try
1001                {
1002                    wire = m_resolver.resolveDynamicImport(this, pkgName);
1003                }
1004                catch (ResolveException ex)
1005                {
1006                    // Ignore this since it is likely normal.
1007                }
1008                if (wire != null)
1009                {
1010                    try
1011                    {
1012                        urls = wire.getResources(name);
1013                    }
1014                    catch (ResourceNotFoundException ex)
1015                    {
1016                        urls = null;
1017                    }
1018                    if (urls != null)
1019                    {
1020                        completeUrlList.add(urls);
1021                    }
1022                }
1023            }
1024    
1025            return new CompoundEnumeration((Enumeration[])
1026                completeUrlList.toArray(new Enumeration[completeUrlList.size()]));
1027        }
1028    
1029        private Enumeration getResourcesLocal(String name)
1030        {
1031            Vector v = new Vector();
1032    
1033            // Special case "/" so that it returns a root URLs for
1034            // each bundle class path entry...this isn't very
1035            // clean or meaningful, but the Spring guys want it.
1036            final IContent[] contentPath = getContentPath();
1037            if (name.equals("/"))
1038            {
1039                for (int i = 0; i < contentPath.length; i++)
1040                {
1041                    v.addElement(createURL(i + 1, name));
1042                }
1043            }
1044            else
1045            {
1046                // Remove leading slash, if present.
1047                if (name.startsWith("/"))
1048                {
1049                    name = name.substring(1);
1050                }
1051    
1052                // Check the module class path.
1053                for (int i = 0; i < contentPath.length; i++)
1054                {
1055                    if (contentPath[i].hasEntry(name))
1056                    {
1057                        // Use the class path index + 1 for creating the path so
1058                        // that we can differentiate between module content URLs
1059                        // (where the path will start with 0) and module class
1060                        // path URLs.
1061                        v.addElement(createURL(i + 1, name));
1062                    }
1063                }
1064            }
1065    
1066            return v.elements();
1067        }
1068    
1069        // TODO: API: Investigate how to handle this better, perhaps we need
1070        // multiple URL policies, one for content -- one for class path.
1071        public URL getEntry(String name)
1072        {
1073            URL url = null;
1074    
1075            // Check for the special case of "/", which represents
1076            // the root of the bundle according to the spec.
1077            if (name.equals("/"))
1078            {
1079                url = createURL(0, "/");
1080            }
1081    
1082            if (url == null)
1083            {
1084                // Remove leading slash, if present.
1085                if (name.startsWith("/"))
1086                {
1087                    name = name.substring(1);
1088                }
1089    
1090                // Check the module content.
1091                if (getContent().hasEntry(name))
1092                {
1093                    // Module content URLs start with 0, whereas module
1094                    // class path URLs start with the index into the class
1095                    // path + 1.
1096                    url = createURL(0, name);
1097                }
1098            }
1099    
1100            return url;
1101        }
1102    
1103        public boolean hasInputStream(int index, String urlPath)
1104        {
1105            if (urlPath.startsWith("/"))
1106            {
1107                urlPath = urlPath.substring(1);
1108            }
1109            if (index == 0)
1110            {
1111                return m_content.hasEntry(urlPath);
1112            }
1113            return getContentPath()[index - 1].hasEntry(urlPath);
1114        }
1115    
1116        public InputStream getInputStream(int index, String urlPath)
1117            throws IOException
1118        {
1119            if (urlPath.startsWith("/"))
1120            {
1121                urlPath = urlPath.substring(1);
1122            }
1123            if (index == 0)
1124            {
1125                return m_content.getEntryAsStream(urlPath);
1126            }
1127            return getContentPath()[index - 1].getEntryAsStream(urlPath);
1128        }
1129    
1130        private URL createURL(int port, String path)
1131        {
1132             // Add a slash if there is one already, otherwise
1133             // the is no slash separating the host from the file
1134             // in the resulting URL.
1135             if (!path.startsWith("/"))
1136             {
1137                 path = "/" + path;
1138             }
1139    
1140             try
1141             {
1142                 return m_secureAction.createURL(null, 
1143                     FelixConstants.BUNDLE_URL_PROTOCOL + "://" +  
1144                     m_id + ":" + port + path, m_streamHandler);
1145             }
1146             catch (MalformedURLException ex)
1147             {
1148                 m_logger.log(
1149                     Logger.LOG_ERROR,
1150                     "Unable to create resource URL.",
1151                     ex);
1152             }
1153             return null;
1154        }
1155    
1156        //
1157        // Fragment and dependency management methods.
1158        //
1159    
1160        public synchronized IModule[] getFragments()
1161        {
1162            return m_fragments;
1163        }
1164    
1165        public synchronized void attachFragments(IModule[] fragments) throws Exception
1166        {
1167            // Remove module from old fragment dependencies.
1168            // We will generally only remove module fragment
1169            // dependencies when we are uninstalling the module.
1170            for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
1171            {
1172                ((ModuleImpl) m_fragments[i]).removeDependentHost(this);
1173            }
1174    
1175            // Remove cached capabilities and requirements.
1176            m_cachedCapabilities = null;
1177            m_cachedRequirements = null;
1178            m_cachedDynamicRequirements = null;
1179    
1180            // Update the dependencies on the new fragments.
1181            m_fragments = fragments;
1182    
1183            // We need to add ourself as a dependent of each fragment
1184            // module. We also need to create an array of fragment contents
1185            // to attach to our content loader.
1186            if (m_fragments != null)
1187            {
1188                IContent[] fragmentContents = new IContent[m_fragments.length];
1189                for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
1190                {
1191                    ((ModuleImpl) m_fragments[i]).addDependentHost(this);
1192                    fragmentContents[i] =
1193                        m_fragments[i].getContent()
1194                            .getEntryAsContent(FelixConstants.CLASS_PATH_DOT);
1195                }
1196                // Now attach the fragment contents to our content loader.
1197                attachFragmentContents(fragmentContents);
1198            }
1199        }
1200    
1201        // This must be called holding the object lock.
1202        private void attachFragmentContents(IContent[] fragmentContents)
1203            throws Exception
1204        {
1205            // Close existing fragment contents.
1206            if (m_fragmentContents != null)
1207            {
1208                for (int i = 0; i < m_fragmentContents.length; i++)
1209                {
1210                    m_fragmentContents[i].close();
1211                }
1212            }
1213            m_fragmentContents = fragmentContents;
1214    
1215            if (m_contentPath != null)
1216            {
1217                for (int i = 0; i < m_contentPath.length; i++)
1218                {
1219                    m_contentPath[i].close();
1220                }
1221            }
1222            m_contentPath = initializeContentPath();
1223        }
1224    
1225        public synchronized IModule[] getDependentHosts()
1226        {
1227            return m_dependentHosts;
1228        }
1229    
1230        public synchronized void addDependentHost(IModule module)
1231        {
1232            m_dependentHosts = addDependent(m_dependentHosts, module);
1233        }
1234    
1235        public synchronized void removeDependentHost(IModule module)
1236        {
1237            m_dependentHosts = removeDependent(m_dependentHosts, module);
1238        }
1239    
1240        public synchronized IModule[] getDependentImporters()
1241        {
1242            return m_dependentImporters;
1243        }
1244    
1245        public synchronized void addDependentImporter(IModule module)
1246        {
1247            m_dependentImporters = addDependent(m_dependentImporters, module);
1248        }
1249    
1250        public synchronized void removeDependentImporter(IModule module)
1251        {
1252            m_dependentImporters = removeDependent(m_dependentImporters, module);
1253        }
1254    
1255        public synchronized IModule[] getDependentRequirers()
1256        {
1257            return m_dependentRequirers;
1258        }
1259    
1260        public synchronized void addDependentRequirer(IModule module)
1261        {
1262            m_dependentRequirers = addDependent(m_dependentRequirers, module);
1263        }
1264    
1265        public synchronized void removeDependentRequirer(IModule module)
1266        {
1267            m_dependentRequirers = removeDependent(m_dependentRequirers, module);
1268        }
1269    
1270        public synchronized IModule[] getDependents()
1271        {
1272            IModule[] dependents = new IModule[
1273                m_dependentHosts.length + m_dependentImporters.length + m_dependentRequirers.length];
1274            System.arraycopy(
1275                m_dependentHosts,
1276                0,
1277                dependents,
1278                0,
1279                m_dependentHosts.length);
1280            System.arraycopy(
1281                m_dependentImporters,
1282                0,
1283                dependents,
1284                m_dependentHosts.length,
1285                m_dependentImporters.length);
1286            System.arraycopy(
1287                m_dependentRequirers,
1288                0,
1289                dependents,
1290                m_dependentHosts.length + m_dependentImporters.length,
1291                m_dependentRequirers.length);
1292            return dependents;
1293        }
1294    
1295        private static IModule[] addDependent(IModule[] modules, IModule module)
1296        {
1297            // Make sure the dependent module is not already present.
1298            for (int i = 0; i < modules.length; i++)
1299            {
1300                if (modules[i].equals(module))
1301                {
1302                    return modules;
1303                }
1304            }
1305            IModule[] tmp = new IModule[modules.length + 1];
1306            System.arraycopy(modules, 0, tmp, 0, modules.length);
1307            tmp[modules.length] = module;
1308            return tmp;
1309        }
1310    
1311        private static IModule[] removeDependent(IModule[] modules, IModule module)
1312        {
1313            IModule[] tmp = modules;
1314    
1315            // Make sure the dependent module is present.
1316            for (int i = 0; i < modules.length; i++)
1317            {
1318                if (modules[i].equals(module))
1319                {
1320                    // If this is the module, then point to empty list.
1321                    if ((modules.length - 1) == 0)
1322                    {
1323                        tmp = new IModule[0];
1324                    }
1325                    // Otherwise, we need to do some array copying.
1326                    else
1327                    {
1328                        tmp = new IModule[modules.length - 1];
1329                        System.arraycopy(modules, 0, tmp, 0, i);
1330                        if (i < tmp.length)
1331                        {
1332                            System.arraycopy(modules, i + 1, tmp, i, tmp.length - i);
1333                        }
1334                    }
1335                    break;
1336                }
1337            }
1338    
1339            return tmp;
1340        }
1341    
1342        public synchronized void close()
1343        {
1344            m_content.close();
1345            for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
1346            {
1347                m_contentPath[i].close();
1348            }
1349            for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
1350            {
1351                m_fragmentContents[i].close();
1352            }
1353            m_classLoader = null;
1354        }
1355    
1356        public synchronized void setSecurityContext(Object securityContext)
1357        {
1358            m_protectionDomain = (ProtectionDomain) securityContext;
1359        }
1360    
1361        public synchronized Object getSecurityContext()
1362        {
1363            return m_protectionDomain;
1364        }
1365    
1366        public String toString()
1367        {
1368            return m_id;
1369        }
1370    
1371        private synchronized ModuleClassLoader getClassLoader()
1372        {
1373            if (m_classLoader == null)
1374            {
1375                if (System.getSecurityManager() != null)
1376                {
1377                    try
1378                    {
1379                        Constructor ctor = (Constructor) m_secureAction.getConstructor(
1380                            ModuleClassLoader.class, new Class[] { ModuleImpl.class, ClassLoader.class });
1381                        m_classLoader = (ModuleClassLoader)
1382                            m_secureAction.invoke(ctor, new Object[] { this, determineParentClassLoader() });
1383                    }
1384                    catch (Exception ex)
1385                    {
1386                        throw new RuntimeException("Unable to create module class loader: "
1387                            + ex.getMessage() + " [" + ex.getClass().getName() + "]");
1388                    }
1389                }
1390                else
1391                {
1392                    m_classLoader = new ModuleClassLoader(determineParentClassLoader());
1393                }
1394            }
1395            return m_classLoader;
1396        }
1397    
1398        private ClassLoader determineParentClassLoader()
1399        {
1400            // Determine the class loader's parent based on the
1401            // configuration property; use boot class loader by
1402            // default.
1403            String cfg = (String) m_configMap.get(Constants.FRAMEWORK_BUNDLE_PARENT);
1404            cfg = (cfg == null) ? Constants.FRAMEWORK_BUNDLE_PARENT_BOOT : cfg;
1405            final ClassLoader parent;
1406            if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_APP))
1407            {
1408                parent = m_secureAction.getSystemClassLoader();
1409            }
1410            else if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_EXT))
1411            {
1412                parent = m_secureAction.getSystemClassLoader().getParent();
1413            }
1414            else if (cfg.equalsIgnoreCase(Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK))
1415            {
1416                parent = ModuleImpl.class.getClassLoader();
1417            }
1418            // On Android we cannot set the parent class loader to be null, so
1419            // we special case that situation here and set it to the system
1420            // class loader by default instead, which is not really spec.
1421            else if (m_bootClassLoader == null)
1422            {
1423                parent = m_secureAction.getSystemClassLoader();
1424            }
1425            else
1426            {
1427                parent = null;
1428            }
1429            return parent;
1430        }
1431    
1432        private Object searchImports(String name, boolean isClass)
1433            throws ClassNotFoundException, ResourceNotFoundException
1434        {
1435            // We delegate to the module's wires to find the class or resource.
1436            IWire[] wires = getWires();
1437            for (int i = 0; (wires != null) && (i < wires.length); i++)
1438            {
1439                // If we find the class or resource, then return it.
1440                Object result = (isClass)
1441                    ? (Object) wires[i].getClass(name)
1442                    : (Object) wires[i].getResource(name);
1443                if (result != null)
1444                {
1445                    return result;
1446                }
1447            }
1448    
1449            return null;
1450        }
1451    
1452        private Object searchDynamicImports(
1453            String name, String pkgName, boolean isClass)
1454            throws ClassNotFoundException, ResourceNotFoundException
1455        {
1456            // At this point, the module's imports were searched and so was the
1457            // the module's content. Now we make an attempt to load the
1458            // class/resource via a dynamic import, if possible.
1459            IWire wire = null;
1460            try
1461            {
1462                wire = m_resolver.resolveDynamicImport(this, pkgName);
1463            }
1464            catch (ResolveException ex)
1465            {
1466                // Ignore this since it is likely normal.
1467            }
1468    
1469            // If the dynamic import was successful, then this initial
1470            // time we must directly return the result from dynamically
1471            // created wire, but subsequent requests for classes/resources
1472            // in the associated package will be processed as part of
1473            // normal static imports.
1474            if (wire != null)
1475            {
1476                // Return the class or resource.
1477                return (isClass)
1478                    ? (Object) wire.getClass(name)
1479                    : (Object) wire.getResource(name);
1480            }
1481    
1482            // If implicit boot delegation is enabled, then try to guess whether
1483            // we should boot delegate.
1484            if (m_implicitBootDelegation)
1485            {
1486                // At this point, the class/resource could not be found by the bundle's
1487                // static or dynamic imports, nor its own content. Before we throw
1488                // an exception, we will try to determine if the instigator of the
1489                // class/resource load was a class from a bundle or not. This is necessary
1490                // because the specification mandates that classes on the class path
1491                // should be hidden (except for java.*), but it does allow for these
1492                // classes/resources to be exposed by the system bundle as an export.
1493                // However, in some situations classes on the class path make the faulty
1494                // assumption that they can access everything on the class path from
1495                // every other class loader that they come in contact with. This is
1496                // not true if the class loader in question is from a bundle. Thus,
1497                // this code tries to detect that situation. If the class instigating
1498                // the load request was NOT from a bundle, then we will make the
1499                // assumption that the caller actually wanted to use the parent class
1500                // loader and we will delegate to it. If the class was
1501                // from a bundle, then we will enforce strict class loading rules
1502                // for the bundle and throw an exception.
1503    
1504                // Get the class context to see the classes on the stack.
1505                Class[] classes = m_sm.getClassContext();
1506                // Start from 1 to skip security manager class.
1507                for (int i = 1; i < classes.length; i++)
1508                {
1509                    // Find the first class on the call stack that is not from
1510                    // the class loader that loaded the Felix classes or is not
1511                    // a class loader or class itself, because we want to ignore
1512                    // calls to ClassLoader.loadClass() and Class.forName() since
1513                    // we are trying to find out who instigated the class load.
1514                    // Also ignore inner classes of class loaders, since we can
1515                    // assume they are a class loader too.
1516    
1517    // TODO: FRAMEWORK - This check is a hack and we should see if we can think
1518    // of another way to do it, since it won't necessarily work in all situations.
1519                    // Since Felix uses threads for changing the start level
1520                    // and refreshing packages, it is possible that there is no
1521                    // module classes on the call stack; therefore, as soon as we
1522                    // see Thread on the call stack we exit this loop. Other cases
1523                    // where modules actually use threads are not an issue because
1524                    // the module classes will be on the call stack before the
1525                    // Thread class.
1526                    if (Thread.class.equals(classes[i]))
1527                    {
1528                        break;
1529                    }
1530                    else if (isClassNotLoadedFromBundle(classes[i]))
1531                    {
1532                        // If the instigating class was not from a bundle,
1533                        // then delegate to the parent class loader; otherwise,
1534                        // break out of loop and return null.
1535                        boolean delegate = true;
1536                        ClassLoader last = null;
1537                        for (ClassLoader cl = classes[i].getClassLoader(); (cl != null) && (last != cl); cl = cl.getClass().getClassLoader())
1538                        {
1539                            last = cl;
1540                            if (ModuleClassLoader.class.isInstance(cl))
1541                            {
1542                                delegate = false;
1543                                break;
1544                            }
1545                        }
1546                        // Delegate to the parent class loader unless this call
1547                        // is due to outside code calling a method on the bundle
1548                        // interface (e.g., Bundle.loadClass()).
1549                        if (delegate && !Bundle.class.isAssignableFrom(classes[i - 1]))
1550                        {
1551                            try
1552                            {
1553                                // Return the class or resource from the parent class loader.
1554                                return (isClass)
1555                                    ? (Object) this.getClass().getClassLoader().loadClass(name)
1556                                    : (Object) this.getClass().getClassLoader().getResource(name);
1557                            }
1558                            catch (NoClassDefFoundError ex)
1559                            {
1560                                // Ignore, will return null
1561                            }
1562                        }
1563                        break;
1564                    }
1565                }
1566            }
1567    
1568            return null;
1569        }
1570    
1571        private boolean isClassNotLoadedFromBundle(Class clazz)
1572        {
1573            // If this is an inner class, try to get the enclosing class
1574            // because we can assume that inner classes of class loaders
1575            // are really just the class loader and we should ignore them.
1576            clazz = getEnclosingClass(clazz);
1577            return (this.getClass().getClassLoader() != clazz.getClassLoader())
1578                && !ClassLoader.class.isAssignableFrom(clazz)
1579                && !Class.class.equals(clazz)
1580                && !Proxy.class.equals(clazz);
1581        }
1582    
1583        private static Class getEnclosingClass(Class clazz)
1584        {
1585            // This code determines if the class is an inner class and if so
1586            // returns the enclosing class. At one point in time this code used
1587            // Class.getEnclosingClass() for JDKs > 1.5, but due to a bug in the
1588            // JDK which caused  invalid ClassCircularityErrors we had to remove it.
1589            int idx = clazz.getName().lastIndexOf('$');
1590            if (idx > 0)
1591            {
1592                ClassLoader cl = (clazz.getClassLoader() != null)
1593                    ? clazz.getClassLoader() : ClassLoader.getSystemClassLoader();
1594                try
1595                {
1596                    Class enclosing = cl.loadClass(clazz.getName().substring(0, idx));
1597                    clazz = (enclosing != null) ? enclosing : clazz;
1598                }
1599                catch (Throwable t)
1600                {
1601                    // Ignore all problems since we are trying to load a class
1602                    // inside the class loader and this can lead to
1603                    // ClassCircularityError, for example.
1604                }
1605            }
1606    
1607            return clazz;
1608        }
1609    
1610        private boolean shouldBootDelegate(String pkgName)
1611        {
1612            // Always boot delegate if the bundle has a configured
1613            // boot class loader.
1614            if (m_bootClassLoader != m_defBootClassLoader)
1615            {
1616                return true;
1617            }
1618    
1619            boolean result = false;
1620    
1621            // Only consider delegation if we have a package name, since
1622            // we don't want to promote the default package. The spec does
1623            // not take a stand on this issue.
1624            if (pkgName.length() > 0)
1625            {
1626                for (int i = 0; !result && (i < m_bootPkgs.length); i++)
1627                {
1628                    // Check if the boot package is wildcarded.
1629                    // A wildcarded boot package will be in the form "foo.",
1630                    // so a matching subpackage will start with "foo.", e.g.,
1631                    // "foo.bar".
1632                    if (m_bootPkgWildcards[i] && pkgName.startsWith(m_bootPkgs[i]))
1633                    {
1634                        return true;
1635                    }
1636                    // If not wildcarded, then check for an exact match.
1637                    else if (m_bootPkgs[i].equals(pkgName))
1638                    {
1639                        return true;
1640                    }
1641                }
1642            }
1643    
1644            return result;
1645        }
1646    
1647        private static final Constructor m_dexFileClassConstructor;
1648        private static final Method m_dexFileClassLoadDex;
1649        private static final Method m_dexFileClassLoadClass;
1650    
1651        static
1652        {
1653            Constructor dexFileClassConstructor = null;
1654            Method dexFileClassLoadDex = null;
1655            Method dexFileClassLoadClass = null;
1656            try
1657            {
1658                Class dexFileClass;
1659                try
1660                {
1661                    dexFileClass = Class.forName("dalvik.system.DexFile");
1662                }
1663                catch (Exception ex)
1664                {
1665                    dexFileClass = Class.forName("android.dalvik.DexFile");
1666                }
1667    
1668                try
1669                {
1670                    dexFileClassLoadDex = dexFileClass.getMethod("loadDex", 
1671                        new Class[]{String.class, String.class, Integer.TYPE});
1672                }
1673                catch (Exception ex)
1674                {
1675                    // Nothing we need to do 
1676                }
1677                dexFileClassConstructor = dexFileClass.getConstructor(
1678                    new Class[] { java.io.File.class });
1679                dexFileClassLoadClass = dexFileClass.getMethod("loadClass",
1680                    new Class[] { String.class, ClassLoader.class });
1681            }
1682            catch (Throwable ex)
1683            {
1684               dexFileClassConstructor = null;
1685               dexFileClassLoadDex = null;
1686               dexFileClassLoadClass = null;
1687            }
1688            m_dexFileClassConstructor = dexFileClassConstructor;
1689            m_dexFileClassLoadDex= dexFileClassLoadDex;
1690            m_dexFileClassLoadClass = dexFileClassLoadClass;
1691        }
1692    
1693        public class ModuleClassLoader extends SecureClassLoader implements BundleReference
1694        {
1695            private final Map m_jarContentToDexFile;
1696            private Object[][] m_cachedLibs = new Object[0][];
1697            private static final int LIBNAME_IDX = 0;
1698            private static final int LIBPATH_IDX = 1;
1699    
1700            public ModuleClassLoader(ClassLoader parent)
1701            {
1702                super(parent);
1703                if (m_dexFileClassLoadClass != null)
1704                {
1705                    m_jarContentToDexFile = new HashMap();
1706                }
1707                else
1708                {
1709                    m_jarContentToDexFile = null;
1710                }
1711            }
1712    
1713            public Bundle getBundle()
1714            {
1715                return ModuleImpl.this.getBundle();
1716            }
1717    
1718            protected Class loadClass(String name, boolean resolve)
1719                throws ClassNotFoundException
1720            {
1721                Class clazz = null;
1722    
1723                // Make sure the class was not already loaded.
1724                synchronized (this)
1725                {
1726                    clazz = findLoadedClass(name);
1727                }
1728    
1729                if (clazz == null)
1730                {
1731                    try
1732                    {
1733                        clazz = (Class) findClassOrResourceByDelegation(name, true);
1734                    }
1735                    catch (ResourceNotFoundException ex)
1736                    {
1737                        // This should never happen since we are asking for a class,
1738                        // so just ignore it.
1739                    }
1740                    catch (ClassNotFoundException cnfe)
1741                    {
1742                        ClassNotFoundException ex = cnfe;
1743                        String msg = name;
1744                        if (m_logger.getLogLevel() >= Logger.LOG_DEBUG)
1745                        {
1746                            msg = diagnoseClassLoadError(m_resolver, ModuleImpl.this, name);
1747                            ex = (msg != null)
1748                                ? new ClassNotFoundException(msg, cnfe)
1749                                : ex;
1750                        }
1751                        throw ex;
1752                    }
1753                }
1754    
1755                // Resolve the class and return it.
1756                if (resolve)
1757                {
1758                    resolveClass(clazz);
1759                }
1760                return clazz;
1761            }
1762    
1763            protected Class findClass(String name) throws ClassNotFoundException
1764            {
1765                Class clazz = null;
1766    
1767                // Search for class in module.
1768                if (clazz == null)
1769                {
1770                    String actual = name.replace('.', '/') + ".class";
1771    
1772                    byte[] bytes = null;
1773    
1774                    // Check the module class path.
1775                    IContent[] contentPath = getContentPath();
1776                    IContent content = null;
1777                    for (int i = 0;
1778                        (bytes == null) &&
1779                        (i < contentPath.length); i++)
1780                    {
1781                        bytes = contentPath[i].getEntryAsBytes(actual);
1782                        content = contentPath[i];
1783                    }
1784    
1785                    if (bytes != null)
1786                    {
1787                        // Get package name.
1788                        String pkgName = Util.getClassPackage(name);
1789    
1790                        // Before we actually attempt to define the class, grab
1791                        // the lock for this class loader and make sure than no
1792                        // other thread has defined this class in the meantime.
1793                        synchronized (this)
1794                        {
1795                            clazz = findLoadedClass(name);
1796    
1797                            if (clazz == null)
1798                            {
1799                                int activationPolicy = 
1800                                    ((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
1801                                    ? ((BundleImpl) getBundle()).getCurrentModule().getDeclaredActivationPolicy()
1802                                    : IModule.EAGER_ACTIVATION;
1803    
1804                                // If the module is using deferred activation, then if
1805                                // we load this class from this module we need to activate
1806                                // the module before returning the class. We will short
1807                                // circuit the trigger matching if the trigger is already
1808                                // tripped.
1809                                boolean isTriggerClass = m_isActivationTriggered
1810                                    ? false : isActivationTrigger(pkgName);
1811                                if (!m_isActivationTriggered
1812                                    && isTriggerClass
1813                                    && (activationPolicy == IModule.LAZY_ACTIVATION)
1814                                    && (getBundle().getState() == Bundle.STARTING))
1815                                {
1816                                    List deferredList = (List) m_deferredActivation.get();
1817                                    if (deferredList == null)
1818                                    {
1819                                        deferredList = new ArrayList();
1820                                        m_deferredActivation.set(deferredList);
1821                                    }
1822                                    deferredList.add(new Object[] { name, getBundle() });
1823                                }
1824                                // We need to try to define a Package object for the class
1825                                // before we call defineClass() if we haven't already
1826                                // created it.
1827                                if (pkgName.length() > 0)
1828                                {
1829                                    if (getPackage(pkgName) == null)
1830                                    {
1831                                        Object[] params = definePackage(pkgName);
1832                                        if (params != null)
1833                                        {
1834                                            definePackage(
1835                                                pkgName,
1836                                                (String) params[0],
1837                                                (String) params[1],
1838                                                (String) params[2],
1839                                                (String) params[3],
1840                                                (String) params[4],
1841                                                (String) params[5],
1842                                                null);
1843                                        }
1844                                        else
1845                                        {
1846                                            definePackage(pkgName, null, null,
1847                                                null, null, null, null, null);
1848                                        }
1849                                    }
1850                                }
1851    
1852                                // If we can load the class from a dex file do so
1853                                if (content instanceof JarContent)
1854                                {
1855                                    try
1856                                    {
1857                                        clazz = getDexFileClass((JarContent) content, name, this);
1858                                    }
1859                                    catch (Exception ex)
1860                                    {
1861                                        // Looks like we can't
1862                                    }
1863                                }
1864    
1865                                if (clazz == null)
1866                                {
1867                                    // If we have a security context, then use it to
1868                                    // define the class with it for security purposes,
1869                                    // otherwise define the class without a protection domain.
1870                                    if (m_protectionDomain != null)
1871                                    {
1872                                        clazz = defineClass(name, bytes, 0, bytes.length,
1873                                            m_protectionDomain);
1874                                    }
1875                                    else
1876                                    {
1877                                        clazz = defineClass(name, bytes, 0, bytes.length);
1878                                    }
1879                                }
1880    
1881                                // At this point if we have a trigger class, then the deferred
1882                                // activation trigger has tripped.
1883                                if (!m_isActivationTriggered && isTriggerClass && (clazz != null))
1884                                {
1885                                    m_isActivationTriggered = true;
1886                                }
1887                            }
1888                        }
1889    
1890                        // Perform deferred activation without holding the class loader lock,
1891                        // if the class we are returning is the instigating class.
1892                        List deferredList = (List) m_deferredActivation.get();
1893                        if ((deferredList != null)
1894                            && (deferredList.size() > 0)
1895                            && ((Object[]) deferredList.get(0))[0].equals(name))
1896                        {
1897                            for (int i = deferredList.size() - 1; i >= 0; i--)
1898                            {
1899                                try
1900                                {
1901                                    ((BundleImpl) ((Object[]) deferredList.get(i))[1]).getFramework().activateBundle(
1902                                        (BundleImpl) ((Object[]) deferredList.get(i))[1], true);
1903                                }
1904                                catch (BundleException ex)
1905                                {
1906                                    ex.printStackTrace();
1907                                }
1908                            }
1909                            deferredList.clear();
1910                        }
1911                    }
1912                }
1913    
1914                return clazz;
1915            }
1916    
1917            private Object[] definePackage(String pkgName)
1918            {
1919                String spectitle = (String) m_headerMap.get("Specification-Title");
1920                String specversion = (String) m_headerMap.get("Specification-Version");
1921                String specvendor = (String) m_headerMap.get("Specification-Vendor");
1922                String impltitle = (String) m_headerMap.get("Implementation-Title");
1923                String implversion = (String) m_headerMap.get("Implementation-Version");
1924                String implvendor = (String) m_headerMap.get("Implementation-Vendor");
1925                if ((spectitle != null)
1926                    || (specversion != null)
1927                    || (specvendor != null)
1928                    || (impltitle != null)
1929                    || (implversion != null)
1930                    || (implvendor != null))
1931                {
1932                    return new Object[] {
1933                        spectitle, specversion, specvendor, impltitle, implversion, implvendor
1934                    };
1935                }
1936                return null;
1937            }
1938    
1939            private Class getDexFileClass(JarContent content, String name, ClassLoader loader)
1940                throws Exception
1941            {
1942                if (m_jarContentToDexFile == null)
1943                {
1944                    return null;
1945                }
1946    
1947                Object dexFile = null;
1948    
1949                if (!m_jarContentToDexFile.containsKey(content))
1950                {
1951                    try
1952                    {
1953                        if (m_dexFileClassLoadDex != null)
1954                        {
1955                            dexFile = m_dexFileClassLoadDex.invoke(null, 
1956                                new Object[]{content.getFile().getAbsolutePath(), 
1957                                    content.getFile().getAbsolutePath() + ".dex", new Integer(0)});
1958                        }
1959                        else
1960                        {
1961                            dexFile = m_dexFileClassConstructor.newInstance(
1962                                new Object[] { content.getFile() });
1963                        }
1964                    }
1965                    finally
1966                    {
1967                        m_jarContentToDexFile.put(content, dexFile);
1968                    }
1969                }
1970                else
1971                {
1972                    dexFile = m_jarContentToDexFile.get(content);
1973                }
1974    
1975                if (dexFile != null)
1976                {
1977                    return (Class) m_dexFileClassLoadClass.invoke(dexFile,
1978                        new Object[] { name.replace('.','/'), loader });
1979                }
1980                return null;
1981            }
1982    
1983            public URL getResource(String name)
1984            {
1985                return ModuleImpl.this.getResourceByDelegation(name);
1986            }
1987    
1988            protected URL findResource(String name)
1989            {
1990                return getResourceLocal(name);
1991            }
1992    
1993            // The findResources() method should only look at the module itself, but
1994            // instead it tries to delegate because in Java version prior to 1.5 the
1995            // getResources() method was final and could not be overridden. We should
1996            // override getResources() like getResource() to make it delegate, but we
1997            // can't. As a workaround, we make findResources() delegate instead.
1998            protected Enumeration findResources(String name)
1999            {
2000                return getResourcesByDelegation(name);
2001            }
2002    
2003            protected String findLibrary(String name)
2004            {
2005                // Remove leading slash, if present.
2006                if (name.startsWith("/"))
2007                {
2008                    name = name.substring(1);
2009                }
2010    
2011                String result = null;
2012                // CONCURRENCY: In the long run, we might want to break this
2013                // sync block in two to avoid manipulating the cache while
2014                // holding the lock, but for now we will do it the simple way.
2015                synchronized (this)
2016                {
2017                    // Check to make sure we haven't already found this library.
2018                    for (int i = 0; (result == null) && (i < m_cachedLibs.length); i++)
2019                    {
2020                        if (m_cachedLibs[i][LIBNAME_IDX].equals(name))
2021                        {
2022                            result = (String) m_cachedLibs[i][LIBPATH_IDX];
2023                        }
2024                    }
2025    
2026                    // If we don't have a cached result, see if we have a matching
2027                    // native library.
2028                    if (result == null)
2029                    {
2030                        R4Library[] libs = getNativeLibraries();
2031                        for (int libIdx = 0; (libs != null) && (libIdx < libs.length); libIdx++)
2032                        {
2033                            if (libs[libIdx].match(m_configMap, name))
2034                            {
2035                                // Search bundle content first for native library.
2036                                result = getContent().getEntryAsNativeLibrary(
2037                                    libs[libIdx].getEntryName());
2038                                // If not found, then search fragments in order.
2039                                for (int i = 0;
2040                                    (result == null) && (m_fragmentContents != null)
2041                                        && (i < m_fragmentContents.length);
2042                                    i++)
2043                                {
2044                                    result = m_fragmentContents[i].getEntryAsNativeLibrary(
2045                                        libs[libIdx].getEntryName());
2046                                }
2047                            }
2048                        }
2049    
2050                        // Remember the result for future requests.
2051                        if (result != null)
2052                        {
2053                            Object[][] tmp = new Object[m_cachedLibs.length + 1][];
2054                            System.arraycopy(m_cachedLibs, 0, tmp, 0, m_cachedLibs.length);
2055                            tmp[m_cachedLibs.length] = new Object[] { name, result };
2056                            m_cachedLibs = tmp;
2057                        }
2058                    }
2059                }
2060    
2061                return result;
2062            }
2063    
2064            public String toString()
2065            {
2066                return ModuleImpl.this.toString();
2067            }
2068        }
2069    
2070        private static String diagnoseClassLoadError(
2071            FelixResolver resolver, ModuleImpl module, String name)
2072        {
2073            // We will try to do some diagnostics here to help the developer
2074            // deal with this exception.
2075    
2076            // Get package name.
2077            String pkgName = Util.getClassPackage(name);
2078            if (pkgName.length() == 0)
2079            {
2080                return null;
2081            }
2082    
2083            // First, get the bundle string of the module doing the class loader.
2084            String importer = module.getBundle().toString();
2085    
2086            // Next, check to see if the module imports the package.
2087            IWire[] wires = module.getWires();
2088            for (int i = 0; (wires != null) && (i < wires.length); i++)
2089            {
2090                if (wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
2091                    wires[i].getCapability().getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
2092                {
2093                    String exporter = wires[i].getExporter().getBundle().toString();
2094    
2095                    StringBuffer sb = new StringBuffer("*** Package '");
2096                    sb.append(pkgName);
2097                    sb.append("' is imported by bundle ");
2098                    sb.append(importer);
2099                    sb.append(" from bundle ");
2100                    sb.append(exporter);
2101                    sb.append(", but the exported package from bundle ");
2102                    sb.append(exporter);
2103                    sb.append(" does not contain the requested class '");
2104                    sb.append(name);
2105                    sb.append("'. Please verify that the class name is correct in the importing bundle ");
2106                    sb.append(importer);
2107                    sb.append(" and/or that the exported package is correctly bundled in ");
2108                    sb.append(exporter);
2109                    sb.append(". ***");
2110    
2111                    return sb.toString();
2112                }
2113            }
2114    
2115            // Next, check to see if the package was optionally imported and
2116            // whether or not there is an exporter available.
2117            IRequirement[] reqs = module.getRequirements();
2118    /*
2119    * TODO: RB - Fix diagnostic message for optional imports.
2120            for (int i = 0; (reqs != null) && (i < reqs.length); i++)
2121            {
2122                if (reqs[i].getName().equals(pkgName) && reqs[i].isOptional())
2123                {
2124                    // Try to see if there is an exporter available.
2125                    IModule[] exporters = getResolvedExporters(reqs[i], true);
2126                    exporters = (exporters.length == 0)
2127                        ? getUnresolvedExporters(reqs[i], true) : exporters;
2128    
2129                    // An exporter might be available, but it may have attributes
2130                    // that do not match the importer's required attributes, so
2131                    // check that case by simply looking for an exporter of the
2132                    // desired package without any attributes.
2133                    if (exporters.length == 0)
2134                    {
2135                        IRequirement pkgReq = new Requirement(
2136                            ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")");
2137                        exporters = getResolvedExporters(pkgReq, true);
2138                        exporters = (exporters.length == 0)
2139                            ? getUnresolvedExporters(pkgReq, true) : exporters;
2140                    }
2141    
2142                    long expId = (exporters.length == 0)
2143                        ? -1 : Util.getBundleIdFromModuleId(exporters[0].getId());
2144    
2145                    StringBuffer sb = new StringBuffer("*** Class '");
2146                    sb.append(name);
2147                    sb.append("' was not found, but this is likely normal since package '");
2148                    sb.append(pkgName);
2149                    sb.append("' is optionally imported by bundle ");
2150                    sb.append(impId);
2151                    sb.append(".");
2152                    if (exporters.length > 0)
2153                    {
2154                        sb.append(" However, bundle ");
2155                        sb.append(expId);
2156                        if (reqs[i].isSatisfied(
2157                            Util.getExportPackage(exporters[0], reqs[i].getName())))
2158                        {
2159                            sb.append(" does export this package. Bundle ");
2160                            sb.append(expId);
2161                            sb.append(" must be installed before bundle ");
2162                            sb.append(impId);
2163                            sb.append(" is resolved or else the optional import will be ignored.");
2164                        }
2165                        else
2166                        {
2167                            sb.append(" does export this package with attributes that do not match.");
2168                        }
2169                    }
2170                    sb.append(" ***");
2171    
2172                    return sb.toString();
2173                }
2174            }
2175    */
2176            // Next, check to see if the package is dynamically imported by the module.
2177            IRequirement pkgReq = Resolver.findAllowedDynamicImport(module, pkgName);
2178            if (pkgReq != null)
2179            {
2180                // Try to see if there is an exporter available.
2181                List exports =
2182                    resolver.getResolvedCandidates(pkgReq, module);
2183                exports = (exports.size() == 0)
2184                    ? resolver.getUnresolvedCandidates(pkgReq, module)
2185                    : exports;
2186    
2187                // An exporter might be available, but it may have attributes
2188                // that do not match the importer's required attributes, so
2189                // check that case by simply looking for an exporter of the
2190                // desired package without any attributes.
2191                if (exports.size() == 0)
2192                {
2193                    try
2194                    {
2195                        IRequirement req = new Requirement(
2196                            ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")");
2197                        exports = resolver.getResolvedCandidates(req, module);
2198                        exports = (exports.size() == 0)
2199                            ? resolver.getUnresolvedCandidates(req, module)
2200                            : exports;
2201                    }
2202                    catch (InvalidSyntaxException ex)
2203                    {
2204                        // This should never happen.
2205                    }
2206                }
2207    
2208                String exporter = (exports.size() == 0)
2209                    ? null : ((ICapability) exports.get(0)).getModule().getBundle().toString();
2210    
2211                StringBuffer sb = new StringBuffer("*** Class '");
2212                sb.append(name);
2213                sb.append("' was not found, but this is likely normal since package '");
2214                sb.append(pkgName);
2215                sb.append("' is dynamically imported by bundle ");
2216                sb.append(importer);
2217                sb.append(".");
2218                if (exports.size() > 0)
2219                {
2220                    if (!pkgReq.isSatisfied((ICapability) exports.get(0)))
2221                    {
2222                        sb.append(" However, bundle ");
2223                        sb.append(exporter);
2224                        sb.append(" does export this package with attributes that do not match.");
2225                    }
2226                }
2227                sb.append(" ***");
2228    
2229                return sb.toString();
2230            }
2231    
2232            // Next, check to see if there are any exporters for the package at all.
2233            pkgReq = null;
2234            try
2235            {
2236                pkgReq = new Requirement(ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")");
2237            }
2238            catch (InvalidSyntaxException ex)
2239            {
2240                // This should never happen.
2241            }
2242            List exports =
2243                resolver.getResolvedCandidates(pkgReq, module);
2244            exports = (exports.size() == 0)
2245                ? resolver.getUnresolvedCandidates(pkgReq, module)
2246                : exports;
2247            if (exports.size() > 0)
2248            {
2249                boolean classpath = false;
2250                try
2251                {
2252                    ModuleClassLoader.class.getClassLoader().loadClass(name);
2253                    classpath = true;
2254                }
2255                catch (NoClassDefFoundError err)
2256                {
2257                    // Ignore
2258                }
2259                catch (Exception ex)
2260                {
2261                    // Ignore
2262                }
2263    
2264                String exporter = ((ICapability) exports.get(0)).getModule().getBundle().toString();
2265    
2266                StringBuffer sb = new StringBuffer("*** Class '");
2267                sb.append(name);
2268                sb.append("' was not found because bundle ");
2269                sb.append(importer);
2270                sb.append(" does not import '");
2271                sb.append(pkgName);
2272                sb.append("' even though bundle ");
2273                sb.append(exporter);
2274                sb.append(" does export it.");
2275                if (classpath)
2276                {
2277                    sb.append(" Additionally, the class is also available from the system class loader. There are two fixes: 1) Add an import for '");
2278                    sb.append(pkgName);
2279                    sb.append("' to bundle ");
2280                    sb.append(importer);
2281                    sb.append("; imports are necessary for each class directly touched by bundle code or indirectly touched, such as super classes if their methods are used. ");
2282                    sb.append("2) Add package '");
2283                    sb.append(pkgName);
2284                    sb.append("' to the '");
2285                    sb.append(Constants.FRAMEWORK_BOOTDELEGATION);
2286                    sb.append("' property; a library or VM bug can cause classes to be loaded by the wrong class loader. The first approach is preferable for preserving modularity.");
2287                }
2288                else
2289                {
2290                    sb.append(" To resolve this issue, add an import for '");
2291                    sb.append(pkgName);
2292                    sb.append("' to bundle ");
2293                    sb.append(importer);
2294                    sb.append(".");
2295                }
2296                sb.append(" ***");
2297    
2298                return sb.toString();
2299            }
2300    
2301            // Next, try to see if the class is available from the system
2302            // class loader.
2303            try
2304            {
2305                ModuleClassLoader.class.getClassLoader().loadClass(name);
2306    
2307                StringBuffer sb = new StringBuffer("*** Package '");
2308                sb.append(pkgName);
2309                sb.append("' is not imported by bundle ");
2310                sb.append(importer);
2311                sb.append(", nor is there any bundle that exports package '");
2312                sb.append(pkgName);
2313                sb.append("'. However, the class '");
2314                sb.append(name);
2315                sb.append("' is available from the system class loader. There are two fixes: 1) Add package '");
2316                sb.append(pkgName);
2317                sb.append("' to the '");
2318                sb.append(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
2319                sb.append("' property and modify bundle ");
2320                sb.append(importer);
2321                sb.append(" to import this package; this causes the system bundle to export class path packages. 2) Add package '");
2322                sb.append(pkgName);
2323                sb.append("' to the '");
2324                sb.append(Constants.FRAMEWORK_BOOTDELEGATION);
2325                sb.append("' property; a library or VM bug can cause classes to be loaded by the wrong class loader. The first approach is preferable for preserving modularity.");
2326                sb.append(" ***");
2327    
2328                return sb.toString();
2329            }
2330            catch (Exception ex2)
2331            {
2332            }
2333    
2334            // Finally, if there are no imports or exports for the package
2335            // and it is not available on the system class path, simply
2336            // log a message saying so.
2337            StringBuffer sb = new StringBuffer("*** Class '");
2338            sb.append(name);
2339            sb.append("' was not found. Bundle ");
2340            sb.append(importer);
2341            sb.append(" does not import package '");
2342            sb.append(pkgName);
2343            sb.append("', nor is the package exported by any other bundle or available from the system class loader.");
2344            sb.append(" ***");
2345    
2346            return sb.toString();
2347        }
2348    }