Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

JXSE and Equinox Tutorial, Part 3: Introducing the JP2P Container

DZone's Guide to

JXSE and Equinox Tutorial, Part 3: Introducing the JP2P Container

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

 Abstract

    It has been a while since the first and second posts of this series, but a lot has happened in the past few months, most notably the fact that the code from eclipselabs is going to be ported to Project Chaupal which will (eventually) be(come) the OSGI implementation of the JXTA specs. As a result, I decided to rename the packages and make the architecture as clean as possible prior to the change.

    This tutorial will cover some of the features which are already available, and can help to make the development of JXTA applications in Eclipse/Equinox a bit easier.

NOTE: This tutorial uses a revisioned version of the Extended libraries, and readers who worked with the previous tutorials may find that their code will no longer compile. The tutorial has been updated to reflect the changes. You can get the new libraries here .

      Building a JP2P Container from Scratch

    A quick recap. The previous tutorials demonstrated ways to get existing JXTA applications working in Equinox. A few examples from the book Practical JXTA II were wrapped into OSGI bundles, and could be started and viewed in the Eclipse IDE.

    In fact, the bundles are implementations of a JP2P Container, which is a fancy way of saying that these bundles conform to a certain structure, which other bundles (e.g. the JXTA Container Navigator in your IDE) can recognise. For an end user, the main visible feature of a JP2P container is that it requires a JP2P-INF folder, which serves as a general purpose repository of resources related to peer to peer. As the examples of Practical JXTA II are self-contained, the previous examples do not require such resources, and could therefore remain empty. The result of this was a warning message:

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

    We can now start to utilise this feature of the Chaupal architecture.

    In order to do so, we make a JP2P bundle. Create a new plugin project. In the plugin manifest file, enter the name of an activator, and check the option to “activated the plugin when one of the classes is loaded” Then create the activator, and modify the class as follows:

package your.bundle.name;

import org.osgi.framework.BundleContext;
import net.osgi.jp2p.chaupal.activator.Jp2pBundleActivator;

public class Activator extends Jp2pBundleActivator {

  public static final String S_BUNDLE_ID = "your.bundle.name";

  private static Jp2pBundleActivator activator;

  public Activator() {
    super(S_BUNDLE_ID);
  }

  @Override
  public void start(BundleContext bundleContext) throws Exception {
    activator = this;
    super.start(bundleContext);
  }

  @Override
  public void stop(BundleContext bundleContext) throws Exception {
    super.stop(bundleContext);
    activator = null;
  }
	
	public static Jp2pBundleActivator getDefault(){
		return activator;
	}
}

There are few changes with respect to the Activator class of the previous tutorial, save that this activator inherits from a concrete implementation of the JP2PBundleActivator, instead of its abstract superclass. This implementation looks for a jp2p-1.0.0.xml file stored in the JP2P-INF directory, and parses this file if it finds one. Therefore create a file with this name and add it to the JP2P-INF folder. As a result, your bundle structure should look something like this (the OSGI-INF folder will be included later):

We start with the following contents:

<?xml version='1.0' encoding='UTF-8'?>
<jp2p-container id="your.bundle.name.container" name="My Test JP2P Bundle">
  <properties>
    <bundle-id>your.bundle.name</bundle-id>
    <home-folder>${user.home}/.jp2p/${bundle-id}</home-folder>
  </properties>
</jp2p-container>

The home folder is the place where all the JP2P (and JXTA) files will be stored. You can enter your preferred location in this field if you wish. The default location is a .jp2p folder in the user home directory. The bundle is is used to differentiate between the various bundles. This is represented in the above example, where an Eclipse-style notation is used to represent (in Windows):

C:\Users\.jp2p\your.bundle.name\

