`
yangzb
  • 浏览: 3502444 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Context Class Loader Enhancements

    博客分类:
  • Java
阅读更多

http://wiki.eclipse.org/index.php/Context_Class_Loader_Enhancements

 

Contents

[hide ]

<script type="text/javascript"> if (window.showTocToggle) { var tocShowText = "show"; var tocHideText = "hide"; showTocToggle(); } </script>

Overview

This design defines the behavior of the context class loader in the Equinox OSGi Framework. Many java libraries exist that use the context class loader. In an OSGi Framework environment the context class loader is not defined. This leads to class loading issues when trying to package such libraries into bundles. This document describes the solutions offered by the Equinox OSGi Framework to solve some of these issues.

Terminology

Context Class Loader

A context class loader is associated with a Thread. This class loader is provided by the thread creator to code running in the thread for the purpose loading classes and resources. This class loader is useful for loading classes and resources that are not always available to the class loader which loaded the code running in the thread.

Context Switch

For the purpose of this design a Context Switch is defined as the point in an execution sequence when a component boundary is crossed from one component to the next.

Class forName

A classic Java mechanism for dynamic class discovery and loading. It uses the current classloader to look for and load the requested class. The current classloader is the classloader that loaded the class containing the method executing the forName(String) call.

Context Finder

A special class loader that finds that first Bundle classloader on that stack and delegates load requests to that classloader.

Buddy Policy

A policy that allows a bundle to declare it needs help from other bundles to load classes. This type of policy does not cause a hard dependency wire to be used to load the classes from a buddy.

Problem Description

Most OSGi-biased developers will concede that the majority of java libraries out there are not currently shipped as OSGi bundles and are not aware of the Modular Layer in the OSGi Framework. Many existing java libraries are designed to run inside a container (J2EE container, Applet container etc). Such containers explicitly define execution boundaries between the various components running within the container. The container controls the execution boundaries and knows when a boundary is being crossed from one component to the next.

This level of boundary control allows a container to switch the context of a thread when a component boundary is crossed. Typically when a container detects a context switch it will set the context class loader on the thread to a class loader associated with the component which is being entered. When the component is exited then the container will switch the context class loader back to the previous context class loader.

The OSGi Framework specification does not define what the context class loader should be set to and does not define when it should be switched. Part of the problem is the Framework is not always aware of when a component boundary is crossed. For some operations the Framework is aware of component boundaries, for example, when calling out to BundleActivators and event listeners. Switching the context class loader when calling out to these types of objects will not solve a large number of usecases.

Consider the following example:

  • Bundle W exports a package “some.foo.library”. This package contains code which uses the context class loader to load additional classes. Imagine it has some SPI like behavior where it loads classes based on some properties file similar to how javax.xml.parsers package works.
  • Bundle X exports a package “some.foo.stuff”. This package contains a service interface “some.foo.stuff.BarService”.
  • Bundle Y imports the “some.foo.stuff” package and registers a “BarService” implementation with the service registry.
  • Bundle Y also imports the “some.foo.library” and expects the exporter to be able to load classes from bundle Y. Bundle Y uses the “some.foo.library” package in its implementation of the “BarService”.
  • Bundle Z imports the “some.foo.stuff” package and gets the “BarService” from the service registry and uses it.

In this scenario imagine the framework sets the context class loader to Bundle Z’s class loader before calling its BundleActivator to start the bundle. In the BundleActivator of Z it gets the BarService if it is available and start making calls to it. At this point code in Bundle Y will get executed because it contains the implementation of the BarService. The implementation of the BarService then uses the package “some.foo.library”. This will cause the code in Bundle W to run that uses the context class loader. At this point the context class loader will be set to Bundle Z’s class loader. This class loader does not have access to the content in Bundle Y and will result in Bundle W’s library code not being able to load classes from Bundle Y.

As a work around to this issue Bundle Y could wrap all calls to code in the package “some.foo.library” with context class loader switches, but this puts a great burden on any developer using the package “some.foo.library”. This also calls into question the purpose of the Framework switching the context class loader at all (to Bundle Z’s class loader in the example above). Any time more than one component boundary is crossed the original context class loader will likely not be the one that is needed.

This design specifies a behavior for the context class loader that should help solve some of these issues.

Requirements

  1. The solution MUST reduce or illiminate the need for code packaged in a bundle to call Thread.setContextClassLoader while calling library code
  2. Library bundles MUST be able to use the context class loader to access classes needed by the library
  3. other reqs ...

Technical Solution

To solve the requirements this design introduces the buddy class loading and context finder concepts


Buddy Class Loading

Buddy class loading offers an integration strategy that does not require modifying the Java code of existing libraries that use the Class.forName(String) approach to dynamically discover and load classes. The mechanism works as follows:

  • A Bundle declares that it needs the help of other bundles to load classes
  • The Bundle identifies the kind of help they need by specifying a buddy policy. The policy defines what kind of bundles will be considered to be buddies.
  • When a bundle class loader fails to find a desired class through the normal OSGi delegation model (i.e. Import-Package, Require-Bundle, and local classpath), its buddy policy is invoked
  • The invoked buddy policy discovers a set of buddies and consults each one in turn until either the class is found or the list is exhausted.

Search Order

The following is a modified class/resource search order:

Frameworks must adhere to the following rules for class or resource loading. When a bundle’s class loader is requested to load a class or find a resource, the search must be performed in the following order:

  1. If the class or resource is in a java.* package, the request is delegated to the parent class loader; otherwise, the search continues with the next step. If the request is delegated to the parent class loader and the class or resource is not found, then the search terminates and the request fails.
  2. If the class or resource is from a package included in the boot delegation list (org.osgi.framework.bootdelegation), then the request is delegated to the parent class loader. If the class or resource is found there, the search ends.
  3. If the class or resource is in a package that is imported using Import-Package or was imported dynamically in a previous load, then the request is delegated to the exporting bundle’s class loader; otherwise the search continues with the next step. If the request is delegated to an exporting class loader and the class or resource is not found, then the search terminates and the request fails.
  4. If the class or resource is in a package that is imported from one or more other bundles using Require-Bundle, the request is delegated to the class loaders of the other bundles, in the order in which they are specified in this bundle’s manifest. If the class or resource is not found, then the search continues with the next step.
  5. The bundle’s own internal bundle class path is searched. If the class or resource is not found, then the search continues with the next step.
  6. Each attached fragment’s internal bundle class path is searched. The fragments are searched in ascending bundle ID order. If the class or resource is not found, then the search continues with the next step.
  7. If the class or resource is in a package that is exported by the bundle or the package is imported by the bundle (using Import-Package or Require-Bundle), then the search ends and the class or resource is not found.
  8. Otherwise, if the class or resource is in a package that is imported using DynamicImport-Package, then a dynamic import of the package is now attempted. An exporter must conform to any implied package constraints. If an appropriate exporter is found, a wire is established so that future loads of the package are handled in Step 3. If a dynamic wire is not established, then the search continues to step 10.
  9. If the dynamic import of the package is established, the request is delegated to the exporting bundle’s class loader. If the request is delegated to an exporting class loader and the class or resource is not found, then the search terminates and the request fails.
  10. If the bundle has declared a buddy policy then the request is delegated to each buddy bundle class loader until either the class or resource is found or all of the buddy bundle class loaders have been delegated too.

When delegating to another bundle class loader, the delegated request enters this algorithm at step 4 and exits at step 9. The buddy policy is not used when delegating to another bundle class loader.

Buddy Policy

A buddy policy defines a policy for selecting buddies that will be used to for buddy class loading. Currently Equinox defines a number of built-in Buddy policies. The following are the buddy policies available in Equinox

  • dependent - Consults all bundles that directly or indirectly depend on the bundle. An indirect dependency can be introduced by bundles that use Require-Bundle with the visibility directive set to reexport. Note that this casts a rather wide net and may introduce performance problems as the number of bundles increase.
  • registered - Consults all dependent bundles that explicitly register themselves as buddies to the bundle. This is similar to the dependent policy but scopes down the number of bundles that will be consulted as buddies.
  • global - Consults the available packages exported in the global pool of exported packages.
  • app - Consults the application classloader.
  • ext - Consults the extension classloader.
  • boot - Consults the boot classloader.

Note when using the above policies all packages available from the source buddy will be available. In the case of dependent , and registered , all packages from the bundle are available even if the package is not exported. There is no way to scope down the available packages from the buddy sources.

Eclipse-BuddyPolicy Header

The Eclipse-BuddyPolicy header allows a bundle to declare a comma-separated list of buddy loading policy names that are to be used for the bundle.

The syntax of the Eclipse-BuddyPolicy header is the following

Eclipse-BuddyPolicy ::= buddy-policy ( ',' buddy-policy )*
 buddy-policy ::= ('dependent' | 'registered' | 'global' | 'app' | 'ext' | 'boot')

 

Eclipse-RegisterBuddy

The Eclipse-RegisterBuddy header is used to declare a comma-separated list of symbolic names of bundles that this bundle should be a registered buddy to. The bundles with the specified symbolic names must use the registered buddy policy in order for this bundle to be a registered buddy.

Note that the following conditions must be met before a bundle X can become a registered buddy of another bundle Y :

  1. The bundle Y must specify the registered buddy policy (i.e. Eclipse-BuddyPolicy: registered)
  2. The bundle X must specify the symbolic name of Y in the Eclipse-RegisterBuddy header (i.e Eclipse-RegisterBuddy: Y)
  3. The bundle X must be dependent on a package exported by bundle Y . This can happen through either a Require-Bundle or Import-Package constraint.

The syntax of the Eclipse-RegisterBuddy header is the following:

Eclipse-RegisterBuddy ::= bundle-symbolic-name ( ',' bundle-symbolic-name )*

Context Class Loader

Since Java 1.2, the Class.forName(String) mechanism has been largely superseded by context classloading. As such, most modern class libraries use a context classloader. This section descibes how the use of a context class loader can be transparently converted into something equivalent to Class.forName(String). Doing this allows the buddy loading mechanism described above (and DynamicImport-Package) to be used to eliminate ClassNotFoundExceptions and NoClassDefFoundErrors.

Each Java Thread has an associated context classloader field that contains a classloader. The classloader in this field is set, typically by the application container, to match the context of this current execution. That is, the field contains a classloader that has access to the classes related to the current execution (e.g., Web request being processed). Libraries such as log4j access and use the context classloader with the updated AppenderHelper code pattern below:

public class AppenderHelper {
  private Appender createAppender(String appenderName) {
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    Class appenderClass = loader.loadClass(appenderName);
    return (Appender)appenderClass.newInstance();
  }
}

By default, the context classloader is set to be the normal Java application classloader. That is, the use of the context classloader in normal Java application scenarios is equivalent to using Class.forName(String) and there is only one classloader, the application classloader. When running inside the Framework, however, the code pattern outlined above fails because:

  • By default, the Framework does not consult the application classloader. OSGi-based applications put their code in bundles and export particular packages instead of placing all the code on the normal Java application classpath.
  • The Framework cannot detect bundle context switches and set the context classloader as required. That is, there is no way to tell when execution context shifts from one bundle to the next as is done in Web application servers.

These characteristics, combined with the compositional nature of OSGi, mean that the value of the context classloader field is seldom useful.

Clients can, however, explicitly set the context classloader before calling libraries that use the context classloader. The snippet below shows an example of calling log4j using this approach:

Thread thread = Thread.currentThread();
ClassLoader loader = thread.getContextClassLoader();
thread.setContextClassLoader(this.getClass().getClassLoader());
try {
  ... log4j library call that calls AppenderHelper.createAppender() ...
} finally {
  thread.setContextClassLoader(loader);
}

First the current context classloader is saved. The context classloader is then set to an appropriate value for the current execution and log4j is called. log4j’s AppenderHelper uses the context classloader, so in this case, it uses the client’s classloader (e.g., this.getClass().getClassLoader()). When the operation is finished, the original context classloader is restored.

The assumption here is that the client’s classloader is able to load all required classes. This may or may not be true. Even if it can, the coding pattern is cumbersome to use and hard to maintain for any significant number of library calls. Ideally, log4j would be able to dynamically discover the context relevant to a particular classloading operation. The 'context finder' enables this.

Context Finder

The context finder is a kind of ClassLoader that is installed by the Equinox Framework as the default context classloader. When invoked, it searches down the Java execution stack for a classloader other than the system classloader. In the AppenderHelper example above, it finds the log4j bundle’s classloader (the one that loaded AppenderHelper). The context finder then delegates the load request to the discovered classloader.

This mechanism transforms log4j’s call to getContextClassLoader().loadClass(String) to the equivalent Class.forName(String) call using log4j’s classloader to load the given class. Now the buddy classloading techniques can be applied to help log4j load the needed appender classes.

The net effect is that clients of log4j do not have to use the cumbersome coding pattern outlined above even though the libraries they call use the context classloader. This approach generalizes to other context classloading situations.

评论

相关推荐

    Side​Bar​ Enhancements

    "SideBar Enhancements" 是一个针对Sublime Text编辑器的插件,主要目的是增强和扩展其默认侧边栏的功能。Sublime Text是一款流行的代码编辑器,因其轻量级、高效和高度可定制性而受到开发者喜爱。SideBar ...

    SideBarEnhancements-st3

    SideBarEnhancements是一款针对Sublime Text 3的强大增强插件,它扩展了默认侧边栏的功能,提供了更多方便用户管理和操作文件及项目的选项。Sublime Text是一款广受欢迎的代码编辑器,以其轻量级、高性能和高度可...

    SideBarEnhancements-st3插件

    SideBarEnhancements是一款针对Sublime Text 3的强大增强插件,旨在扩展其侧边栏功能,提供更加便捷的文件和项目管理。Sublime Text 3是一款广受欢迎的文本编辑器,以其高性能、丰富的自定义性和多语言支持而受到...

    sidebarenhancements汉化目录

    个人汉化的sidebarenhancements目录,大部分的文字已翻译,替换相应的文件即可。

    SideBarEnhancements.zip

    Sublime Text3文件右键增加插件 SideBarEnhancements汉化版 下载解压后放到插件packages目录重启Sublime Text3即可 文件右键出现添加文件 添加文件夹 复制 重命名 删除等操作 上传不了图片 如这链接所示...

    sublime SideBarEnhancements,Emmet插件安装包

    Emmet允许开发者使用简短的语法快速生成复杂的HTML结构,比如`div&gt;ul&gt;li*5`可以瞬间生成五个`&lt;li&gt;`元素嵌套在`&lt;ul&gt;`内,`div#header.class`则能创建一个带有ID和类名的`&lt;div&gt;`元素。这种语法大大减少了手动输入的...

    SideBarEnhancements.sublime-package

    Sublime Text一个小插件——SideBarEnhancements 搜索“SideBarEnhancements”,还是第一个,直接回车确认。骚等一会儿就安装成功了。 如果无法在线安装,可以尝试通过下载安装包,放到Packages目录。...

    sublime 侧边栏增强插件 SideBarEnhancements

    SideBarEnhancements 是 sublime IDE 的一个侧边栏增强插件 , 安装方法详见:https://blog.csdn.net/PY0312/article/details/89529640

    SideBarEnhancements-st3 Sublime Text3 插件

    SideBarEnhancements-st3 是专为Sublime Text3设计的一款强大插件,旨在增强原生侧边栏的功能,提供更为高效和便捷的文件管理体验。Sublime Text是一款广受欢迎的文本编辑器,以其轻量级、高度可定制化以及丰富的...

    Verilog-2001 Behavioral and Synthesis Enhancements

    The Verilog 2001 Standard includes a number of enhancements that are targeted at simplifying designs improving designs and reducing design errors This paper details important enhancements that were ...

    sublime text2 coffeescript perl SideBarEnhancements 等配置

    在这个“sublime text2 coffeescript perl SideBarEnhancements 等配置”的主题中,我们将探讨如何为Sublime Text 2配置CoffeeScript和Perl编程环境,以及如何利用SideBarEnhancements插件优化左侧树菜单。...

    SideBarEnhancements ST2 右键菜单插件

    由于ST3现在SideBarEnhancements已经在ST2的install package菜单中找不到了 此为sublime text2的版本 解压出来的目录放到Packages下,运行SideBar.py文件即可

    5G NR and Enhancements From R15 to R16.pdf

    5G R15到R16演进及R17展望

    SideBarEnhancements-st2(st3)两个版本.rar

    Sublime Text 2的插件SideBarEnhancements不好找,我找了好久的哦! 现在Sublime Text 2扩展安装包里没有SideBarEnhancements插件了,只好手动安装。 -------------------------- -------------------------- ...

    Web Services Enhancements 3.0 Hands On Lab - Security

    Web Services Enhancements (WSE) 是微软为.NET框架提供的一系列增强工具,旨在提升Web服务的安全性和功能性。WSE3.0是其中的一个版本,它着重于改进和扩展了基本的SOAP(简单对象访问协议)服务的安全性。在这个...

    Positioning in CSS Layout Enhancements for the Web epub

    Positioning in CSS Layout Enhancements for the Web 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除

    Wi-Fi 6 Standard - Enhancements for High Efficiency WLAN.zip

    Wi-Fi 6 Standard - Enhancements for High Efficiency WLAN.zip

    Laravel开发-eloquent-enhancements

    BeyondCode\EloquentEnhancements\EloquentEnhancementsServiceProvider::class, ``` 为了启用特定功能,可能还需要在模型中继承相应的类或使用 traits。 3. **关联预加载优化** Eloquent Enhancements 支持预...

    BC425 Enhancements and Modifications

    SAP BC425 Enhancements and Modifications

    C#3.0 Language Enhancements

    public class Person { public string Name { get; set; } public int Age { get; set; } } ``` #### 对象与集合初始化器 (Object and Collection Initializers) 对象与集合初始化器提供了一种更加直观的方式来...

Global site tag (gtag.js) - Google Analytics