JXSE and Equinox Tutorial, Part 2

http://java.dzone.com/articles/jxse-and-equinox-tutorial-part-0

——————————————————————————————————————————————————————

Abstract

In the first post of this series, we covered the quickest way to get JXSE 2.7x working in Equinox and, with some considerations, probably other OSGI frameworks as well. The approach aimed to get the same functionality as you would get by using the jar file in a regular JAVA setting, but as I demonstrated, Equinox does have the benefit of allowing multiple JXSE instances to run simultaneously, which is a great benefit when developing JXSE applications.

In this second tutorial, we will cover some ways of improving the functionality by severing the dependency on the 4.2x version of Jetty (which has just released it's 9.0 version!) and we will introduce some basic tools to visualize the properties and functions of JXSE. This will improve the development cycle of JXSE applications even more.

NOTE: This tutorial will use a modified JXSE jar, which currently has only been sparsely tested. If you want to develop in a fail-safe environment, I would recommend that you stick to approach of adding the 2.7x and org.mortbay.jetty 4.2x jars to your plugin structure. If you are more adventurous or, even better, want to help to improve the tooling, then this post is for you!

Updating Jetty

The approach we covered earlier involves including a Jetty server for every JXSE bundle you want to deploy. The Jetty server is used for HTTP tunneling and basically starts a simple HTTP server with one servlet. If we want to optimize this approach when running multiple JXSE bundle projects, it would stand to reason that we run one server instance, which registers multiple servlets, which is basically the way that Jetty runs in most OSGI environments. Ideally we would utilize the http service for Equinox to register our servlets, but because every JXSE bundle needs to run on a different network port, registration of the servlets is a bit more involved.

The implication of breaking the dependency on the org.mortbay.jetty 4.2x jar (i.e. removing the jar-file from the lib directory and putting it in a separate bundle) is that the JXSE code has to be changed as well, and so we might just as well create a JXSE 2.7x bundle! This bundle will be a client for a declarative service that is offered by the Jetty plugin, and which allows registration of the specific servlets that JXSE uses. The necessary bundles can be found in the extended.zip, which offers functionality that extends the base porting of JXSE applications to Equinox. can be included in your target definition:

  1. Download the zip-file and unpack it in the same target root as the other target plugins (e.g. C:ProjectsTargetExtended)

  2. In the Eclipse IDE, select Window → Preferences → Plug-in Development → Target Platform, and select the active target we assembled in the previous posting

  3. Press 'Edit' and 'Add' a new 'Directory' by selecting the appropriate controls. Browse to the folder where the unzipped files are located and add the folder to your target definition. Press 'Finish' to complete the Wizard

TIP: Extending a target definition with new directories can be a RSI-ridden chore. Luckily the 'Location' field stores previous entries, and so if you select one of these before pressing the 'Browse' button, you may win quite a few mouse-clicks


The target definition has been extended with a number of bundles, three of which are important for this part of the tutorial:

  1. net.jxse (2.7.x)

  2. org.mortbay.jetty (4.2.x)

  3. org.eclipse.jetty.jxse.bridge (1.x.y)

I try to follow the convention to use the version of a jar-file when it is converted to OSGI, and from this it becomes evident that the first two bundles are basically the original jar-files that were deployed in part one of the tutorial. The only difference is that these bundles share their dependencies through a declarative service.

NOTE: Internally, the necessary modifications were performed with minimal impact of the code. To my knowledge, the core functionality should therefore not be affected. The major differences only come into play when multiple JXSE bundles are deployed simultaneously, which I consider to be new functionality anyway. When developing fail-safe applications, the two bundles should therefore be good to use, but keep a sharp eye on the HTTP logs to be certain!


With this, we can now modify the two JXSE Plugin project we developed in the previous post to work with these bundles. We will also use a different example from the book Practical JXTA II to make things more interesting. We will try to connect Edge_Anna with Rendezvous_Jack. Modify the two JXSE 2.7x Bundle Projects you made in the previous tutorial :

  1. Remove the lib folder and the references to lib/jxse-2.7.jar and lib/org.mortbay.jetty.jar in the bundle classpath settings.

  2. Solve the compiler errors by importing the necessary bundle dependencies in the 'imported packages' tab of the Manifest editor.

  3. Include the org.mortbay.jettybundle (not the bridge!)

The resulting Manifest.MF file should look something like this:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Practical JXTA II: Anna the Edge Peer
Bundle-SymbolicName: net.practical.jxta.decoupled.anna
Bundle-Version: 1.0.0.v20130816
Bundle-Activator: net.practical.jxta.decoupled.anna.Activator
Bundle-Vendor: MyCompany
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
Import-Package: net.jxta,
net.jxta.document,
net.jxta.endpoint,
net.jxta.exception,
net.jxta.id,
net.jxta.peer,
net.jxta.peergroup,
net.jxta.platform,
net.jxta.rendezvous,
org.osgi.framework;version="1.7.0"

We can now copy Edge_Anna.java and Rendezvous_Jack.javafrom the folder B_Exploring_Connectivity_Issuesfrom the examples that are covered by Practical JXTA II as replacement for the previous examples. Last we need to make a slight adjustment to the Activators in order to ensure concurrency:

import java.util.concurrent.Executors;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator, Runnable {

private static Activator activator;
private ExecutorService executor;

  static Activator getDefault() {
    return activator;
  }

  public void start(BundleContext bundleContext) throws Exception {
    activator = this;
    executor = Executors.newSingleThreadExecutor();
    executor.execute(this);
  }

  public void stop(BundleContext bundleContext) throws Exception {
    if( !executor.isShutdown() && !executor.isTerminated())
      executor.shutdown();
    Activator.context = null;
  }

  @Override
  public void run() {
    Edge_Anna.main(null);
  }
}

When the bundles are added to your run configuration, you will see that the various pop-up messages are spawned as the two bundles are activated. But hidden deep in the log messages, you will also find that a -if all went well:

    INFO: Jetty Server for JXTA found!

And, a bit later:

    INFO: Line 679 net.jxta.impl.peergroup.GenericPeerGroup.loadModule()
    Loaded privileged module : Reference Implementation of the HTTP Message Transport
    (net.jxta.impl.endpoint.servlethttp.ServletHttpTransport)

As Rendezvous_Jack is starting up HTTP transport. If this doesn't seem to happen, then the most likely reason is that the org.mortbay.jettybundle has not started (type 'ss' in the Eclipse console for the status of the bundles). You can also check if the declarative services that forge the connection between the two bundles have started correctly by typing in the OSGI console:

  1. 'attendees': this should return (at least)
    osgi> attendees
    net.jxse.petitioner.ServerStarterPetitioner
    org.mortbay.jetty.provider.ServerProvider

  2. 'matched': this should return:
    osgi> matched
    net.jxse.petitioner.ServerStarterPetitioner is matched with:
    org.mortbay.jetty.provider.ServerProvider

The bundles use a light-weight wrapper around OSGI's standard declarative services called a secure broker (org.eclipselabs.osgi.ds.broker) to make the connection. This bundle is also provided by the target addition that was downloaded .One of the added functions are three console commands to check the connections (the third one is 'waiting'). The first command lists the (broker) services that are registered, the second one which services are matched with one another. Above you can see that the bunle net.jxseis connected with the bundle org.mortbay.jetty. If these commands are not recognized, it means that the broker plugin is not activated yet.

Integration with Jetty 6 to Jetty 8 (inclusive)

If you are a bit more adventurous, you may want to try the Jetty Bridge that has also been included in the additions to the target. This bundle requires the org.eclipse.jetty bundles from 6.0.0 to 8.9.9, which are often shipped with certain Eclipse distributions. Simply deactivate the org.mortbay.jettybundle and activate org.eclipse.jetty.jxse.bridge. (and Jetty, of course). If all is well, you should see the same behavior as previously, but now more recent versions of Jetty can be used.

NOTE: Please consider that I have only tested this option with Jetty 8, and not extensively. The sources are available on eclipselabs, so a Jetty expert (JXTA knowledge is hardly required) may be able to improve the current code considerably!

Enabling JXSE Tools for Eclipse

The previous examples probably have made clear that pop-up messages and log messages are not going to be very helpful when developing multiple JXSE Bundle projects simultaneously. Although this is work in progress (and probably will always be so), a start has been made to develop JXSE tools that can be used in an Eclipse IDE. I hope to add more posts to this tutorial as functionality becomes available, but here we can begin by demonstrating what needs to be done at minimum to make your JXSE Bundle Projects visible to the tools.

In order to enable the Eclipse tooling, a new target is needed which replaces equinox with an Eclipse version (which, of course, includes an Equinox version). As we have ample experience with target definition management by now, I will just list the three directories that are needed to get things up and running

  1. An Eclipse version. You can opt to copy the Kepler installation to the target environment (recommended), or use the
    ${eclipse-home} option as location (quickest)

  2. the core JXSE bundles that were downloaded for the previous post

  3. The extended JXSE bundles that were downloaded for the first part of this tutorial

Next we need to create a new Eclipse application ( File → New → Plugin Project). Select the option that the 'plug-in is targeted to run with' an Eclipse version, and (in the second tab) that we will develop a Rich Client application. Clicking 'Finish' will generate a bare-bones Eclipse RCP. We will include one perspective to the RCP by hardcoding it in the ApplicationWorkbenchAdvisor:

package org.{my-company}.jxse.ui;

import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
import org.eclipse.ui.application.WorkbenchAdvisor;
import org.eclipse.ui.application.WorkbenchWindowAdvisor;

public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {

  private static final String PERSPECTIVE_ID = "org.eclipse.jxse.perspective";

  @Override
  public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
         IWorkbenchWindowConfigurer configurer) {
    return new ApplicationWorkbenchWindowAdvisor(configurer);
  }

  @Override
  public String getInitialWindowPerspectiveId() {
    return PERSPECTIVE_ID;
  } 
}