Next we need to add a declarative service, so that the bundle will be registered with the IDE. This procedure is exactly the same as that of the previous tutorial, so I will only give a quick recap here:

  1. Create an OSGI-INF folder in your bundle and add it to the build.properties file

  2. create a jp2p.xml file with the following content:

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

  4. Service-Component: OSGI-INF/jp2p.xml

  5. Implement the OsgiComponent implementation class:

    package <your.bundle.name>;
    
    import net.jp2p.container.startup.Jp2pStartupService;
    
      import net.osgi.jp2p.chaupal.core.Jp2pDSComponent;
      import <your.bundle.name>.Activator;
    
      public class OsgiComponent extends JxseDSComponent<Jp2pStartupService> {
       
       public OsgiComponent() {
         super( Activator.getDefault());
       }
    }

    There's another small change with respect to the OSGI component of the previous tutorial, and this is that a Jp2pStartupService has replaced the NetworkManager. With this, you should be able to run the bundle in the IDE that you made previously.

    Open the launch configuration, ensure that all the required bundles are added (REMOVE the net.osgi.jxse.compatibility bundle!), and start the launcher. If all went well, you should see the following:

    Figure 1: A Simple JP2P Bundle

    At this point, you have an empty JP2P container. The JP2P Container Navigator consists of only one node, the container itself. The property sheet shows the (converted) information that is given in the jp2p-1.0.0.xml file. Furthermore it doesn't do much, so let's kick it to life! Modify the jp2p-1.0.0.xml as follows:

<?xmlversion='1.0'encoding='UTF-8'?>
  <jp2p-container id="your.bundle.name.container" name="My Test JP2P Bundle">
    <properties>
      <bundle-id>your.bundle.name</bundle-id>
      <home-folder>${user.home}/.jp2p/${bundle-id}</home-folder>
    </properties>
    <startup-service auto-start="true"/>
    <logger-service auto-start="true"/>
    <persistence-service context="chaupal"/>
    <network-manager clear-config="true" auto-start="true">
      <properties>
        <config-mode>RENDEZVOUS</config-mode>
        <peer-id create="true" persist="true"/>
      </properties>
      <network-configurator>
        <properties>
          <tcp>
            <enabled>true</enabled>
            <incoming-status>true</incoming-status>
            <outgoing-status>true</outgoing-status>
            <port>9715</port>
          </tcp>
          <multicast>
            <enabled>false</enabled>
          </multicast>
        </properties>
     </network-configurator>
   </network-manager>
</jp2p-container>

Relaunch and take a look at the JP2P Container Navigator. As you can see, the container now shows a tree which represents a JP2P bundle. In fact, the above XML describes the 'Jack the Rendezvous' example of Practical JXTA II! This is what 'Anna the Edge Peer' looks like.

<?xmlversion='1.0'encoding='UTF-8'?>
  <jp2p-container id="anna.the.edge.peer.bundle.container" name="Anna the Edge Peer">
    <properties>
      <bundle-id>anna.the.edge.peer.bundle</bundle-id>
      <home-folder>${user.home}/.jp2p/${bundle-id}</home-folder>
    </properties>
    <startup-service auto-start="true"/>
    <persistence-service context="chaupal"/>
    <logger-service auto-start="true"/>
    <network-manager clear-config="true">
      <properties>
        <config-mode>EDGE</config-mode>
        <peer-id create="true" persist="true"/>
      </properties>
        <network-configurator>
          <properties>
            <tcp>
              <enabled>true</enabled>
              <port>9711</port>
              <incoming-status>true</incoming-status>
              <outgoing-status>true</outgoing-status>
            </tcp>
            <multicast>
            <enabled>false</enabled>
          </multicast>
          <use-only_relay_seeds>false</use-only_relay_seeds>
          <use-only_rendezvous_seeds>false</use-only_rendezvous_seeds>
          <seed-list>
            <my-seed1>RDV, tcp://localhost:9715</my-seed1>
          </seed-list>
        </properties>
      </network-configurator>
    </network-manager>
</jp2p-container>

As you can see, JP2P is an attempt to decouple the hassle of programming JXTA applications and turn it into a matter of configuration instead. Also, the often intimidating dependencies between JXTA services are hidden from the user as much as possible. A JXTA application is a sequence of activities that depend heavily on each other: first you must configure the network manager, then the configurator, start the network manager, get the net peergroup, start discovery, create advertisements...

In JP2P these dependencies are hidden behind the JP2P services. The mutual relationships are still there, of course, but in the container they are represented as a fairly clean list of services. Besides this, a service implementer will only have to go through the deep and shady crevices of JXTA once, and share the results with the community, who then only will have to concern themselves with the necessary configuration issues. As a long-term user of JXTA, this is what I really missed in all these years!

