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 }