Next, remove the Tools.java file from the JXSE bundles and rename the faulty imports in the JXSE bundles to 'net.osgi.jxse.compatibility.Tools'. We also need to make some changes to the code from Practical JXTA II:

  1. Make the class that run the examples extend net.osgi.jxse.compatibility.AbstractExampleContext

  2. Include addModule( MyNetworkManager) after it has been instantiated;

NOTE: The static method addModule registers JXTA modules (NetworkManager, the various services, and so on) to the JXSEContext, by wrapping them in a IJxseComponent instance. In fact, the Jxse service context accepts any object; it manily serves to create a tree structure of provided modules (objects) with the NetworkManager as primary child. The NetworkConfigurator is also represented internally as a  IJxseComponent, but the service context already takes care of this when the NetworkManager is registered. 

Last, a new launch configuration is needed for an 'Eclipse Application'. Select the 'Run an Application' option in the first tab, and select the application you just created. The workspace plugins that are needed should include the application, and the two JXSE Bundle projects 'Jack the Rendezvous' and 'Anna the Edge'. Add the 'Required Bundles', and ensure that the following bundles are included:

  1. org.eclipse.equinox.console

  2. The three org.apache.felix.gogobundles

  3. org.eclipselabs.osgi.ds.broker

  4. org.mortbay.jetty or org.eclipse.jetty.jxse.bridge

  5. net.osgi.jxse.compatibility

  6. org.eclipselabs.jxse.ui