As a demonstration of this different perspective, take a look at the JP2P Navigator Tree above. The net peergroup has become a separate service under the container root. Even though this peergroup is tightly connected to the network manager in JXTA (through the startNetwork() method, this does not show in the JP2P container. This implementation choice is hidden in the configuration.

While we're at it, it may be worthwhile to go through the XML configuration file in greater detail. The properties section of the container speaks for itself. Then three services are defined:

  1. The startup-service takes care of activating the container. The auto-start directive tells the application to start as soon as possible. Concretely, this means that the network manager service will perform a 'startNetwork' once it is configured.

  2. The persistence-service is going to take care of all the properties that need to be persisted, particularly the peer- and peergroup ids that you need to create once, after which you want to use them every time you restart the application. The context directive tells the parser to select the persistency provided by OSGI. By default, the application will create a properties file in the home-folder for this functionality.

  3. A logger-service is added to show some messages about the creation and activation of the JP2P services. Currently it merely displays a number of system.out messages about the progress, and you can see them as the black log messages in the console. This will be covered in greater detail in the next of this series of tutorials

Note that these services are all independent of JXTA. A JP2P service is not in any way related to the JXTA libraries. In fact, the core functionality is provided by the net.jp2p.container package, which only has dependencies with JAVA classes. JXTA functionality is provided by the net.jp2p.jxta bundle, which is a so-called fragment with the libraries needed to include JXTA services in the JP2P container. If this fragment is not included, then the parser will not recognise the network-manager and network-configurator service.

These services are fairly self-explanatory for those who have worked with JXTA before, or who are reading the book Practical JXTA II. The seed-list section of the network configuration service deserves a bit of attention, though. This service holds a list of key-value pairs, of which the first is a choice between rendezvous (RDV) or relay (RELAY), and the latter holds a URL. In the example above, only one of these is provided, and it points to the port of Jack the Rendezvous! If Jack the Rendezvous is the only active bundle then your console will eventually end up spawning a single recurrent message:

New Seeding...
Jan 29, 2014 11:59:20 PM net.jxta.logging.Logging logCheckedInfo
INFO: Line 995 net.jxta.impl.rendezvous.rpv.PeerView.seed()

If both bundles are activated, you should see a bit more activity:

INFO: Line 127 net.jxta.impl.pipe.NonBlockingWireOutputPipe.<init>()
Constructing for urn:jxta:cbid-59616261646162614E50472050325033F078673BFEEC42949856BDD37C5DDA1804
Jan 30, 2014 12:01:22 AM net.jxta.logging.Logging logCheckedInfo
INFO: Line 1635 net.jxta.impl.rendezvous.rpv.PeerView.openWirePipes()
Propagate Pipes opened.

If you see something like this, then you have successfully made contact between two peers!

The only problem is, that you do not have any hook for the functionality you want to add yourself. This hook can be obtained by adding an observer to the container. Create a class called ContainerObserver in your bundle (s):

package <your.bundle.name>;

import net.jp2p.container.component.ComponentChangedEvent;
import net.jp2p.container.component.IComponentChangedListener;

public class ContainerObserver implements IComponentChangedListener {

  public ContainerObserver() {
    System.out.println( this.getClass().getName() + ": " +
      "Starting to Observe.");
  }

  @Override
  public void notifyServiceChanged(ComponentChangedEvent event) {
    System.out.println( "OBSERVING: " + this.getClass().getName() + ": " +
      event.toString() );
  }
}

The observer can be included in the start method of your activator:

@Override
public void start(BundleContext bundleContext) throws Exception {
   super.setObserver( new ContainerObserver() );
   super.start(bundleContext);
   activator = this;
}

If you relaunch your application, the observer will start to spawn messages when JP2P services are added to (or removed from) the container.

Conclusion

This tutorial has introduced the JP2P container as a means to make a shift from programming JXTA applications to configuring them. A lot of the implementation details are hidden for the implementer of a JXTA application, who only needs to understand the services and the properties that can be included. Currently a lot of functionality still needs to be implemented, but the container can already be used, as developers can hook into the code at any given point and extend or modify the provided functionality.

The next tutorial will demonstrate how you can develop your own services (or libraries).

Download Modern Java EE Design Patterns: Building Scalable Architecture for Sustainable Enterprise Development.  Brought to you in partnership with Red Hat

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}