程式扎記: [ OSGi In Action ] Introducing OSGi : Learning lifecycle (3)

標籤

2011年7月19日 星期二

[ OSGi In Action ] Introducing OSGi : Learning lifecycle (3)


Dynamically extending the paint program :
As you’ll recall from the last chapter, you first converted a nonmodular version of the paint program into a modular one using an interfacebased programming approach for the architecture. This is great because you can reuse the resulting bundles with minimal extra work. The bundles containing the shape implementations don’t need to change, except for some additional metadata in their manifest. You just need to modify the paint program to make it possible for shapes to be added and removed at execution time.

The approach you’ll take is a well-known pattern in the OSGi world, called the extender patternThe main idea behind the extender pattern is to model dynamic extensibility on the lifecycle events (installing, resolving, starting, stopping, and so onof other bundles. Typically, some bundle in the application acts as the extender: it listens for bundles being started and/or stopped. When a bundle is started, the extender probes it to see if it’s an extension bundle. The extender looks in the bundle’s manifest (using Bundle.getHeaders()) or the bundle’s content (using Bundle.getEntry()) for specific metadata it recognizes. If the bundle does contain an extension, the extension is described by the metadata. The extender reads the metadata and performs the necessary tasks on behalf of the extension bundle to integrate it into the application. The extender also listens for extension bundles to be stopped, in which case it removes the associated extensions from the application. That’s the general description of the extender pattern, which is shown in figure 3.16. Let’s look at how you’ll use it in the paint program.


You’ll treat the shape implementations as extensions. The extension metadata will be contained in the bundle manifest and will describe which class implements the shape contained in the shape bundle. The extender will use this information to load the shape class from the bundle, instantiate it, and inject it into the application when an extension bundle is activated. If a shape bundle is stopped, the extender will remove it from the application. Figure 3.17 illustrates this usage scenario :

Figure 3.17 Paint program as an implementation of the extender pattern

Let’s dive in and start converting the application. The first thing you need to do is define the extension metadata for shape bundles to describe their shape implementation. In the following snippet, you add a couple of constants to the SimpleShape interface for extension metadata property names; it’s not strictly necessary to add these, but it’s good programming practice to use constants :
  1. package org.foo.shape;  
  2.   
  3. import java.awt.Graphics2D;  
  4. import java.awt.Point;  
  5.   
  6. public interface SimpleShape {  
  7.     public static final String NAME_PROPERTY = "Extension-Name";  
  8.     public static final String ICON_PROPERTY = "Extension-Icon";  
  9.     public static final String CLASS_PROPERTY = "Extension-Class";  
  10.   
  11.     public void draw(Graphics2D g2, Point p);  
  12. }  
The constants indicate the name of the shape, the bundle resource file for the shape’s icon, and the bundle class name for the shape’s class. The draw()method draws the shape on the canvas. From the constants, it’s fairly straightforward to see how you’ll describe a specific shape implementation. You only need to know the name, an icon, and the class implementing the shape. As an example, for the circle implementation you add the following entries to its bundle manifest :
Extension-Name: Circle
Extension-Icon: org/foo/shape/circle/circle.png
Extension-Class: org.foo.shape.circle.Circle

The name is just a string, and the icon and class refer to a resource file and a class inside the bundle JAR file, respectively. You add similar metadata to the manifests of all shape implementation bundles, which converts them all to extensions. Next, you need to tweak the architecture of the paint program to make it cope with dynamic addition and removal of shapes. Figure 3.18 captures the updated design.

Figure 3.18 Dynamic paint program class relationships

Comparing the new design to the original, you add two new classes: ShapeTracker and DefaultShape. They help you dynamically adapt the paint frame to deal with SimpleShape implementations dynamically appearing and disappearing. In a nutshell, the ShapeTracker is used to track when extension bundles start or stop, in which case it adds or removes DefaultShapes to/from the PaintFrame, respectively.

The concrete implementation of the ShapeTracker is a subclass of another class, called BundleTracker. The latter class is a generic class for tracking when bundles are started or stopped. Because BundleTracker is somewhat long, we’ll divide it across multiple listings; the first part is shown next.
- Listing 3.14 BundleTracker class declaration and constructor
  1. package org.foo.paint;  
  2.   
  3. import java.util.*;  
  4. import org.osgi.framework.*;  
  5.   
  6. public abstract class BundleTracker {  
  7.     final Set m_bundleSet = new HashSet();  
  8.     final BundleContext m_context;  
  9.     final SynchronousBundleListener m_listener;  
  10.     boolean m_open;  
  11.   
  12.     public BundleTracker(BundleContext context) {  
  13.         m_context = context;  
  14.         m_listener = new SynchronousBundleListener() {  
  15.             public void bundleChanged(BundleEvent evt) {  // (1) Implement Bundle Listener Method  
  16.                 synchronized (BundleTracker.this) {  
  17.                     if (!m_open) {  // (2) Check if tracking bundles  
  18.                         return;  
  19.                     }  
  20.                     if (evt.getType() == BundleEvent.STARTED) {  
  21.                         if (!m_bundleSet.contains(evt.getBundle())) {  
  22.                             m_bundleSet.add(evt.getBundle()); // (3) Adds bundle to list  
  23.                             addedBundle(evt.getBundle());  
  24.                         }     
  25.                     } else if (evt.getType() == BundleEvent.STOPPING) {  
  26.                         if (m_bundleSet.contains(evt.getBundle())) {  
  27.                             m_bundleSet.remove(evt.getBundle());  
  28.                             removedBundle(evt.getBundle());  
  29.                         }  
  30.                     }  
  31.                 }  
  32.             }  
  33.         }  
  34.     }  
  35. }  

The bundle tracker is constructed with a BundleContext object, which is used to listen for bundle lifecycle events. The tracker uses aSynchronousBundleListener to listen to events because a regular BundleListener doesn’t get notified when a bundle enters the STOPPING state, only STOPPED. You need to react on the STOPPING event instead of the STOPPED event because it’s still possible to use the stopping bundle, which hasn’t been stopped yet; a potential subclass might need to do this if it needed to access the stopping bundle’s BundleContext object. The bundle listener’s single method (1) makes sure the tracker is tracking bundles (2). If so, for started events, it adds the associated bundle to its bundle list (3) and invokes the abstractaddedBundle() method. Likewise, for stopping events, it removes the bundle from its bundle list and invokes the abstract removedBundle() method.

The following listing shows the next portion of the BundleTracker :
- Listing 3.15 Opening and using a BundleTracker
  1. public synchronized void open() {  
  2.     if (!m_open) {  
  3.         m_open = true;  
  4.         m_context.addBundleListener(m_listener);  
  5.         Bundle[] bundles = m_context.getBundles();  
  6.         for (int i = 0; i < bundles.length; i++) {  
  7.             if (bundles[i].getState() == ACTIVE) {  
  8.                 m_bundleSet.add(bundles[i]);  
  9.                 addedBundles(bundles[i]);  
  10.             }  
  11.         }  
  12.     }  
  13. }  
  14.   
  15. public synchronized Bundle[] getBundles() {  
  16.     return (Bundle[]) m_bundleSet.toArray(new Bundle[m_bundleSet.size()]);  
  17. }  
  18.   
  19. protected abstract void addedBundle(Bundle bundle);  
  20.   
  21. protected abstract void removedBundle(Bundle bundle);  

To start a BundleTracker instance tracking bundles, you must invoke its open() method. This methods registers a bundle event listener and processes any existing ACTIVE bundles by adding them to its bundle list and invoking the abstract addedBundle() method. The getBundles() method provides access to the current list of active bundles being tracked. Because BundleTracker is abstract, subclasses must provide implementations of addedBundle() andremovedBundle() to perform custom processing of added and removed bundles, respectively.

The last portion of the BundleTracker is as follows :
- Listing 3.16 Disposing of a BundleTracker
  1. public synchronized void close() {  
  2.     if (m_open) {  
  3.         m_open = false;  
  4.         m_context.removeBundleListener(m_listener);  
  5.         Bundle[] bundles = (Bundle[]) m_bundleSet  
  6.                 .toArray(new Bundle[m_bundleSet.size()]);  
  7.         for (int i = 0; i < bundles.length; i++) {  
  8.             if (m_bundleSet.remove(bundles[i])) {  
  9.                 removedBundle(bundles[i]);  
  10.             }  
  11.         }  
  12.     }  
  13. }  

Calling BundleTracker.close() stops it from tracking bundles. This removes its bundle listener, removes each currently tracked bundle from its bundle list, and invokes the abstract removedBundle() method.


Now that you know how the BundleTracker works, let’s return to its subclass, Shape-Tracker. The heart of this subclass is the processBundle() method shown next, which processes added and removed bundles.
- Listing 3.17 Processing shapes in ShapeTracker
  1. private void processBundle(int action, Bundle bundle) {  
  2.     Dictionary dict = bundle.getHeaders();  
  3.     String name = (String) dict.get(SimpleShape.NAME_PROPERTY); // (1) Check if bundle is an extension  
  4.     if (name == null) {  
  5.         return;  
  6.     }  
  7.     switch (action) {  
  8.     case ADDED:  // (2) Add shape to paint frame  
  9.         String iconPath = (String) dict.get(SimpleShape.ICON_PROPERTY);  
  10.         Icon icon = new ImageIcon(bundle.getResource(iconPath));  
  11.         String className = (String) dict.get(SimpleShape.CLASS_PROPERTY);  
  12.         m_frame.addShape(  
  13.                 name,  
  14.                 icon,  
  15.                 new DefaultShape(m_context, bundle.getBundleId(), className));  
  16.         break;  
  17.     case REMOVED:  // (3) Remove shape  
  18.         m_frame.removeShape(name);  
  19.         break;  
  20.     }  
  21. }  

ShapeTracker overrides BundleTracker’s addedBundle() and removedBundle() abstract methods to invoke processBundle() in either case. You determine whether the bundle is an extension by probing its manifest for the Extension-Name property B. Any bundle without this property in its manifest is ignored. If the bundle being added contains a shape, the code grabs the metadata from the bundle’s manifest headers and adds the shape to the paint frame wrapped as a DefaultShape (2). For the icon metadata, you use Bundle.getResource() to load it. If the bundle being removed contains a shape, you remove the shape from the paint frame (3).

DefaultShape, shown in listing 3.18, serves two purposes. It implements the SimpleShape interface and is responsible for lazily creating the shape implementation using the Extension-Class metadata. It also serves as a placeholder for the shape if and when the shape is removed from the application. You didn’t have to deal with this situation in the original paint program, but now shape implementations can appear or disappear at any time when bundles are installed, started, stopped, and uninstalled. In such situations, the DefaultShape draws a placeholder icon on the paint canvas for any departed shape implementations.
- Listing 3.18 DefaultShape example
  1. package org.foo.paint;  
  2.   
  3. import java.awt.*;  
  4. import javax.swing.ImageIcon;  
  5. import org.foo.shape.SimpleShape;  
  6. import org.osgi.framework.Bundle;  
  7. import org.osgi.framework.BundleContext;  
  8.   
  9. class DefaultShape implements SimpleShape {  
  10.   private SimpleShape m_shape;  
  11.   private ImageIcon m_icon;  
  12.   private BundleContext m_context;  
  13.   private long m_bundleId;  
  14.   private String m_className;  
  15.   
  16.   public DefaultShape() {  
  17.   // Do nothing.  
  18.   }  
  19.   
  20.   public DefaultShape(BundleContext context, long bundleId, String className) {  
  21.     m_context = context;  
  22.     m_bundleId = bundleId;  
  23.     m_className = className;  
  24.   }  
  25.   
  26.   public void draw(Graphics2D g2, Point p) {  
  27.     // If this is a proxy shape, instantiate the shape class  
  28.     // and use it to draw the shape.  
  29.     if (m_context != null) {  
  30.       try {  
  31.         if (m_shape == null) {  
  32.           // Get the bundle.  
  33.           Bundle bundle = m_context.getBundle(m_bundleId);  
  34.           // Load the class and instantiate it.  
  35.           Class clazz = bundle.loadClass(m_className);  
  36.           m_shape = (SimpleShape) clazz.newInstance();  
  37.         }  
  38.         // Draw the shape.  
  39.         m_shape.draw(g2, p);  
  40.         // If everything was successful, then simply return.  
  41.         return;  
  42.       } catch (Exception ex) {  
  43.         // This generally should not happen, but if it does then  
  44.         // we can just fall through and paint the default icon.  
  45.       }  
  46.     }  
  47.   
  48.     // If the proxied shape could not be drawn for any reason or if  
  49.     // this shape is simply a placeholder, then draw the default icon.  
  50.     if (m_icon == null) {  
  51.       try {  
  52.         m_icon = new ImageIcon(this.getClass().getResource("underc.png"));  
  53.       } catch (Exception ex) {  
  54.         ex.printStackTrace();  
  55.         g2.setColor(Color.red);  
  56.         g2.fillRect(006060);  
  57.         return;  
  58.       }  
  59.     }  
  60.     g2.drawImage(m_icon.getImage(), 00null);  
  61.   }  
  62. }  

In summary, when the paint application is started, its activator creates and opens a ShapeTracker. This tracks STARTED and STOPPED bundle events, interrogating the associated bundle for extension metadata. For every started extension bundle, it adds a new DefaultShape for the bundle to the paint frame, which creates the shape implementation, if needed, using the extension metadata. When the bundle stops, the ShapeTracker removes the shape from the paint frame. When a drawn shape is no longer available, the DefaultShape is used to draw a placeholder shape on the canvas instead. If the departed shape reappears, the placeholder is removed and the real shape is drawn on the canvas again.

Now you have a dynamically extensible paint program, as demonstrated in Part1. Although we didn’t show the activator for the paint program, it’s reasonably simple and only creates the framework and shape tracker on start and disposes of them on stop. Overall, this is a good example of how easy it is to make a modularized application take advantage of the lifecycle layer to make it dynamically extensible. What you’re still missing at this point is a discussion about how the lifecycle and module layers interact with each other, which we’ll get into next.

Lifecycle and modularity :
A two-way relationship exists between OSGi’s lifecycle and module layers. The lifecycle layer manages which bundles are installed into the framework, which obviously impacts how the module layer resolves dependencies among bundles. The module layer uses the metadata in bundles to make sure all their dependencies are satisfied before they can be used. This symbiotic relationship creates a chicken-and-egg situation when you want to use your bundles; to use a bundle you have to install it, but to install a bundle you must have a bundle context, which are only given to bundles. This close relationship is also obvious in how the framework resolves bundle dependencies, especially when bundles are dynamically installed and/or removed. Let’s explore this relationship by first looking into bundle dependency resolution.

Resolving bundles :
The act of resolving a bundle happens at the discretion of the framework, as long as it happens before any classes are loaded from the bundle. Often, when resolving a given bundle, the framework ends up resolving another bundle to satisfy a dependency of the original bundle. This can lead to cascading dependency resolution, because in order for the framework to use a bundle to satisfy the requirements of another bundle, the satisfying bundle too must be resolved, and so on. Because the framework resolves dependencies when needed, it’s possible to mostly ignore transitioning bundles to the RESOLVEDstate; you can start a bundle and know the framework will resolve it before starting it, if possible. This is great compared to the standard Java way, where you can run into missing dependencies at any point during the lifetime of your application.

But what if you want to make sure a given bundle resolves correctly? For example, maybe you want to know in advance whether an installed bundle can be started. In this case, there’s a way to ask the framework to resolve the bundle directly, but it’s not a method on Bundle like most other lifecycle operations. Instead, you use the Package Admin Service. The Package Admin Service is represented as an interface and is shown here :
  1. public interface PackageAdmin {  
  2.     static final int BUNDLE_TYPE_FRAGMENT = 0x00000001;  
  3.   
  4.     Bundle getBundle(Class clazz);  
  5.     Bundle[] getBundles(String symbolicName, String versionRange);  
  6.     int getBundleType(Bundle bundle);  
  7.     ExportedPackage getExportedPackage(String name);  
  8.     ExportedPackage[] getExportedPackages(Bundle bundle);  
  9.     ExportedPackage[] getExportedPackages(String name);  
  10.     Bundle[] getFragments(Bundle bundle);  
  11.     RequiredBundle[] getRequiredBundles(String symbolicName);  
  12.     Bundle[] getHosts(Bundle bundle);  
  13.     void refreshPackages(Bundle[] bundles);  
  14.     boolean resolveBundles(Bundle[] bundles);  
  15. }  
You can explicitly resolve a bundle with the resolveBundles() method, which takes an array of bundles and returns a Boolean flag indicating whether the bundles could be resolved. The Package Admin Service can do a bit more than resolving bundles, and it’s a fairly important part of the framework; it also supports the following operations, among others :
* Determines which bundle owns a particular class
In rare circumstances, you may need to know which bundle owns a particular class. You can accomplish this with the getBundle() method, which takes a Class and returns the Bundle to which it belongs.

* Introspects how the framework resolves bundle dependencies
You can use the getExportedPackage() family of methods to find out which bundles are importing a given package, whereas other methods inspect other types of dependencies we won’t talk about until chapter 5, such as getRequiredBundles() and getFragments().

* Refreshes the dependency resolution for bundles
Because the installed set of bundles can evolve over time, sometimes you need to have the framework recalculate bundle dependencies. You can do this with the refreshBundles() method.

The most important feature of the Package Admin Service isn’t the ability to resolve bundles or introspect dependencies; it’s the ability to refresh bundle dependencies, which is another tool needed for managing bundles. But before we get into the details of refreshing bundles, let’s finish the discussion of explicitly resolving bundles. To demonstrate how to use the Package Admin Service to explicitly resolve a bundle, you’ll create a new resolve command for the shell to instigate bundle resolution, as shown next :
- Listing 3.19 Bundle resolve command
  1. package org.foo.shell;  
  2.   
  3. import java.io.PrintStream;  
  4. import java.util.*;  
  5. import org.osgi.framework.Bundle;  
  6. import org.osgi.service.packageadmin.PackageAdmin;  
  7.   
  8. public class ResolveCommand extends BasicCommand {  
  9.     public void exec(String args, PrintStream out, PrintStream err) throws Exception {  
  10.         boolean success;      
  11.         if (args == null) {  
  12.             success = getPackageAdminService().resolveBundles(null);  
  13.         } else {  
  14.             List bundles = new ArrayList();  
  15.             StringTokenizer tok = new StringTokenizer(args);  
  16.             while (tok.hasMoreTokens()) {  
  17.                 bundles.add(getBundle(tok.nextToken()));  
  18.             }  
  19.             success = getPackageAdminService().resolveBundles(  
  20.                     bundles.toArray(newBundle[bundles.size()]));  
  21.         }  
  22.         out.println(success ? "Success" : "Failure");  
  23.     }  
  24.     private PackageAdmin getPackageAdminService() {//...}  
  25. }  

We won’t discuss the details of how you obtain the Package Admin Service until the next chapter; for now, you use the getPackageAdminService() method. If the resolve command is executed with no arguments, you invoke resolveBundles() with null, which causes the framework to attempt to resolve all unresolved bundles. Otherwise, you parse the argument as a list of whitespace-separated bundle identifiers. For each identifier, you get its associated Bundle object and add it to a list. After you’ve retrieved the complete list of bundles, you pass them in as an array to resolveBundles(). The framework attempts to resolve any unresolved bundles of those specified.

Resolving a bundle is a fairly easy process, because the framework does all the hard work for you. You’d think that’d be it. As long as your bundle’s dependencies are resolved, you have nothing to worry about, right? It turns out the dynamic nature of the bundle lifecycle makes this an invalid assumption. Sometimes you need to have the framework recalculate a bundle’s dependencies. We’ll tell you all about it in the next section.

Refreshing bundles :
The lifecycle layer allows you to deploy and manage your application’s bundles. Up until now we’ve focused on installing, resolving, and starting bundles, but there are other interesting bundle lifecycle operations. How about updating or uninstalling a bundle? In and of themselves, these operations are as conceptually simple as the other lifecycle operations. We certainly understand what it means to update or uninstall a bundle. The details are a little more complicated. When you update or uninstall a resolved bundle, you stand a good chance of disrupting your system. This is the place where you can start to see the impact of the framework’s dynamic lifecycle management.

The simple case is updating or uninstalling a self-contained bundle. In this case, the disruption is limited to the specific bundle. Even if the bundle imports packages from other bundles, the disruption is limited to the specific bundle being updated or uninstalled. In either case, the framework stops the bundle if it’s active. In the case of updating, the framework updates the bundle’s content and restarts it if it was previously active. Complications arise if other bundles depend on the bundle being updated or uninstalled. Such dependencies can cause a cascading disruption to your application, if the dependent bundles also have bundles depending on them.

It’s worthwhile to limit the disruptions caused by bundle updates or uninstalls. The framework provides such control by making updating and uninstalling bundles a twostep process. Conceptually, the first step prepares the operation; and the second step, called refreshing, enacts its. Refreshing recalculates the dependencies of the impacted bundles. How does this help? It allows you to control when the changeover to the new bundle version or removal of a bundle occurs for updates and uninstalls, respectively, as shown in figure 3.19 :


We say this is a two-step process, but what happens in the first step? For updates, the new bundle version is put in place, but the old version is still kept around so bundles depending on it can continue loading classes from it. You may be thinking, "Does this mean two versions of the bundle are installed at the same time?" Effectively, the answer is, yes. And each time you perform an update without a refresh, you introduce yet another version. For uninstalls, the bundle is removed from the installed list of bundles, but it isn’t removed from memory. Again, the framework keeps it around so dependent bundles can continue to load classes from it.

For example, imagine you want to update a set of bundles. It would be fairly inconvenient if the framework refreshed all dependent bundles after each individual update. With this two-step approach, you can update all bundles in the set and then trigger one refresh of the framework at the end. You can experience a similar situation if you install a bundle providing a newer version of a package. Existing resolved bundles importing an older version of the package won’t be automatically rewired to the new bundle unless they’re refreshed. Again, it’s nice to be able to control the point in time when this happens. It’s a fairly common scenario when updating your application that some of your bundles are updated, some are uninstalled, and some are installed; so a way to control when these changes are enacted is helpful.

You trigger a refresh by using the Package Admin Service again. To illustrate how to use it, let’s add a refresh command to the shell, as shown next :
- Listing 3.20 Bundle refresh command
  1. package org.foo.shell;  
  2.   
  3. import java.io.PrintStream;  
  4. import java.util.*;  
  5. import org.osgi.framework.Bundle;  
  6. import org.osgi.service.packageadmin.PackageAdmin;  
  7.   
  8. public class RefreshCommand extends BasicCommand {  
  9.     public void exec(String args, PrintStream out, PrintStream err)  
  10.             throws Exception {  
  11.         if (args == null) {  
  12.             getPackageAdminService().refreshPackages(null);  
  13.         } else {  
  14.             List bundles = new ArrayList();  
  15.             StringTokenizer tok = new StringTokenizer(args);  
  16.             while (tok.hasMoreTokens()) {  
  17.                 bundles.add(getBundle(tok.nextToken())); // (1) List bundles to be refreshed  
  18.             }  
  19.             getPackageAdminService().refreshPackages(  // (2) Pass bundle array to Package Admin Service  
  20.                     bundles.toArray(new Bundle[bundles.size()]));  
  21.         }  
  22.     }  
  23.   
  24.     private PackageAdmin getPackageAdminService() {//...}  
  25. }  

The PackageAdmin.refreshPackages() method updates or removes packages exported by the bundles being refreshed. The method returns to the caller immediately and performs the following steps on a separate thread :
1. It computes the graph of affected dependent bundles, starting from the specified bundles (or from all updated or uninstalled bundles if null is specified). Any bundle wired to a package currently exported by a bundle in the graph is added to the graph. The graph is fully constructed when there is no bundle outside the graph wired to a bundle in the graph.

2. Each bundle in the graph in the ACTIVE state is stopped, moving it to the RESOLVED state.

3. Each bundle in the graph in the RESOLVED state, including those that were stopped, is unresolved and moved to the INSTALLED state. This means the bundles’ dependencies are no longer resolved.

4. Each bundle in the graph in the UNINSTALLED state is removed from the graph and completely removed from the framework (is free to be garbage collected). You’re back to a fresh starting state for the affected bundles.

5. For remaining bundles in the graph, the framework restarts any previously ACTIVE bundles, which resolves them and any bundles on which they depend.

6. When everything is done, the framework fires an event of type Framework-Event.PACKAGES_REFRESHED.

As a result of these steps, it’s possible that some of the previously ACTIVE bundles can no longer be resolved; maybe a bundle providing a required package was uninstalled. In such cases, or for any other errors, the framework fires an event of type FrameworkEvent.ERROR.

When updating isn’t updated :
One of the gotchas many people run into when updating a bundle is the fact that it may or may not use its new classes after the update operation. We said previously that updating a bundle is a two-step process, where the first step prepares the operation and the second step enacts it, but this isn’t entirely accurate when you update a bundle. The specification says the framework should enact the update immediately, so after the update the bundle should theoretically be using its new classes; but it doesn’t necessarily start using them immediately. In some situations, after a bundle is updated, new classes are used; in other situations, old classes are used. Sounds confusing, doesn’t it? It is. Why not just wait until a refresh to enact the new revision completely?

The answer, as you might guess, is historical. The original R1 specification defined the update operation to update a bundle. End of story. There was no Package Admin Service. With experience, it became clear that the specified definition of update was insufficient. Too many details were left for framework implementations to decide, such as when to dispose of old classes and start using new classes. Back to the issue of an updated bundle sometimes using old or new classes. There is a way to understand what’s going on. Whether your bundle’s new classes or the old classes are used after an update depends on two factors :
■ Whether the classes are from a private package or an exported package
■ If the classes are from an exported package, whether they’re being used by another bundle

Regarding the first factor :
■ If the classes come from a private bundle package (one that isn’t exported), the new classes become available immediately no matter what.
■ If they’re from an exported package, their visibility depends on whether other bundles are using them :
– If no other bundles are using the exported packages, the new classes become available immediately. The old versions of the classes are no longer needed.
– If any other bundles are using the exported packages, the new classes don’t become available immediately, because the old version is still required. In this case, the new classes aren’t made available until the PackageAdmin.refreshPackages() method is called.

Supplement :
[ OSGi In Action ] Introducing OSGi : Learning lifecycle (1)
- 3.1 Introducing lifecycle management
- 3.2 OSGi bundle lifecycle

[ OSGi In Action ] Introducing OSGi : Learning lifecycle (2)
- 3.3 Using the lifecycle API in your bundles
So far, you haven’t implemented much functionality for the shell—you just created the activator to start it up and shut it down. In this section, we’ll show you how to implement the bulk of its functionality. You’ll use a simple command pattern to provide the executable actions to let you interactively install, start, stop, update, and uninstall bundles. You’ll even add a persistent history to keep track of previously executed commands...

[ OSGi In Action ] Introducing OSGi : Learning lifecycle (3)
- 3.4 Dynamically extending the paint program
- 3.5 Lifecycle and modularity
This message was edited 29 times. Last update was at 15/07/2011 17:50:37

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!