Validate the configuration and 'add Required Bundles' if necessary. If no problems have been detected, you can launch the application. If all went well, you should see the two JXSE bundles start up as they did previously, but instead of the somewhat outdated pop-up boxes from JAVA Swing, the more flashy alternatives from Eclipse's SWT packages should pop up. Also a basic Eclipse application should become visible with some views which are currently empty. The more observant developers will probably spot the

    WARNING:
       This bundle is not a valid JXSE Bundle. A JXSE-INF directory is required.

Ignore this warning for now. It will become more important in the next part of this series.

We are almost there! Stop the run configuration, and make the following modifications to the JXSE Bundle projects:

  1. Create a new component definition (File → New→ …) and select as the parent folder <your.bundle.name>/OSGI-INF, and name jxse.xml (or another name of your choosing). This will create a folder OSGI-INFunder the root of your JXSE bundles and add the file.

  2. When the component editor opens, fill in the details as follows:

    1.<?xml version="1.0" encoding="UTF-8"?>
    2.<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="<your.bundle.name>.service">
    3.<implementation class="<your.bundle.name>.service.OsgiComponent"/>
    4.<reference bind="setAttendeeService" cardinality="1..1" interface="org.eclipselabs.osgi.ds.broker.IAttendeeService" name="IAttendeeService" policy="static" unbind="unsetAttendeeService"/>
    5.</scr:component>
  3. After the component definition is completed, the required OsgiComponent.java needs to be present in the designated package. This class should look as follows:

    1.<?xml version="1.0" encoding="UTF-8"?>
    2.<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="<your.bundle.name>.service">
    3.<implementation class="<your.bundle.name>.service.OsgiComponent"/>
    4.<reference bind="setAttendeeService" cardinality="1..1" interface="org.eclipselabs.osgi.ds.broker.IAttendeeService" name="IAttendeeService" policy="static" unbind="unsetAttendeeService"/>
    5.</scr:component>
  4. Include the following line in the Manifest-MF file:

    Service-Component: OSGI-INF/jxse.xml

  5. The OsgiComponent is very simple:

    01.package <your.bundle.name>;
    02. 
    03.import net.osgi.jxse.service.core.JxseDSComponent;
    04.import <your.bundle.name>.Activator;
    05. 
    06.public class OsgiComponent extends JxseDSComponent {
    07. 
    08.public OsgiComponent() {
    09.super( Activator.getDefault());
    10.}
    11.}

  6. Modify the Activator as follows:

