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 }