Requiring bundles :
In section 5.1.2, we discussed how implicit export attributes allow bundles to import packages from a specific bundle. The OSGi specification also supports a module-level dependency concept called a required bundle that provides a similar capability. In chapter 2, we discussed a host of reasons why package-level dependencies are preferred over module-level dependencies, such as them being more flexible and fine-grained. We won’t rehash those general issues. But there is one particular use case where requiring bundles may be necessary in OSGi: if you must deal with split packages.
In standard Java programming, packages are generally treated as split; the Java class path approach merges all packages from different JAR files on the class path into one big soup. This is anathema to OSGi’s modularization model, where packages are treated as atomic (that is, they can’t be split).
When migrating to OSGi from a world where split packages are common, we’re often forced to confront ugly situations. But even in the OSGi world, over time a package may grow too large and reach a point where you can logically divide it into disjoint functionality for different clients. Unfortunately, if you break up the existing package and assign new disjoint package names, you break all existing clients. Splitting the package allows its disjoint functionality to be used independently; but for existing clients, you still need an aggregated view of the package.
This gives you an idea of what a split package is, but how does this relate to requiring bundles? This will become clearer after we discuss what it means to require a bundle and introduce a use case for doing so.
Declaring bundle dependencies :
The big difference between importing a package and requiring a bundle is the scope of the dependency. Whereas an imported package defines a dependency from a bundle to a specific package, a required bundle defines a dependency from a bundle to every package exported by a specific bundle. To require a bundle, you use the Require-Bundle manifest header in the requiring bundle’s manifest file.
You use the Require-Bundle header to specify a bundle dependency in a manifest, like this :
Resolving required bundles is similar to imported packages. The framework tries to satisfy each required bundle; if it’s unable to do so, the bundle can’t be used. The framework resolves the dependency by searching the installed bundles for ones matching the specified symbolic name and version range. Figure 5.9 shows a resolved bundle dependency.
Figure 5.9 Requiring a bundle is similar to explicitly importing every package exported by the target bundle.
To a large degree, requiring bundles is just a brittle way to import packages, because it specifies who instead of what. The significant difference is how it fits into the overall class search order for the bundle, which is as follows :
Packages from required bundles are searched only if the class wasn’t found in an imported package, which means imported packages override packages from required bundles. Did you notice another important difference between imported packages and packages from required bundles in the search order? If a class in a package from a required bundle can’t be found, the search continues to the next required bundle in declared order or the bundle’s local class path. This is how Require-Bundle supports split packages, which we’ll discuss in more detail in the next subsection. First, let’s look at the remaining details of requiring bundles.
As we briefly mentioned in section 5.2.4, it’s also possible to optionally require a bundle using the resolution directive :
The meaning is the same as when you optionally import packages, such as not impacting dependency resolution and the need to catch ClassNotFoundExceptions when your bundle attempts to use potentially missing classes. It’s also possible to control downstream visibility of packages from a required bundle using the visibility directive, which can be specified as private by default or as reexport. For example :
This makes the required bundle dependency transitive. If a bundle contains this, any bundle requiring it also sees the packages from bundle A (they’re re-exported). Figure 5.10 provide a pictorial example :
Aggregating split packages :
Avoiding split packages is the recommended approach in OSGi, but occasionally you may run into a situation where you need to split a package across bundles. Require-Bundle makes such situations possible. Because class searching doesn’t stop when a class isn’t found for required bundles, you can useRequire-Bundle to search for a class across a split package by requiring multiple bundles containing its different parts.
For example, assume you have a package org.foo.bar that’s split across bundles A and B. Here’s a manifest snippet from bundle A :
Here is a manifest snippet from bundle B :
Both bundles claim to export org.foo.bar, even though they each offer only half of it. (Yes, this is problematic, but we’ll ignore that for now and come back to it shortly.) Now, if you have another bundle that wants to use the entire org.foo.bar package, it
has to require both bundles. The bundle metadata may look something like this :
When code from bundle C attempts to load a class from the org.foo.bar package, it follows these steps :
The last step allows org.foo.bar to be split across the required bundles as well as the requiring bundle. Because searching continues across all required bundles, bundle C is able to use the whole package.
What about a bundle wanting to use only one half of the package? Instead of requiring both bundles, it can require just the bundle containing the portion it needs. Sounds reasonable; but does this mean that after you split a package, you’re stuck with using bundle-level dependencies and can no longer use package-level dependencies? No, it doesn’t, but it does require some best practice recommendations.
- HANDLING SPLIT PACKAGES WITH IMPORT-PACKAGE
If another bundle wants to use Import-Package to access the portion of the package contained in bundle B, it can do something like this :
This is similar to using Require-Bundle for the specific bundle. If you add an arbitrary attribute to each exported split package—called split, for example—you can use it to indicate a part name instead. Assume you set split equal to part1 for bundle A and part2 for bundle B. You can import the portion from B as follows :
This has the benefit of being a little more flexible, because if you later change which bundle contains which portion of the split package, it won’t break existing clients. What about existing clients that were using Import-Package to access the entire org.foo.bar package? Is it still possible? It’s likely that existing client bundles are doing the following :
Will they see the entire package if it’s now split across multiple bundles? No. How can the framework resolve this dependency? The framework has no understanding of split packages as far as dependency resolution is concerned. If bundles A and B are installed and another bundle comes along with the above import declaration, the framework treats A and B as both being candidates to resolve dependency. It chooses one following the normal rules of priority for multiple matching candidates. Clearly, no matter which candidate it chooses, the resulting solution will be incorrect.
To avoid such situations, you need to ensure that your split package portions aren’t accidentally used by the framework to resolve an import for the entire package. But how? Mandatory attributes can help. You can rewrite bundle A’s metadata like so :
Likewise for bundle B, but with split equal to part2. Now for a bundle to import either part of the split package, they must explicitly mention the part they wish to use. But what about an existing client bundle wanting to import the whole package? Because its import doesn’t specify the mandatory attribute, it can’t be resolved. You need some way to reconstruct the whole package and make it available for importing; OSGi allows you to create a facade bundle for such a purpose. To make bundle C a facade bundle, you change its metadata to be :
The only change is the last line where bundle C exports org.foo.bar, which is another form of re-exporting a package. In this case, it aggregates the split package by requiring the bundles containing the different parts, and it re-exports the package without the mandatory attribute. Now any client importingorg.foo.bar will be able to resolve to bundle C and have access to the whole package.
Admittedly, this isn’t the most intuitive or straightforward way to deal with split packages. This approach wasn’t intended to make them easy to use, because they’re best avoided; but it does make it possible in those situations where you have no choice
Despite these dire-sounding warnings, OSGi provides another way of dealing with split packages, called bundle fragments. We’ll talk about those shortly, but first we’ll discuss some of the issues surrounding bundle dependencies and split packages.
Issues with bundle dependencies :
Using Import-Package and Export-Package is the preferred way to share code because they couple the importer and exporter to a lesser degree. UsingRequire-Bundle entails much higher coupling and lower cohesion between the importer and the exporter and suffers from other issues, such as the following :
This is by no means an exhaustive list of issues, but it gives you some ideas of what to look out for when using Require-Bundle and (we hope) dissuades you from using it too much.
Dividing bundles into fragments :
Although splitting packages isn’t a good idea, occasionally it does make sense, such as with Java localization. Java handles localization by usingjava.util.ResourceBundles (which have nothing to do with OSGi bundles) as a container to help you turn localeneutral keys into locale-specific objects. When a program wants to convert information into the user’s preferred locale, it uses a resource bundle to do so. A Resource-Bundle is created by loading a class or resource from a class loader using a base name, which ultimately defines the package containing the class or resource for the ResourceBundle. This approach means you typically package many localizations for different locales into the same Java package.
If you have lots of localizations or lots of information to localize, packaging all your localizations into the same OSGi bundle can result in a large deployment unit. Additionally, you can’t introduce new localizations or fix mistakes in existing ones without releasing a new version of the bundle. It would be nice to keep localizations separate; but unlike the split package support of Require-Bundle, these split packages generally aren’t useful without the bundle to which they belong. OSGi provides another approach to managing these sorts of dependencies through bundle fragments. We’ll come back to localization shortly when we present a more in-depth example, but first we’ll discuss what fragments are and what you can do with them.
Understanding fragments :
If you recall the modularity discussion in chapter 2, you know there’s a difference between logical modularity and physical modularity. Normally, in OSGi, a logical module and a physical module are treated as the same thing; a bundle is a physical module as a JAR file, but it’s also the logical module at execution time forming an explicit visibility encapsulation boundary. Through fragments, OSGi allows you to break a single logical module across multiple physical modules. This means you can split a single logical bundle across multiple bundle JAR files.
Breaking a bundle into pieces doesn’t result in a handful of peer bundles; instead, you define one host bundle and one or more subordinate fragmentbundles. A host bundle is technically usable without fragments, but the fragments aren’t usable without a host. Fragments are treated as optional host-bundle dependencies by the OSGi framework. But the host bundle isn’t aware of its fragments, because it’s the fragments that declare a dependency on the host using the Fragment-Host manifest header.
A fragment bundle uses the Fragment-Host manifest header like this :
Although
this header value follows the common OSGi syntax, you can’t specify multiple symbolic names. A fragment is limited to belonging to one host bundle, although it may be applicable to a range of host versions. Note that you don’t need to do anything special to define a bundle as a host; any bundle without aFragment-Host header is a potential host bundle. Likewise, any bundle with a Fragment-Host header is a fragment.
You now understand the relationship between a host and its fragments, but how do they work together? When the framework resolves a bundle, it searches the installed bundles to see if there are any fragments for the bundle being resolved. If so, it merges the fragments into the host bundle. This merging happens in two different ways :
The first form of merging recombines the split physical pieces of the logical bundle, and the second form creates a single logical bundle because OSGi uses a single class loader per logical bundle to achieve encapsulation.
In addition to merging the exported and imported packages and required bundles, the bundle class paths are also merged. This impacts the overall class search order for the bundle, like this :
This is the complete bundle class search order, so you may want to mark this page for future reference! This search order makes it clear how fragments support split packages, because the host and all fragment class paths are searched until the class is found.
Some final issues regarding fragments: Fragments are allowed to have any metadata a normal bundle can have except Bundle-Activator. This makes sense because fragments can’t be started or stopped and can only be used when combined with the host bundle. Attaching a fragment to a host creates a dependency between the two, which is similar to the dependencies created between two bundles via Import-Package or Require-Bundle. This means if either bundle is updated or uninstalled, the other bundle is impacted, and any refreshing of the one will likely lead to refreshing of the other.
Using fragments for localization :
To see how you can use fragments to localize an application, let’s return to the service-based paint program from chapter 4. The main application window is implemented by the PaintFrame class. Recall its design: PaintFrame doesn’t have any direct dependencies on the OSGi API. Instead, it uses a ShapeTrackerclass to track SimpleShape services in the OSGi service registry and inject them into the PaintFrame instance. ShapeTracker injects services into the PaintFrameusing its addShape() method, as shown in the following listing.
The addShape() method is invoked with the name, icon, and service object of the SimpleShape implementation. The exact details aren’t important, but the shape is recorded in a data structure, a button is created for it, its name is set as the button’s tool tip, and, after a few other steps, the associated button is added to the toolbar. The tool tip is textual information displayed to users when they hover the mouse over the shape’s toolbar icon. It would be nice if this information could be localized.
You can take different approaches to localize the shape name. One approach is to define a new service property that defines the ResourceBundle base name. This way, shape implementations can define their localization base name, much as they use service properties to indicate the name and icon. In such an approach, the PaintFrame.addShape() must be injected with the base name property so it can perform the localization lookup. This probably isn’t ideal, because it exposes implementation details.
Another approach is to focus on where the shape’s name is set in the first place: in the shape implementation’s bundle activator. The following listing shows the activator of the circle implementation :
The hardcoded shape name is assigned to the service property dictionary, and the shape service is registered. The first thing you need to do is change the hardcoded name into a lookup from a ResourceBundle. This code shows the necessary changes :
You modify the activator to look up the shape name using the key constant "CIRCLE_NAME" in a ResourceBundle you create C, whose resulting value is assigned to the service properties. Even though we won’t go into the complete details of using ResourceBundle objects, the important part in this example is when you define it. You specify the base name of org.foo.shape.circle.resource.Localize. By default, this refers to a Localize.properties file in the org.foo.shape.circle.resource package, which contains a default mapping for your name key. You need to modify the circle implementation to have this additional package, and you add the Localize.properties file to it with the following content :
This is the default mapping for the shape name. If the example was more complicated, you’d have many more default mappings for other terms. To provide other mappings to other languages, you need to include them in this same package, but in separate property files named after the locales’ country codes. For example, the country code for Germany is DE, so for its localization you create a file called Localize_de.properties with the following content :
You do this for each locale you want to support. Then, at execution time, when you create your ResourceBundle, the correct property file is automatically selected based on the locale of the user’s computer.
This all sounds nice; but if you have a lot of information to localize, you need to include all this information in your bundle, which can greatly increase its size. Further, you have no way of adding support for new locales without releasing a new version of your bundle. This is where fragments can help, because you can split the resource package into different fragments. You keep the default localization in your circle implementation, but all other localizations are put into separate fragments. You don’t need to change the metadata of your circle bundle, because it’s unaware of fragments, but the content of your circle bundle becomes :
For this example, you’ll create a German localization fragment bundle for the circle using the property file shown earlier. The metadata for this fragment bundle is :
The important part of this metadata is the last line, which declares it as a fragment of the circle bundle. The content of the fragment bundle is simple :
It only contains a resource file for the German translation, which you can see is a split package with the host bundle. You can create any number of localization fragments following this same pattern for your other shapes (square and triangle). Figure 5.12 shows the paint program with the German localization fragments installed.
Figure 5.12 Paint program with installed German localization fragments
We’ve now covered all major aspects of the OSGi module layer! As you can see, tools are available to help you deal with virtually any scenario the Java language can throw at you. But we have one more trick up our sleeves: the OSGi specification does a pretty good job of dealing with native code that runs outside of the Java environment. We’ll look at this and how to deal with general factors relating to the JVM environment in the next and final section of this chapter.
This is a blog to track what I had learned and share knowledge with all who can take advantage of them
標籤
- [ 英文學習 ]
- [ 計算機概論 ]
- [ 深入雲計算 ]
- [ 雜七雜八 ]
- [ Algorithm in Java ]
- [ Data Structures with Java ]
- [ IR Class ]
- [ Java 文章收集 ]
- [ Java 代碼範本 ]
- [ Java 套件 ]
- [ JVM 應用 ]
- [ LFD Note ]
- [ MangoDB ]
- [ Math CC ]
- [ MongoDB ]
- [ MySQL 小學堂 ]
- [ Python 考題 ]
- [ Python 常見問題 ]
- [ Python 範例代碼 ]
- [心得扎記]
- [網路教學]
- [C 常見考題]
- [C 範例代碼]
- [C/C++ 範例代碼]
- [Intro Alg]
- [Java 代碼範本]
- [Java 套件]
- [Linux 小技巧]
- [Linux 小學堂]
- [Linux 命令]
- [ML In Action]
- [ML]
- [MLP]
- [Postgres]
- [Python 學習筆記]
- [Quick Python]
- [Software Engineering]
- [The python tutorial]
- 工具收集
- 設計模式
- 資料結構
- ActiveMQ In Action
- AI
- Algorithm
- Android
- Ansible
- AWS
- Big Data 研究
- C/C++
- C++
- CCDH
- CI/CD
- Coursera
- Database
- DB
- Design Pattern
- Device Driver Programming
- Docker
- Docker 工具
- Docker Practice
- Eclipse
- English Writing
- ExtJS 3.x
- FP
- Fraud Prevention
- FreeBSD
- GCC
- Git
- Git Pro
- GNU
- Golang
- Gradle
- Groovy
- Hadoop
- Hadoop. Hadoop Ecosystem
- Java
- Java Framework
- Java UI
- JavaIDE
- JavaScript
- Jenkins
- JFreeChart
- Kaggle
- Kali/Metasploit
- Keras
- KVM
- Learn Spark
- LeetCode
- Linux
- Lucene
- Math
- ML
- ML Udemy
- Mockito
- MPI
- Nachos
- Network
- NLP
- node js
- OO
- OpenCL
- OpenMP
- OSC
- OSGi
- Pandas
- Perl
- PostgreSQL
- Py DS
- Python
- Python 自製工具
- Python Std Library
- Python tools
- QEMU
- R
- Real Python
- RIA
- RTC
- Ruby
- Ruby Packages
- Scala
- ScalaIA
- SQLAlchemy
- TensorFlow
- Tools
- UML
- Unix
- Verilog
- Vmware
- Windows 技巧
- wxPython
訂閱:
張貼留言 (Atom)
[Git 常見問題] error: The following untracked working tree files would be overwritten by merge
Source From Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 # git clean -d -fx 方案2: 今天在服务器上 gi...
-
前言 : 為什麼程序管理這麼重要呢?這是因為: * 首先,本章一開始就談到的,我們在操作系統時的各項工作其實都是經過某個 PID 來達成的 (包括你的 bash 環境), 因此,能不能進行某項工作,就與該程序的權限有關了。 * 再來,如果您的 Linux 系統是個...
-
屬性 : 系統相關 - 檔案與目錄 語法 : du [參數] [檔案] 參數 | 功能 -a | 顯示目錄中個別檔案的大小 -b | 以bytes為單位顯示 -c | 顯示個別檔案大小與總和 -D | 顯示符號鏈結的來源檔大小 -h | Hum...
-
來源自 這裡 說明 : split 是 Perl 中非常有用的函式之一,它可以將一個字串分割並將之置於陣列中。若無特別的指定,該函式亦使用 RE 與 $_ 變數 語法 : * split /PATTERN/,EXPR,LIMIT * split /...
沒有留言:
張貼留言