01.package <your.bundle.name>;
02. 
03.import org.osgi.framework.BundleActivator;
04.import org.osgi.framework.BundleContext;
05. 
06.public class Activator extends AbstractJxseBundleActivator{
07. 
08.private static Activator activator;
09. 
10.static Activator getDefault() {
11.return activator;
12.}
13. 
14.public void start(BundleContext bundleContext) throws Exception {
15.activator = this;
16.}
17. 
18.public void stop(BundleContext bundleContext) throws Exception {
19.activator = null;
20.}
21. 
22.@Override
23.public IJxseServiceContext<NetworkManager> createContext(){
24.return new Edge_Anna();
25.}
26.}


As you can see, the Activator has become much simpler, as the superclass it derives from does most of the work. All that needs to be done is to tell the activator which IJxseServiceContextis required, and these rest is taken care of. The OsgiComponentextracts the context from the Activator and offers it as a declarative service.

If the changes compile without problems, you can launch the application, and if all went well, you should now see both JXSE bundles become visible in the navigator at the left-hand side of the GUI. Clicking these should show you the NetworkManager, NetworkConfiguratorand the NetPeerGroup, while their properties are displayed in the properties editor.

NOTE: Currently not all the functionality works flawlessly (as it is an incubator project), but you can set some properties without problems. I am however not sure how JXSE responds to dynamically changing them, so I recommend to use the PropertyViewer for viewing only.

Securing your JXSE Bundle

When deploying your bundle in a production environment, you have to consider that the declarative service opens up means for 'rogue bundles' to listen to the creation and deployment of your JXSE bundle. As this poses a security risk, it is advised to disable the declarative service before deployment in real-world settings. You can either remove the Service-Component entry in the manifest file in order to achieve this. In the next tutorial I will cover a different way of securing the declarative service.

Conclusion

This tutorial has demonstrated some improvements on the results of the previous posting, and has opened up a way to view the content of the JXSE Bundles. So far, the main focus has been to modify existing code in order to get it working in Eclipse / Equinox. The next post will concentrate on building a JXSE bundle from scratch, and mixed-method approaches.

原文地址:https://www.cnblogs.com/cuizhf/p/3412771.html