UberNet : The Infospheres Network Layer User Guide

46
¨ UberNet: The Infospheres Network Layer User Guide 1 Daniel M. Zimmerman Computer Science 256-80 California Institute of Technology Pasadena, California 91125 [email protected] Version 1.0a1 11 February 1998 1 ¨ UberNet, its specification, implementation and documentation are Copyright c 1997-98, Califor- nia Institute of Technology. All rights are reserved. This work is supported in part by the Air Force Office of Scientific Research under grant AFOSR F49620-94-1-0244, by the CISE directorate of the Na- tional Science Foundation under Problem Solving Environments grant CCR-9527130, by the Center for Research in Parallel Computing under grant NSF CCR-9120008, by Parasoft and Novell Corporation, and by an NSF Graduate Research Fellowship. For more Infospheres information, please visit our web site at http://www.infospheres.caltech.edu/.

Transcript of UberNet : The Infospheres Network Layer User Guide

UberNet: The Infospheres Network LayerUser Guide1

Daniel M. ZimmermanComputer Science 256-80

California Institute of TechnologyPasadena, California [email protected]

Version 1.0a111 February 1998

1UberNet, its specification, implementation and documentation are Copyright c© 1997-98, Califor-nia Institute of Technology. All rights are reserved. This work is supported in part by the Air ForceOffice of Scientific Research under grant AFOSR F49620-94-1-0244, by the CISE directorate of the Na-tional Science Foundation under Problem Solving Environments grant CCR-9527130, by the Center forResearch in Parallel Computing under grant NSF CCR-9120008, by Parasoft and Novell Corporation,and by an NSF Graduate Research Fellowship. For more Infospheres information, please visit our website at http://www.infospheres.caltech.edu/.

Contents

1 Introduction 11.1 Welcome! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Supported Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.3 What You Need to Use UberNet . . . . . . . . . . . . . . . . . . . . . . . . . . 21.4 Where is UberNet Heading? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.5 Installing UberNet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.6 Demonstration Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.7 Known Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.8 Credits & Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 UberNet Concepts 72.1 UberNet: A Communications Framework . . . . . . . . . . . . . . . . . . . . . . 72.2 The UberNet Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2.1 Addresses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.2.2 Message Events and Listeners . . . . . . . . . . . . . . . . . . . . . . . . . 82.2.3 Error Events and Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.2.4 Outgoing Messages and Customizable Protocol Stacks . . . . . . . . . . . . 92.2.5 Incoming Messages and On-The-Fly Protocol Stack Generation . . . . . . 92.2.6 Wire Protocol Daemons . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.3 Reusable Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.3.1 Pooled Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.3.2 Pooled Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3 Standard UberNet Components 133.1 Wire Protocol Daemons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.1.1 TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.1.2 UDP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.1.3 IP Multicast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.1.4 Loopback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.2 Protocol Stack Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.2.1 Inbox & Outbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.2.2 Ordering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

i

ii CONTENTS

3.2.3 Segmenting & Joining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.2.4 Reliable Ordering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.2.5 Simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4 Programming with UberNet 194.1 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194.2 Use of UberNet for Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.2.1 Sending Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204.2.2 Receiving Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

4.3 Finalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

5 Extending UberNet 255.1 Writing Protocol Stack Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

5.1.1 Sending-Side Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255.1.2 Receiving-Side Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

5.2 Writing Wire Protocol Daemons . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

6 Example Programs 416.1 TCPPingReceiver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416.2 UDPPingReceiver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416.3 TCPTimingSender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426.4 UDPTimingSender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426.5 ClassLoaderTester . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

Chapter 1

Introduction

1.1 Welcome!

Welcome! This is the README and User Guide introduction for the first public alpha release ofUberNet, the Infospheres Network Layer. UberNet is a framework which provides commu-nication services to a Java Virtual Machine, with the following features:

• Built-in support for multiple low-level (wire) communication protocols.

• Dynamic customizable protocol stacks for outgoing messages.

• On-the-fly construction of protocol stacks for incoming messages, including dynamic loadingof protocol modules from remote sites to handle new protocols.

• Support for dynamic addition of new protocol stack modules for message processing.

• Support for dynamic addition of new low-level (wire) communication protocols (not com-plete in alpha 1 release).

With UberNet, a developer can create distributed object systems which communicate witheach other using arbitrary custom protocols over the Internet. The framework is written in Java,allowing applications on multiple platforms to easily communicate with each other and therebyallowing cross-platform distributed systems development.

1.2 Supported Platforms

UberNet uses many APIs which were added to Java in version 1.1. As a result, it will not workwith any Java Virtual Machines which are not compliant with Java 1.1 or later. This release ofUberNet has been successfully tested on the following Java virtual machines as of 1 February1998:

• JavaSoft JDK 1.1.5 on Windows NT 4.0

1

2 CHAPTER 1. INTRODUCTION

• JavaSoft JDK 1.1.5 on Solaris SPARC 2.X, both native and green threads

• JavaSoft JDK 1.1.5 on Solaris x86 2.X, both native and green threads

• JavaSoft JDK 1.2b1 on Solaris SPARC 2.X, both native and green threads

• Apple Mac OS Runtime for Java 2.0 on Mac OS 8.X

• Rhapsody Developer Release 1 Java Runtime

There are no Java 1.1 compliant Virtual Machines on which this release of UberNet isknown not to work.

We would like to test the UberNet alpha release on other platforms as well; please reportany experiences running on platforms not listed here to [email protected].

1.3 What You Need to Use UberNet

To use the UberNet framework, you need a Java 1.1 compliant Java Developer’s Kit. SunMicrosystems provides the JDK free of charge for SPARC Solaris, x86 Solaris, Windows NT andWindows 95. Apple Computer provides the Mac OS Runtime for Java Software DevelopmentKit free of charge for Mac OS 8.X. Other ports are available from other sources on the Internetor through software vendors.

UberNet can also be used in concert with version 2.0 of the Infospheres Infrastructure(II), a distributed system framework which provides many advanced features such as dynamicremote method invocation on arbitrary Java objects, object persistence, and more. UberNetis a “drop-in” replacement for the communications architecture shipped with the II distribution,which retains compatibility with the II’s RMI-based communication protocol while adding fasterand more efficient communication capabilities.

1.4 Where is UberNet Heading?

Because UberNet is a modular system, there are many possibilities for future expansion. Somepotential future protocol modules include support for IIOP as a wire protocol, virtual synchronysupport and reliable multicast support.

In a future (beta) release, UberNet protocol stacks will support manipulation and use fromwithin Java Beans-compliant visual development tools, such as Sun’s “Java Studio”. All protocolmodules are already complant with the Java Beans specification, but certain hooks which wouldallow them to easily be used by Beanbox tools have not yet been added.

Other features planned for future (probably alpha 2 or beta 1) releases are:

• Support for multiple TCP/UDP/MulticastDaemons within the same VM.

• Support for multi-homing machines (ie: integrated support for machines with multiple IPaddresses and/or network interfaces).

1.5. INSTALLING UBERNET 3

• Standardized interface to low-level protocol daemons through NetworkLayer control class(ie: instead of TCPDaemon.getInstance(), one would call something like NetworkLayer.get-ProtocolDaemon(TCP, InetAddress, port)).

Still more features, which may or may not be included in a future release, but which are beingconsidered:

• Elimination of the ReceivingEventRouter.

• Versioning of protocol stack modules in line with JavaSoft’s versioning schemas, and thepossibility of running multiple versions of the same module simultaneously.

If you have suggestions for additional functionality, or comments about the above proposedchanges, please send them to us at [email protected].

1.5 Installing UberNet

Step 1: Unpack Unpack the UberNet archive using your preferred tools. Under Windows95 or NT, use any program which can unpack ZIP archives, but be sure that it supports long filenames. Under Mac OS, just double-click on the self-extracting archive. Under UNIX, unpackingis usually accomplished with a command line like the following:

zcat UberNet1.0a1.tar.gz | tar xpvf -

Step 2: Set the CLASSPATH You must make sure that your default CLASSPATH is set suchthat the UberNet classes are included. Specifically, your CLASSPATH must include the twoincluded JAR files, UberNet1.0a1.jar and InfospheresCommon.jar. These are located in thelib/ directory of the unpacked UberNet archive.

Under UNIX, this is done by modifying your shell startup (.cshrc, .bashrc, .login, or theequivalent) and including a line which sets CLASSPATH to the appropriate value. Under WindowsNT, Windows 95, and Mac OS, follow the directions for your specific Java implementation.

These two JAR files contain all the UberNet classes, including the demonstration programs,so this CLASSPATH change is the only setup task required to use UberNet(assuming you alreadyhave a properly-configured Java implementation on your machine).

Building UberNet Applications We do not ship the source code for the bulk of the UberNetpackage. We do provide source for the example/demonstration programs (the classes in packagesedu.caltech.cs.infotest.*). You may wish to examine the source code of the example/demonstrationprograms to learn about building UberNet applications. Full documentation for UberNet pro-grammers is located in Chapter 4, Programming With UberNet, and in Chapter 5, ExtendingUberNet.

4 CHAPTER 1. INTRODUCTION

1.6 Demonstration Examples

To take UberNet for a test drive, follow the directions in Chapter 6, Example Programs.

1.7 Known Problems

There is currently one known bug in the package – apparently, the UDPTimingSender test programdoes not work properly under the Mac OS Runtime for Java, though it does work fine underother Java runtimes we have tried. As this is an alpha release, however, it is highly unlikely thatthis is the only bug. As with all alpha software, proceed with caution - do not use UberNetfor mission-critical applications such as air traffic control or nuclear power plant management.

Reporting Bugs The more information we have, the more likely it is that we can quickly fix thebug. Please supply all of the following information and mail it to [email protected] sure you provide all the below information with your bug report.

1. Operating System and Version

2. Java Port and Version

3. UberNet Version

4. Example Code (if applicable)

5. Exception/Thread Trace (if applicable)

6. Comments

7. Contact Information

1.8 Credits & Acknowledgements

UberNet, while primarily implemented by one person, incorporates the input of many presentand former members of the Caltech community. The original idea to “build a Beans-basedcommunication layer” was suggested by Jonathan Aldrich, who contributed greatly to the initialdesign and implementation work. Professor K. Mani Chandy and graduate student JosephKiniry also contributed much to the design of UberNet, as well as providing feedback on earlyimplementation efforts. Others who have contributed to this work include graduate studentsAdam Rifkin, Eve Schooler, Paul Sivilotti and Roman Ginis, and post-doctoral scholar JohnThornley.

1.8. CREDITS & ACKNOWLEDGEMENTS 5

Sponsors The Caltech Infospheres Project is sponsored by the Air Force Office of ScientificResearch (grant AFOSR 91-0070), the CISE directorate of the National Science Foundationunder Problem Solving Environments (grant CCR-9527130), the Center for Research in ParallelComputing (grant NSF CCR-9120008), and the Parasoft and Novell Corporations. This work hasalso been partially supported by a National Science Foundation Graduate Research Fellowship.

Contacting Us If you have any comments, questions or suggestions about UberNet, contactus at [email protected].

Copyright Notice UberNet: The Infospheres Network Layer — Copyright c© 1997-98 Cali-fornia Institute of Technology. All Rights Reserved. All material contained within the UberNetpackage is protected by the copyright laws of the United States and international treaties.

By downloading this software, you agree to the following conditions:

1. You may not redistribute any of this software or documentation without obtaining explicitpermission from the California Institute of Technology.

2. You may not use this software for any commercial purpose without obtaining explicitpermission from the California Institute of Technology.

3. The California Institute of Technology shall not be obliged to provide support for thissoftware.

6 CHAPTER 1. INTRODUCTION

Chapter 2

UberNet Concepts

In this chapter, we explore the general principles underlying the design of UberNet. Unless youare only using UberNet as a drop-in replacement for the Infospheres Infrastructure networkingcode and are not interested in actually developing UberNet-specific applications, this sectioncontains information which will be important to you.

2.1 UberNet: A Communications Framework

UberNet is a communications framework which allows objects in multiple Java Virtual Ma-chines to communicate with each other using the Internet. It provides the following features andcapabilities, each of which is described in detail in later sections and/or chapters:

• Dynamic customizable protocol stacks for outgoing messages (Section 2.2.4).

• On-the-fly construction of protocol stacks for incoming messages, including dynamic loadingof protocol modules from remote sites to handle new protocols (Section 2.2.5).

• Built-in support for multiple low-level (wire) communication protocols (Section 2.2.6).

• Support for addition of new protocol stack modules for message processing (Section 5.1).

• Support for addition of new low-level (wire) communication protocols (not complete inalpha 1 release) (Section 5.2).

With UberNet, a developer can create distributed object systems which communicate witheach other using arbitrary custom protocols. Since UberNet is written in Java, it allows forcross-platform development and deployment of distributed communicating objects.

7

8 CHAPTER 2. UBERNET CONCEPTS

2.2 The UberNet Architecture

The architecture of UberNet revolves around the ideas of dynamic modular communicationprotocol stacks and message streams. The sender of a message creates a protocol stack (or reusesan existing protocol stack), consisting of any number of protocol stack modules and a low-level(wire) communication protocol. Each component of the protocol stack is a Java Bean, andthe protocol stack components communicate with each other using the Java event model withthe interfaces provided by UberNet. By taking advantage of Java’s I/O stream interfaces, inwhich filtering streams can be overlaid atop existing streams, each Bean can add its own filteringstream to a message, allowing arbitrary encoding and processing of the message data in a nearlytransparent fashion.

One example of such a protocol stack is the following: Suppose you want to use UDP asa low-level transport, but also want reliable ordered messaging. You would then construct aprotocol stack with the UDP transport at the bottom, a reliability module above it, an orderingmodule above the reliability module, and any number of modules above that which format themessage in whatever way you decide is appropriate.

We now give a brief overview of each important part of the UberNet architecture.

2.2.1 Addresses

A virtual machine within which UberNet is running has an address, consisting of the host-name/IP address at which the virtual machine is running and the port number on whichUberNet’s standard unicast protocols are listening. Because addresses are used so frequently,there is a class, Address, which encapsulates them. The Address class is used to indicate thedestination and source of every message sent with UberNet, to designate inboxes to whichmessages should be sent, and more. Address objects will be mentioned frequently in the remain-der of this document; in fact, wherever the word “address” appears from here forward, it can beassumed to be referring to the Address class unless explicitly stated otherwise.

2.2.2 Message Events and Listeners

On both the sending and receiving sides of any communication, each message is represented asan event conforming to the Java event model. On the sending side, the event is called a Message-SendingEvent, and on the receiving side, this event is called MessageReceivingEvent. Objects whichlisten for MessageSendingEvents must implement an interface called MessageSendingListener, andobjects which broadcast MessageSendingEvents must implement an interface called MessageSend-ingBroadcaster. Similarly, objects which listen for MessageReceivingEvents must implement aninterface called MessageReceivingListener, and objects which broadcast MessageReceivingEventsmust implement an interface called MessageReceivingBroadcaster.

A message event encapsulates a stack of Java streams. MessageSendingEvents encapsulateOutputStreams, and MessageReceivingEvents encapsulate InputStreams. By adding to the stackof streams in a MessageSendingEvent, one can arbitrarily process the outgoing message data -

2.2. THE UBERNET ARCHITECTURE 9

this is what MessageSendingListeners do (see Section 2.2.4). Likewise, by adding to the stack ofstreams in a MessageSendingEvent, one can arbitrarily process incoming message data - this iswhat MessageReceivingListeners do (see Section 2.2.5).

A message event also contains other information about the message. Every message has aMessageID, which contains a message number, a timestamp, the address of the message’s sender,and the address of the message’s destination. In addition, every MessageSendingEvent containsa MessageStatus object, which can be used to track the status of the outgoing message.

2.2.3 Error Events and Listeners

On the receiving side of a communication, any object can listen for error events (class edu.caltech.-cs.infospheres.net.ReceivingErrorEvent). Objects which listen for ReceivingErrorEvents must im-plement the interface edu.caltech.cs.infospheres.net.ReceivingErrorListener. By listening for errorevents, objects can discover the MessageIDs of incoming messages which cause exceptions to beraised and/or error conditions to be registered by receiving-side message processing.

2.2.4 Outgoing Messages and Customizable Protocol Stacks

A protocol stack in UberNet consists of an arbitrary number of Java Beans, each of which(except the one at the bottom) has the Bean below it in the stack registered as a MessageSend-ingListener. All Beans in a protocol stack except the top one must implement MessageSend-ingListener; all Beans except the bottom one must implement MessageSendingBroadcaster. Thebottom Bean should, in most cases, be a wire protocol daemon (Section 2.2.6).

When a MessageSendingEvent is initially constructed, it is given a reference to the Message-SendingListener at the top of the protocol stack which will be used to process it. During theconstruction of the MessageSendingEvent, each element of its protocol stack is given the oppor-tunity to perform initialization functions on the message, such as adding FilterOutputStreamsto the stack of streams within the MessageSendingEvent, adding headers to the message, andwriting any listener-specific data to the message1.

Once the MessageSendingEvent has been constructed, a DataOutputStream connected tothe top stream in the MessageSendingEvent’s stack can be used to write data to the message;alternatively, the top stream itself can be used directly. After all the data has been written tothe message, invocation of the send() method on the MessageSendingEvent causes the messageto be sent. Each MessageSendingListener gets a chance to process the message again before itreaches the bottom of the stack and is (in most cases) sent over the network.

2.2.5 Incoming Messages and On-The-Fly Protocol Stack Generation

When a message arrives from the network, it is converted to a MessageReceivingEvent andprocessed. Since a receiving-side program has no way to know exactly what outgoing protocol

1Actually, this is not strictly the case - pre-processing of MessageSendingEvents is more complex than this,and will be discussed in more detail in Chapter 5.

10 CHAPTER 2. UBERNET CONCEPTS

stack has processed any given incoming message, there are no real protocol stacks on the receivingside. Instead, receiving-side protocol stacks are “virtual”, generated on the fly by an object calledthe ReceivingEventRouter.

After a MessageReceivingEvent is constructed from the incoming message, it is sent to theReceivingEventRouter, which reads the headers on the message to determine what the firstprotocol stack element should be. It first checks to see if it has such a handler locally, and ifit cannot find it locally, it attempts to load the handler’s bytecode from a URL contained inthe headers and instantiate the handler. Once the handler has been instantiated, the event ispassed to it and it does whatever processing is required. It then either returns the event tothe ReceivingEventRouter (which then reads the next set of headers and finds the next stackelement) or, if it is the last element of the receiving stack (corresponding to the top elementof the sending stack), performs whatever final message delivery tasks are appropriate (such asputting the decoded message in a queue).

This dynamic construction of protocol stacks allows a freshly unpacked UberNet distribu-tion to successfully receive messages from any other UberNet system with no need for specialconfiguration, and is one of UberNet’s most powerful features.

2.2.6 Wire Protocol Daemons

A wire protocol daemon is an object responsible for converting MessageSendingEvents to outgo-ing network transmissions and/or converting incoming network transmissions into MessageRe-ceivingEvents. Every wire protocol daemon (hereafter referred to as simply “daemon”) imple-ments the WireProtocolDaemon interface, in addition to any interfaces needed for listening toMessageSendingEvents and/or broadcasting MessageReceivingEvents. This allows UberNet todynamically locate and load newly-installed daemons automatically (this feature is not enabledin the alpha release).

Most daemons are single-instance classes, meaning that there can only be one instance of theclass in the virtual machine at any given time. Since there can only be one running UberNetframework in a given VM, this is a reasonable thing. Regardless of whether a given daemonis single-instance or not, UberNet will only create and use a single instance of each protocoldaemon class it finds.

Typically, a sending protocol stack will have a daemon as its bottom element.

2.3 Reusable Elements

UberNet incorporates many novel Java constructs which have the potential for reusability inother projects (some of these constructs, written originally for UberNet, have already founduses in version 2.0 of the Infospheres Infrastructure). This section briefly introduces them anddescribes their functionality.

2.3. REUSABLE ELEMENTS 11

2.3.1 Pooled Streams

Allocation of streams, especially ObjectInputStreams and ObjectOutputStreams, is an expensiveoperation. When working with a system like UberNet, which uses new streams for every singlemessage, the overhead of creating and destroying all the required byte array streams and objectstreams can be almost crippling. To reduce this overhead, UberNet uses special “pooled”extensions of critical stream types.

The classes PooledByteArrayInputStream, PooledByteArrayOutputStream, PooledObjectInput-Stream and PooledObjectOutputStream are extensions of standard Java stream classes. They arenot instantiated using standard constructors, but rather by requesting them from a PooledByteAr-rayStreamSource or PooledObjectStreamSource. They are then returned to the stream source whenthey are no longer needed, and the stream source “recycles” them so they can be used again.

It is important to note that the serialization format used by the pooled object streams is notcompatible with standard object streams. It incorporates mechanisms for including the bytecodeof serialized classes, with special treatment for classes which implement the RemoteLoadableinterface (which allows a class to specify a URL from which its bytecode can be loaded).

2.3.2 Pooled Threads

Pooled threads are a mechanism which reduces the overhead incurred by the creation of threads.A pool of threads is obtained by instantiating a ThreadPool object. A maximum number ofthreads, as well as the default priority for each thread, can be specified. The thread pool isthen used by enqueuing Runnable tasks, which are executed on available threads in the pool inthe order in which they are enqueued. Up to the specified maximum number of tasks can runconcurrently.

While pooled threads can greatly reduce overhead, the use of thread pools with a presetmaximum number of threads can lead to deadlock if care is not taken in their use.

12 CHAPTER 2. UBERNET CONCEPTS

Chapter 3

Standard UberNet Components

In this chapter, we present the standard components included in the UberNet package, de-scribing their functionality and use.

3.1 Wire Protocol Daemons

UberNet includes 4 wire protocol daemons, which enable communication via TCP, UDP, IPMulticast, and Loopback1. A future (probably beta) release will include support for communica-tion via Java RMI, for “drop-in” compatibility with version 2.0 of the Infospheres Infrastructure.

3.1.1 TCP

The TCPDaemon is the wire protocol daemon which enables communication via the TCP/IPprotocol. It handles both incoming and outgoing messages, and therefore implements both theMessageSendingListener and MessageReceivingBroadcaster interfaces.

TCP has the advantage that it provides reliability and ordering of messages for free. However,this advantage comes at the price of overhead incurred by setting up (and tearing down) connec-tions and by the TCP/IP protocol itself. Since TCP is connection-based, the TCPDaemon mustopen and maintain connections to remote TCPDaemons in other UberNet systems. There isa specified (user-configurable) number of connections which can be open at any one time, sothe TCPDaemon closes the least recently used connection when a new connection which exceedsthis maximum number is needed. In this way, the connection overhead incurred by hosts whichcommunicate with each other frequently is minimized.

There can be only one TCPDaemon running per Java Virtual Machine. It is instantiated byUberNet at network layer startup, and references to it can be obtained using the static methodTCPDaemon.getInstance().

1The LoopbackDaemon is not present in the alpha 1 release.

13

14 CHAPTER 3. STANDARD UBERNET COMPONENTS

3.1.2 UDP

The UDPDaemon is the wire protocol daemon which enables communication via UDP/IP data-grams. Like the TCPDaemon, it handles both incoming and outgoing messages, and implementsboth the MessageSendingListener and MessageReceivingBroadcaster interfaces.

UDP is a connectionless protocol, and so it offers the advantage of speed. Since it is notnecessary to establish a connection with each destination host, not to mention keeping trackof open connections, much of the overhead of TCP is not incurred by UDP. However, UDPdatagrams are neither reliable nor ordered, so extra steps must be taken to ensure that a messagesent via UDP actually reaches its destination. Protocol stack components which do exactly thisare provided with the UberNet distribution, and are described later in this chapter. UDPdatagrams sent by the UDPDaemon are limited to a maximum size of approximately 16 kilobytes,for efficiency reasons; a message segmenting protocol stack element is supplied so that thisrestriction does not limit the size of messages which can be sent using the UDPDaemon.

In addition to handling all incoming and outgoing UDP message traffic, the UDPDaemonalso handles all outgoing IP Multicast traffic, since IP Multicast packets are simply datagramssent to a multicast address.

There can be only one UDPDaemon running per Java Virtual Machine. It is instantiated byUberNet at network layer startup, and references to it can be obtained using the static methodUDPDaemon.getInstance().

3.1.3 IP Multicast

The MulticastDaemon is the wire protocol daemon which enables communication via IP Multicast.It handles only incoming messages directly, so it implements only the MessageReceivingBroad-caster interface, but it is also responsible for managing membership in multicast groups. It istightly integrated with the UDPDaemon.

IP Multicast allows single datagrams to be sent simultaneously to many listeners with asingle send operation, rather than a single send operation per datagram destination. This candramatically improve the efficiency of group communications, by reducing both the processingtime necessary on the sending side and the network bandwidth required per message. Theseefficiency improvements come at a price, however: like UDP, IP Multicast provides no guaranteesabout message reliability or ordering, and it is far more difficult to provide such guarantees ontop of IP Multicast than on top of UDP. There are no such facilities currently provided in theUberNet package.

UberNet currently provides support for multicast group membership, as detailed in thedescriptions of the top-level messaging components in Section 3.2.1. Additionally, instancemethods joinGroup() and leaveGroup() (on the currently-running MulticastDaemon, a referenceto which can be obtained using the static method MulticastDaemon.getInstance()) allow individualprotocol stack elements to join and leave multicast groups. The MulticastDaemon automaticallytracks how many requests have been made to join or leave each multicast group, and opens andcloses multicast sockets as necessary.

3.2. PROTOCOL STACK MODULES 15

3.1.4 Loopback

The LoopbackDaemon, while technically a WireProtocolDaemon, does not actually implementan on-the-wire protocol. It takes MessageSendingEvents and converts them to MessageRe-ceivingEvents on the same virtual machine. All destination addresses are treated identicallyby the LoopbackDaemon–a message addressed to mailbox “Valen” on some other VM is de-livered to mailbox “Valen” on the LoopbackDaemon’s VM (and, moreover, its MessageID ismodified to make it appear to have originally been addressed to the LoopbackDaemon’s VM).This allows for the efficient testing of “distributed” system configurations on a single virtualmachine, without actually needing to access a network interface.

3.2 Protocol Stack Modules

UberNet includes five pairs of protocol stack modules, one of which (SimpleSender and SimpleRe-ceiver) is a bare-bones example which does no actual message processing. Most of these, includingSimpleSender and SimpleReceiver, are in the edu.caltech.cs.infospheres.net.modules package, be-cause they are “optional” in the sense that one can use UberNet for message passing withoutever using them.

3.2.1 Inbox & Outbox

Inbox and Outbox are the provided “top level” protocol stack modules. They are located inthe edu.caltech.cs.infospheres.net package. Together, they allow the communication of arbitrarySerializable objects among multiple virtual machines.

An Outbox, when constructed, is assigned a unique (within its virtual machine) name, eitherfrom a parameter passed at construction, or automatically from a set of unique mailbox namesreserved by UberNet for internal use. Once created, an Outbox can be bound to an arbitrarynumber of inbox addresses (a special extension of Address which additionally contains an Inboxname), using one of various methods which are described in the accompanying API documen-tation. A message, which is in this case an arbitrary Serializable object, is sent by calling thesend() method on an Outbox, which causes a seperate copy of the message to be sent to eachinbox address to which the Outbox is bound at the time of the call. There is no reliability orordering built into the Outbox module–thus, there is no inherent guarantee that all addresseesof a message will receive it, nor that all addressees will receive multiple messages from the sameOutbox in the same order.

An Inbox, like an Outbox, is assigned a unique name at construction. Inbox and Outboxnames reside in different namespaces, so it is perfectly legal (if perhaps a bit confusing) to createan Inbox witih the same name as an already-existing Outbox, and vice-versa. An Inbox, oncecreated, receives all messages sent to it and places them in a first-in first-out queue. An objectwith a reference to an instance of Inbox can retrieve the first message in its queue by callingthe receive() method on it; this method returns the Serializable object and removes it from the

16 CHAPTER 3. STANDARD UBERNET COMPONENTS

queue. There are multiple forms of the receive() method, as well as other methods to obtaininformation about received messages; these are described in the API documentation.

Both Outbox and Inbox have integrated multicast support. Inbox has joinGroup() and leave-Group() methods which allow individual inboxes to join an arbitrary number of multicast groups(and thereby receive all messages sent to those groups). Outbox supports multicast groups trans-parently; since an Outbox can be bound to arbitrary addresses, binding an Outbox to a multicastaddress will result in messages being sent to the multicast group. Note that the Outbox must beusing the UDPDaemon as the bottom element of its protocol stack in order to send to multicastgroups, since a TCP transmission to a multicast group is not possible.

3.2.2 Ordering

The classes OrderingSender and OrderingReceiver, in the edu.caltech.cs.infospheres.net.modulespackage, implement the message ordering protocol stack module. Their function is to ensurethat messages arrive at a their destination in the order in which they are sent.

This protocol stack module is used simply by instantiating an OrderingSender and adding itto a protocol stack; no other configuration is required. It does have some customizable options,such as the timeout period after which a message will be declared lost, which are described inmore detail in the API documentation.

3.2.3 Segmenting & Joining

The classes SegmentingSender and JoiningReceiver, in the edu.caltech.cs.infospheres.net.modulespackage, implement message segmenting. Since UDP datagrams have a maximum size, messagesegmenting is necessary to allow the transmission of large messages. The segmenting moduledoes not require that segments be received in the order they were sent, but does require that allsegments be received; it will not reconstruct partial messages.

There are some customizable options, such as segment size and timeout period, which are de-scribed in more detail in the API documentation. However, the easiest way to use the segmentingmodule is just to include an instance of SegmentingSender in a protocol stack.

3.2.4 Reliable Ordering

The classes ReliableOrderingSender and ReliableOrderingReceiver, in the edu.caltech.cs.infospheres.-net.modules package, implement reliable ordered messaging. That is, they perform the functionsof the OrderingSender and OrderingReceiver, plus ensure that all messages sent get delivered.

The algorithm used by the reliable ordering module is a sliding window protocol with windowsize 1. It is, therefore, not an incredibly efficient way to ensure reliability. It is meant mainly asan example of how one might go about providing reliable messaging.

Because it is an acknowledgement-based algorithm, sliding window is inappropriate for mul-ticast applications; the reliable ordering module, therefore, ignores any messages addressed tomulticast groups, passing them through the stack with no additional processing.

3.2. PROTOCOL STACK MODULES 17

3.2.5 Simple

The classes SimpleSender and SimpleReceiver, in the edu.caltech.cs.infospheres.net.modules pack-age, are basically “no-operation” modules which do no message processing. They do, however,implement the basic functionality of sending-side and receiving-side modules, and so can be usedas base classes for other protocol stack modules. More details on writing protocol stack modulescan be found in Section 5.1.

18 CHAPTER 3. STANDARD UBERNET COMPONENTS

Chapter 4

Programming with UberNet

In this chapter, we describe how to incorporate the use of UberNet into your own programs.This chapter does not discuss the writing of protocol stack modules or wire protocol daemons -that discussion is in Chapter 5, Extending UberNet.

4.1 Initialization

Before a program can use the facilities provided by UberNet, it must start UberNet sothat initialization tasks can be performed and wire protocol daemons can start running. Acontrol class, NetworkLayer, is provided for the purpose of making this task, and other UberNetadministration tasks, easier for the programmer.

To initialize UberNet the static method NetworkLayer.start() must be called. This methodwill start all the wire protocol daemons and the ReceivingEventRouter, making it possible tosend and receive messages. An optional integer parameter can be used to specify a port numberon which the UDPDaemon and TCPDaemon should listen for messages. If this port is alreadyin use, an exception is thrown; if no port is specified, the UDPDaemon and TCPDaemon willlisten on a random port. In either case, the UDPDaemon and TCPDaemon always listen on thesame port number.

The start() method returns an object of class java.security.Key. This is the administrationkey for the UberNet instance which has been started, and it should be kept for use in lateradministrative commands. If start() is called while UberNet is already running, it returns anull key, to prevent any random object from obtaining the administration key.

4.2 Use of UberNet for Messaging

Once UberNet has been initialized, the various classes included in the package can be used tosend and receive messages. This section describes how to send and receive messages using theOutbox and Inbox classes.

19

20 CHAPTER 4. PROGRAMMING WITH UBERNET

4.2.1 Sending Messages

To send messages to a listening object elsewhere on the network, a sending protocol stack con-sisting of an Outbox, a wire protocol daemon, and an arbitrary number of protocol stack modulesmust be constructed. The constructor for Outbox, as for all other MessageSendingBroadcasters,takes a MessageReceivingListener as a parameter. This MessageReceivingListener is the nextelement of the protocol stack. The name of the Outbox can also be specified in the constructor,or a unique Outbox name can be automatically generated. For example, to declare a namedOutbox which used TCP for communication, we would write1:

Outbox myOutbox = new Outbox("Sheridan", TCPDaemon.getInstance());

This creates an Outbox named “Sheridan” with the currently-running TCPDaemon as thenext listener in its protocol stack – the protocol stack in this case has exactly 2 elements. Amore complex protocol stack might be created as follows:

ReliableOrderingSender reliableOrderingSender =

new ReliableOrderingSender(UDPDaemon.getInstance());

SegmentingSender segmentingSender =

new SegmentingSender(reliableOrderingSender);

Outbox myOutbox =

new Outbox("Delenn", segmentingSender);

This creates an Outbox named “Delenn” with a protocol stack consisting of a Segment-ingSender, followed by a ReliableOrderingSender, followed by the currently-running UDPDae-mon – a protocol stack which enables reliable ordered message transfer using UDP datagrams.Of course, this same protocol stack could have been created in one statement, constructing theprotocol stack as a parameter to the Outbox constructor, but it’s less confusing this way.

It would also have been perfectly OK for us to have added the line

Outbox mySecondOutbox = new Outbox("Lennier", segmentingSender);

in order to share the protocol stack elements between two Outboxes. All of the protocolstack elements provided with UberNet can be shared in this way, but the documentation ofexternally-provided protocol stack elements should be checked to see whether or not they supportthis style of use.

Now that we’ve created an Outbox, we need to tell it where to send messages. We couldhave done so on construction, but in this case we chose not to, so we must use some variant ofthe bind() method to do so. Let’s assume that we know there is an Inbox named “Kosh” onport 8888 of host “babylon.caltech.edu”, and we want to send it a message. We can do so in thefollowing way:

1In all code samples, we omit necessary try...catch constructs, such as those required when obtaining instancesof java.net.InetAddress, because they are not critical to understanding the operation of the code samples.

4.2. USE OF UBERNET FOR MESSAGING 21

myOutbox.bind

(new InboxAddress(InetAddress.getByName("babylon.caltech.edu"),

8888, "Kosh");

MessageStatus messageStatus =

myOutbox.send(new String("I’m going to Z’ha’dum"));

We can send a String as a message, because String implements the Serializable interface(recall that Outbox can only send arbitrary Serializable objects as messages). We now have aMessageStatus object which allows us to check and see if the message got through to the otherside of the network (if we were using an unreliable protocol, we would only be able to check andsee if the message was successfully sent over the network – the difference is a very importantone). The MessageStatus object can not tell us whether or not the message was delivered to thecorrect mailbox on the other side, however.

If we now want to bind the Outbox to some other destination, we can use the rebind() method.For example:

myOutbox.rebind

(new InboxAddress(InetAddress.getByName("babylon.caltech.edu"),

8888, "Morden"));

If we had used the bind() method again, we would now be bound to both “Kosh” and “Mor-den”. Both the bind() and rebind() methods support are polymorphic, and their other formsare described in the API documentation. There is also an unbind() method, which allows theOutbox to be unbound from one or more of its destinations. In fact, the rebind() method isfunctionally equivalent to a unbind() call with no arguments (which unbinds the Outbox from allits destinations) followed by a bind() call.

The above discussion is basically all you need to know to create simple message passingapplications. For more information on special capabilities of the various sending protocol stackmodules, see their API documentation.

4.2.2 Receiving Messages

Receiving messages with UberNet is easier than sending them, because receiving protocol stacksare constructed on-the-fly from the message headers themselves. Therefore, they need not bespecified when Inboxes are created, nor even worried about at all. When a message arrives in anInbox, in fact, it looks the same regardless of the method by which it was transmitted over thenetwork.

The constructor for Inbox, like that for Outbox, allows the Inbox name to be specified atconstruction. Inbox names tend to be more useful than Outbox names, since an Inbox’s namemust be somehow known in order for Outboxes to bind to it, while an Outbox name is used onlyto see where a message came from. We can declare a named Inbox as follows:

Inbox myInbox = new Inbox("Kosh");

22 CHAPTER 4. PROGRAMMING WITH UBERNET

Once an Inbox has been constructed, there are two ways to receive a message which hasbeen sent to it. One, a blocking receive, will actually block the receiving thread if no messageis currently in the Inbox, and will not unblock until a message is received. This means thata blocking receive can potentially block forever. The second, a timed receive, will block thereceiving thread if no message is currently in the Inbox, and will remain blocked until either amessage arrives or a specified timeout period expires. Both types of receive are performed usingthe receive() method, as follows:

Serializable message = myInbox.receive(); // blocking

Serializable message = myInbox.receive(5000); // timed

If no message is received before the timeout of a timed receive expires, the return value ofthe receive() call is null. The timeout of a timed receive is specified in milliseconds.

Of course, we must cast the message to the appropriate Java class before performing complexoperations on it. In some cases, the message type will be known a priori; sometimes, though, itmust be discovered using reflection. If we were willing to risk the possibility of a ClassCastEx-ception at runtime, knowing that the only messages we were going to receive would be Strings(because we designed the system), we could just have easily written the following:

String message = (String) myInbox.receive();

After a message has been received, we can obtain information about it by getting its associatedMessageID from the Inbox. This can be done only before another receive is performed (theInbox only keeps track of the MessageID for the most recent message), using the getMessageID()method. Using information from the MessageID, we can send a reply to the message, as follows:

MessageID messageID = myInbox.getMessageID();

if (messageID.getSender().getMailboxName().equals("Sheridan"))

{

InboxAddress inboxAddress =

new InboxAddress(messageID.getSender(), "Sheridan");

String returnMessage =

new String("If you go to Z’ha’dum, you will die.");

myOutbox.rebind(inboxAddress);

myOutbox.send(returnMessage);

}

Recall that Inbox and Outbox have seperate namespaces, so it is quite legal for us to send ourreply to the Inbox named “Sheridan” on the same host and port as the Outbox named “Sheridan”,

4.2. USE OF UBERNET FOR MESSAGING 23

from which the original message came. To do this, we used a form of the InboxAddress constructorwhich takes an Address object and an Inbox name, and constructs the address of that Inbox on thehost/port of the original Address object. A similar constructor is available for OutboxAddress.

If we didn’t know, a priori, that there was an Inbox named “Sheridan” at that address, oreven that we wanted to send the reply to that address at all, we might explicitly include thereturn address with the message. One way to do this is to use the class java.util.Vector to createan aggregate message. The sending-side code might look like this:

Vector message = new Vector();

message.addElement(new String("I’m going to Z’ha’dum"));

message.addElement

(new InboxAddress(InetAddress.getByName("whitestar.caltech.edu"),

8888, "Sheridan"));

myOutbox.send(message);

And the receiving-side code like this:

Vector message = (Vector) myInbox.receive();

InboxAddress inboxAddress = (InboxAddress) message.elementAt(1);

String returnMessage =

new String("If you go to Z’ha’dum, you will die.");

myOutbox.rebind(inboxAddress);

myOutbox.send(returnMessage);

In addition to waiting for a message on a single Inbox, we can also wait for a message tocome in on a set of Inboxes. To do this, we use an InboxSelect object, as follows:

InboxSelect inboxSelect = new InboxSelect();

inboxSelect.add(myFirstInbox);

inboxSelect.add(mySecondInbox);

inboxSelect.add(myThirdInbox);

Serializable message = inboxSelect.receive();

MessageID messageID = inboxSelect.getMessageID();

Inbox selectedInbox = inboxSelect.getSelectedInbox();

This will return a message (and its corresponding ID) from one of the three Inboxes (we knowwhich Inbox from the getSelectedInbox() method). The example illustrates a blocking receive ona set of Inboxes, but timed receives work on sets of Inboxes as well, with syntax identical to thatof timed receives on a single Inbox.

24 CHAPTER 4. PROGRAMMING WITH UBERNET

4.3 Finalization

When an application is done using UberNet, either because it is terminating execution orbecause it has no further need for messaging services, it should shut down UberNet in anorderly fashion. This is important for several reasons, not the least of which is that, due to itsaggressively multithreaded nature, a running instance of UberNet adds unnecessary overheadin the virtual machine if it is not being used. Also, unless a System.exit() call is made, a JavaVM in which running threads still exist will not exit properly.

Other reasons one might shut down UberNet include putting new administrative settingsinto effect — many such settings do not take effect until a UberNet restart, and these aredescribed in the API documentation — or moving the messaging services to a different port.

To shut down UberNet simply call NetworkLayer.stop(), passing the administration Keyobtained at UberNet startup. Note that if you do not have the administration Key, you cannotshut down UberNet. When this call is made, all messaging services will stop immediately; anyoutgoing messages which have not yet been delivered will be discarded, and any messages whicharrive subsequently will be ignored. Therefore, the stop() call should be made only with theutmost care.

After UberNet has shut down completely, it can be started again with the start() method.This will generate a new administration Key and start all the daemons again. If new daemonshave been installed locally, they will be started as well. However, all classes (such as protocolstack modules) loaded from remote sites will be lost when UberNet shuts down. This maychange in a future version of UberNet.

Chapter 5

Extending UberNet

In this chapter, we describe how to extend UberNet to include new protocol stack modulesand wire protocol daemons. For a description of messaging with UberNet’s standard protocolstack modules and wire protocol daemons, see Chapter 4, Programming with UberNet.

5.1 Writing Protocol Stack Modules

Most extensions one might want to make to UberNet are in the form of protocol stack modules,which do arbitrary processing on messages before they are transmitted on the wire. In this versionof UberNet, protocol stack modules always come in pairs – every sending-side protocol stackmodule has a specific corresponding receiving-side protocol stack module. This may change inthe (somewhat distant) future, to allow a more semantic-based method of message processing.For now, every sending-side module knows the class name of its corresponding receiving-sidemodule, and provides a URL (hard-coded) from which the bytecode of the receiving-side modulecan be loaded at runtime.

5.1.1 Sending-Side Modules

Every sending-side module must implement the edu.caltech.cs.infospheres.net.MessageSending-Broadcaster interface. Most sending-side modules will also implement the edu.caltech.cs.info-spheres.net.MessageSendingListener interface, since most sending-side modules will not be suit-able for use as the top module in a protocol stack (Outbox is the only such module providedwith UberNet, though it is conceivable that others might be developed if messages other thanarbitrary Serializazble objects, or messaging schemes which do not involve the concept of “mail-boxes”, are desired). We now describe the methods in each of these interfaces and their purposes,and then work through an example implementation of a sending-side protocol stack module whichuses Java’s DeflaterOutputStream class to perform data compression.

25

26 CHAPTER 5. EXTENDING UBERNET

The MessageSendingBroadcaster Interface

The MessageSendingBroadcaster interface includes methods which add and remove event listen-ers, as well as a method which allows the UberNet framework to perform an orderly shutdown ofrunning MessageSendingBroadcasters when NetworkLayer.stop() is called. They are the following:

public void addMessageSendingListener(MessageSendingListener l)

throws TooManyListenersException

public void removeMessageSendingListener(MessageSendingListener l)

These methods add and remove MessageSendingListeners from the module’s list of listeners.Since protocol stacks in UberNet can only be linear, there is a limit of 1 MessageSendingLis-tener per module (and the method signature for addMessageSendingListener indicates that Mes-sageSendingEvent is a unicast event). The module can use any method it wishes to keep trackof its listeners.

public String getReceiverClassName()

public byte getReceiverClassVersion()

public String getReceiverClassURL()

These methods return information about the receiving-side module which corresponds to thissending-side module: its full class name (including package name), the minimum version (asa byte) of the receiving-side module which is required to successfully decode messages sent bythis sending-side module, and a URL pointing to a “.class”, “.jar” or “.zip” file containing thebytecode of the receiving-side module. These methods are not actually used by the UberNetframework directly, but rather comprise the required set of headers which must be added to amessage processed by this sending-side module so it can be properly processed at the receivingside.

public void stop()

throws SecurityException

This method is used by the UberNet framework to “clean up” running MessageSending-Broadcasters at network layer shutdown time. If the class of MessageSendingBroadcaster actuallyuses any system resources statically (such as a shared thread pool or Hashtable), these staticresources should be cleaned up when stop() is invoked on any running instance of the class.The stop() method should never be called by user code; therefore, the static method Network-Layer.isAuthorizedStop() should be called to verify that the stop() call is actually coming fromthe UberNet framework, and a SecurityException should be thrown if an unauthorized call isdetected.

5.1. WRITING PROTOCOL STACK MODULES 27

In addition to implementing these methods, it is recommended that all MessageSending-Broadcasters call NetworkLayer.addMessageSendingBroadcaster(this) in their constructors, so thatthey are registered with the UberNet framework and can be properly cleaned up at shutdowntime.

The MessageSendingListener Interface

The MessageSendingListener interface includes methods which process outgoing MessageSending-Events. They are the following:

public void setupMessage(MessageSendingEvent e)

throws IOException

This method is called on MessageSendingEvent construction, to allow each protocol stackmodule to add headers, output streams, and/or additional data to the message. In most cases,a MessageSendingListener which also implements MessageSendingBroadcaster should call thesetupMessage() method on its listener, so that headers are added to the message in the properorder (the headers from the bottom module should be added first, since the bottom module’scorresponding receiving-side module should be first to process the message when it gets to thereceiving side). After this call returns, the MessageSendingListener can do arbitrary processingon the message. Examples of what might be done in setupMessage() include constructing anew FilterOutputStream from the top stream of the MessageSendingEvent and adding it to theMessageSendingEvent, or “intercepting” the MessageSendingEvent (by not calling its listener’ssetupMessage() method at this time) in order to perform more complicated processing. TheReliableOrderingSender (described in Section 3.2.4) is an example of a MessageSendingListenerwhich intercepts MessageSendingEvents in this fashion.

public void sendMessage(MessageSendingEvent e)

throws IOException

This method is called after a MessageSendingEvent has been “finished” and its send() methodhas been called. The MessageSendingListener should do whatever final tasks are associated withthe message at this point, and then call sendMessage() on its listener. If the MessageSend-ingListener intercepted the MessageSendingEvent during the setupMessage() method, it shouldfirst call setupMessage() on its listener, and then call sendMessage() on its listener, so that theMessageSendingEvent is properly processed through the rest of the stack.

Example: A Compression Sending-Side Module

We now illustrate the concepts involved in constructing a sending-side protocol module, by build-ing a simple one from scratch. The code is presented here in full, excessively commented to illus-trate the concepts involved (javadoc comments are not included, however). Note that much of the

28 CHAPTER 5. EXTENDING UBERNET

functionality implemented here is already implemented in the class edu.caltech.cs.infospheres.net.-modules.SimpleSender, which can be used as a base class for custom sending-side protocol stackmodules quite easily.

package edu.caltech.cs.infospheres.net.modules;

// of course, your classes will be in a different package

import java.io.DataOutputStream;

import java.io.IOException;

import java.util.TooManyListenersException;

import java.util.zip.DeflaterOutputStream;

import edu.caltech.cs.infospheres.net.MessageSendingBroadcaster;

import edu.caltech.cs.infospheres.net.MessageSendingEvent;

import edu.caltech.cs.infospheres.net.MessageSendingListener;

public class CompressingSender

implements MessageSendingBroadcaster, MessageSendingListener

{

// Instance Variables

private MessageSendingListener nextListener = null;

// Constructors

public CompressingSender()

{

// no-argument constructor, for use by beanboxes. does nothing

// since listener is already initialized. since this class

// of broadcaster uses no system resources statically, there

// is no need to register it with the NetworkLayer

}

public CompressingSender(MessageSendingListener nextListener)

{

// constructor which sets the next listener initially.

// since this class of broadcaster uses no system resources

// statically, there is no need to register it with the

// NetworkLayer

this.nextListener = nextListener;

}

5.1. WRITING PROTOCOL STACK MODULES 29

// MessageSendingBroadcaster Interface Methods

public synchronized void

addMessageSendingListener(MessageSendingListener listener)

throws TooManyListenersException

{

// method is synchronized to prevent confusing outcomes such as

// two simultaneous invocations completing with no exception

// raised

if ((nextListener != null) && (nextListener != listener))

{

throw new TooManyListenersException

("There is already a listener assigned to this module.");

}

else

{

nextListener = listener;

}

}

public synchronized void

removeMessageSendingListener(MessageSendingListener listener)

{

// method is synchronized to ensure proper interleavings with

// addMessageSendingListener(). if the current listener is

// not the same as the specified listener, this method simply

// does nothing.

if (nextListener == listener)

{

nextListener = null;

}

}

public String getReceiverClassName()

{

// we will build the receiving class in the next section

30 CHAPTER 5. EXTENDING UBERNET

return "edu.caltech.cs.infospheres.net.modules.ExpandingReceiver";

}

public byte getReceiverClassVersion()

{

// version 0 or higher of the receiving class is required to

// properly decode our messages

return (byte) 0;

}

public String getReceiverClassURL()

{

// we will make the class available on the WWW

return "http://www.infospheres.caltech.edu/classes/Expanding.class";

}

public void stop()

{

// since we have no static resources, there’s nothing for us

// to do in this method. if we _did_ have static resources, we

// would first notify all instances of our class in some way

// that shutdown was imminent, and then dispose of the static

// resources. we would also check to make sure this was an

// authorized stop() call, with a block like:

//

// if (!NetworkLayer.isAuthorizedStop())

// {

// throw new SecurityException

// ("There is a hole... in your authorization.");

// }

}

// MessageSendingListener Interface Methods

public void setupMessage(MessageSendingEvent event)

5.1. WRITING PROTOCOL STACK MODULES 31

throws IOException

{

// first, we send the event to our next listener. if we don’t

// have one, we just go on. more robust handling of this

// situation may be desirable in some circumstances; this can

// involve the use of synchronized methods, wait loops, etc.

if (nextListener != null)

{

nextListener.setupMessage(event);

}

// now, we add the required headers to the message

DataOutputStream dataStream = event.getDataStream();

dataStream.writeUTF(getReceiverClassName());

dataStream.writeByte(getReceiverClassVersion());

dataStream.writeUTF(getReceiverClassURL());

// having added the required headers, we add a

// DeflaterOutputStream to the stack of streams in

// the event, to compress the data written to the

// message.

DeflaterOutputStream deflaterOutputStream =

new DeflaterOutputStream(event.getTopStream());

event.setTopStream(deflaterOutputStream);

// and now we’re done! compression will take place

// automatically when stack modules above us write to

// the message.

}

public void sendMessage(MessageSendingEvent event)

throws IOException

{

// we don’t need to do any final processing on the message.

// if we did, it would be done here. instead, we just pass

// the event to the next listener, if one exists. If no next

32 CHAPTER 5. EXTENDING UBERNET

// listener exists, we eat the event - as in setupMessage(),

// more robust handling of this situation might be called for

// in some circumstances.

if (nextListener != null)

{

nextListener.sendMessage(event);

}

}

// end of class definition

}

This example illustrates how simple it is to write sending-side protocol stack modules. Anydata processing which can be done by a Java FilterOutputStream can be done to a message withnothing more complex than the above code. For more sophisticated processing, of course, morework is likely to be necessary.

5.1.2 Receiving-Side Modules

Every receiving-side module must implement the edu.caltech.cs.infospheres.net.MessageReceiving-Listener interface. Most receiving-side modules will also implement the edu.caltech.cs.infospheres.-net.MessageReceivingBroadcaster interface, since most receiving-side modules will not be suit-able for use as the top module in a protocol stack. In addition, there is an optional interface,edu.caltech.cs.infospheres.net.ReceivingErrorBroadcaster, which allows a receiving-side module toreport receiving errors to interested listeners which have implemented edu.caltech.infospheres.net.-ReceivingErrorListener. We now describe the methods in each of these interfaces and their pur-poses, and then work through an example implementation of a receiving-side protocol stack mod-ule (corresponding to the example sending-side protocol stack module we built in Section 5.1.1)which uses Java’s InflaterInputStream class to uncompress data in a message.

The MessageReceivingListener Interface

The MessageReceivingListener interface includes a method which tells the module to processan incoming message, methods which allow the UberNet framework to get information aboutthe MessageReceivingListener, and a method which allows the UberNet framework to performan orderly shutdown of running MessageReceivingListeners when NetworkLayer.stop() is called.They are the following:

public void receiveMessage(MessageReceivingEvent e)

throws IOException

5.1. WRITING PROTOCOL STACK MODULES 33

This method tells the MessageReceivingListener that it should process the specified messageevent. The required headers (receiver name, receiver version, receiver URL) will already havebeen stripped from the message at this point, so the MessageReceivingListener can begin byreading any module-specific header information which may have been added to the message onthe sending side.

An important thing to remember is that a MessageReceivingListener is quite different from aMessageSendingBroadcaster, in that the UberNet framework itself only creates one instance ofeach MessageReceivingListener class it uses, while the entity constructing sending-side protocolstacks can create as many of a particular class of MessageSendingBroadcaster as it chooses. Thus,a MessageReceivingListener must itself handle the construction of multiple instances of itself, ifnecessary, and any single instance must be able to properly deliver a message to the instance forwhich it is meant. Inbox, for example, contains a static Hashtable of running Inbox instances,which allows it to route incoming messages to the correct Inbox based on header informationadded by Outbox on message send. Most receiving side modules, such as the one we will buildbelow, will not need to deal with this issue, as a single instance of them per VM will usually besufficient.

Once a MessageReceivingListener has processed a message, it should send it on to the nextMessageReceivingListener in the chain (assuming that it implements MessageReceivingBroad-caster and that the next listener has been set). This will usually (always, in MessageReceiv-ingListeners instantiated by the UberNet framework) be the ReceivingEventRouter, which willdetermine the next handler for the message and route it accordingly.

public boolean isStateless()

This method tells the UberNet framework whether or not this class of MessageReceivingLis-tener is stateless - that is, whether it maintains any state about prior messages which is used todeal with new incoming messages. If a class identifies itself as stateless, and a later version ofthe class is needed to process an incoming message, the class will be dynamically replaced by thelater version by the ReceivingEventRouter. If a class identifies itself as having state, however,it cannot be replaced in this fashion, as that might compromise communication of messages al-ready in transit. An example of a stateless MessageReceivingListener is the ExpandingReceiver,which we will develop below as a complement to our example CompressingSender; an exampleof a MessageReceivingListener which saves state is the OrderingReceiver, which ensures messageordering and is included in the UberNet package.

public String getClassName()

This method tells the UberNet framework the name of the class. In most circumstances, itcan be implemented using the method call this.getClass().getName(). The UberNet frameworkcompares the return value of this call to the class name in a message header to verify that agiven class is the correct handler class for the message.

34 CHAPTER 5. EXTENDING UBERNET

public byte getClassVersion()

This method tells the UberNet framework the class version. This number is compared withthe version number in the header of an incoming message to determine whether this MessageRe-ceivingListener can handle the message; if the returned version is greater than or equal to theversion number on the message, it is assumed that the class can handle the message successfully.

public String getClassURL()

This method tells the UberNet framework where it can obtain the bytecode for the latestversion of this class. It is used when a later version than the one currently loaded is required byan incoming message.

public void stop()

throws SecurityException

This method is used by the UberNet framework to “clean up” running MessageReceiv-ingListeners at network layer shutdown time. If the class of MessageReceivingListener actuallyuses any system resources statically (such as a shared thread pool or Hashtable), these staticresources should be cleaned up when stop() is invoked on any running instance of the class.The stop() method should never be called by user code; therefore, the static method Network-Layer.isAuthorizedStop() should be called to verify that the stop() call is actually coming fromthe UberNet framework, and a SecurityException should be thrown if an unauthorized call isdetected.

The MessageReceivingBroadcaster Interface

The MessageReceivingBroadcaster interface includes methods which add and remove event lis-teners. They are the following:

public void addMessageReceivingListener(MessageReceivingListener l)

throws TooManyListenersException

public void removeMessageReceivingListener(MessageReceivingListener l)

These methods add and remove MessageReceivingListeners from the module’s list of listen-ers. Since protocol stacks in UberNet can only be linear, there is a limit of 1 MessageReceiv-ingListener per module (and the method signature for addMessageReceivingListener indicates thatMessageReceivingEvent is a unicast event). The module can use any method it wishes to keeptrack of its listeners.

5.1. WRITING PROTOCOL STACK MODULES 35

The ReceivingErrorBroadcaster Interface

The ReceivingErrorBroadcaster interface includes methods which add and remove event listen-ers. Any object can implement the ReceivingErrorListener interface and register to receiveReceivingErrorEvents. These methods are the following:

public void addReceivingErrorListener(ReceivingErrorListener l)

public void removeReceivingErrorListener(ReceivingErrorListener l)

These methods add and remove ReceivingErrorListeners from the module’s list of listeners.There is no limit on the number of ReceivingErrorListeners which can be registered. The modulecan use any method it wishes to keep track of its listeners, and can send ReceivingErrorEventswhenever it is appropriate to do so.

Example: An Expansion Receiving-Side Module

We now illustrate the concepts involved in constructing a receiving-side protocol module, bybuilding a simple one from scratch. This receiving-side module will process messages which havebeen processed on the sending side by the CompressingSender we constructed in Section 5.1.1.The code is presented here in full, excessively commented to illustrate the concepts involved(javadoc comments are not included, however). Note that much of the functionality implementedhere is already implemented in the class edu.caltech.cs.infospheres.net.modules.SimpleReceiver,which can be used as a base class for custom receiving-side protocol stack modules quite easily.

package edu.caltech.cs.infospheres.net.modules;

// of course, your classes will be in a different package

import java.io.IOException;

import java.util.TooManyListenersException;

import java.util.zip.InflaterInputStream;

import edu.caltech.cs.infospheres.net.MessageReceivingBroadcaster;

import edu.caltech.cs.infospheres.net.MessageReceivingEvent;

import edu.caltech.cs.infospheres.net.MessageReceivingListener;

public class ExpandingReceiver

implements MessageReceivingBroadcaster, MessageReceivingListener,

{

// Instance Variables

private MessageReceivingListener nextListener = null;

// Constructors

36 CHAPTER 5. EXTENDING UBERNET

public ExpandingReceiver()

{

// no-argument constructor, for use by beanboxes. does nothing

// since nextListener is already initialized.

}

public ExpandingReceiver(MessageReceivingListener nextListener)

{

// constructor which sets the next listener initially.

this.nextListener = nextListener;

}

// MessageReceivingBroadcaster Interface Methods

public synchronized void

addMessageReceivingListener(MessageReceivingListener listener)

throws TooManyListenersException

{

// method is synchronized to prevent confusing outcomes such as

// two simultaneous invocations completing with no exception

// raised

if ((nextListener != null) && (nextListener != listener))

{

throw new TooManyListenersException

("There is already a listener assigned to this module.");

}

else

{

nextListener = listener;

}

}

public synchronized void

removeMessageReceivingListener(MessageReceivingListener listener)

{

// method is synchronized to ensure proper interleavings with

5.1. WRITING PROTOCOL STACK MODULES 37

// addMessageReceivingListener(). if the current listener is

// not the same as the specified listener, this method simply

// does nothing.

if (nextListener == listener)

{

nextListener = null;

}

}

// MessageReceivingListener Interface Methods

public void receiveMessage(MessageReceivingEvent event)

throws IOException

{

// the CompressingSender didn’t add any non-required headers,

// so we don’t need to read any. we first add an

// InflaterInputStream to the event’s stack of streams, to

// match the DeflaterOutputStream added by the CompressingSender.

InflaterInputStream inflaterInputStream =

new InflaterInputStream(event.getTopStream());

// now, we send the event to our next listener. if we don’t

// have a next listener, we just eat the event. more robust

// handling of this situation may be desirable in some

// circumstances; this can involve the use of synchronized

// methods, wait loops, etc.

if (nextListener != null)

{

nextListener.receiveMessage(event);

}

}

public boolean isStateless()

{

// this MessageReceivingListener is, in fact, stateless.

return true;

38 CHAPTER 5. EXTENDING UBERNET

}

public String getClassName()

{

// return this class’s name. this will work for subclasses

// as well, which will not have to override this method.

return this.getClass().getName();

}

public byte getClassVersion()

{

// return this class’s version, which happens to be 0

return (byte) 0;

}

public String getClassURL()

{

// we will make the class available on the WWW

return "http://www.infospheres.caltech.edu/classes/Expanding.class";

}

public void stop()

{

// since we have no static resources, there’s nothing for us

// to do in this method. if we _did_ have static resources, we

// would first notify all instances of our class in some way

// that shutdown was imminent, and then dispose of the static

// resources. we would also check to make sure this was an

// authorized stop() call, with a block like:

//

// if (!NetworkLayer.isAuthorizedStop())

// {

// throw new SecurityException

// ("You are not ready to call this method.");

// }

5.2. WRITING WIRE PROTOCOL DAEMONS 39

}

// end of class definition. since the only receiving errors we can

// experience are IOExceptions, we will let the framework which

// calls receiveMessage() deal with them.

}

This example illustrates how simple it is to write receiving-side protocol stack modules. Anydata processing which can be done by a Java FilterInputStream can be done to a message withnothing more complex than the above code. For more sophisticated processing, of course, morework is likely to be necessary.

5.2 Writing Wire Protocol Daemons

The mechanism for writing and adding new wire protocol daemons has not been finalized (oreven written) for version 1.0a1. This section will be updated to include instructions for writingnew wire protocol daemons for the next UberNet release.

40 CHAPTER 5. EXTENDING UBERNET

Chapter 6

Example Programs

The alpha 1 release of the UberNet package includes some testing and example programs,which allow you to see UberNet in action. In this chapter, we present these example programsand describe their operation. Future releases of UberNet will likely contain more, as well aslarger, example programs.

6.1 TCPPingReceiver

The TCPPingReceiver program (class edu.caltech.cs.infotest.net.TCPPingReceiver) is a simple testprogram which, when started up, listens on an Inbox named “Test Inbox” on a specified port.Whenever it receives a message on this Inbox, it prints it to the screen, and also sends it back toan Inbox named “Test Inbox” on the host from which it came using the TCP protocol. Though ituses the TCP protocol to send the replies, the TCPTimingSender is capable of receiving messagesvia any low-level protocol (and any protocol stack).

The TCPPingReceiver takes a single, optional command line argument – the port numberon which it is to listen. Thus, to start a TCPPingReceiver on port 8888, one would type thecommand line:

java edu.caltech.cs.infotest.net.TCPPingReceiver 8888

If the port parameter is omitted, the TCPPingReceiver starts on a random port. Currently,the TCPPingReceiver will run forever – the only way to exit it is with a Control-C.

6.2 UDPPingReceiver

The UDPPingReceiver program (class edu.caltech.cs.infotest.net.UDPPingReceiver) is a simpletest program which, when started up, listens on an Inbox named “Test Inbox” on a specifiedport. Whenever it receives a message on this Inbox, it prints it to the screen, and also sends itback to an Inbox named “Test Inbox” on the host from which it came using the UDP protocol

41

42 CHAPTER 6. EXAMPLE PROGRAMS

with reliable ordering. Though it uses the UDP protocol to send the replies, the UDPTim-ingSender is capable of receiving messages via any low-level protocol (and any protocol stack).

The UDPPingReceiver takes a single, optional command line argument – the port numberon which it is to listen. Thus, to start a UDPPingReceiver on port 8888, one would type thecommand line:

java edu.caltech.cs.infotest.net.UDPPingReceiver 8888

If the port parameter is omitted, the UDPPingReceiver starts on a random port. Currently,the UDPPingReceiver will run forever – the only way to exit it is with a Control-C.

6.3 TCPTimingSender

The TCPTimingSender (class edu.caltech.cs.infotest.net.TCPTimingSender) is a simple test pro-gram which sends a specified number of messages (of class java.lang.Long) to the Inbox “TestInbox” at a specified destination using the TCP protocol, and waits for them to be returned. Itthen calculates round-trip timing information for the messages, and presents statistics. Thoughit uses the TCP protocol to send messages, the TCPTimingSender is capable of receiving thereturned messages via any low-level protocol (and any protocol stack).

The TCPTimingSender takes three arguments, all of which are required. The first is thehostname (or IP address) of the destination host, the second is the port on the destination hostto which the messages should be sent, and the third is the number of messages to send. Thus, tosend 1000 messages to a listener on port 8888 of host “z-ha-dum.cs.caltech.edu”, one would typethe command line (all on the same line, though it is listed on multiple lines for readability):

java edu.caltech.cs.infotest.net.TCPTimingSender \

z-ha-dum.cs.caltech.edu 8888 1000

The TCPTimingSender is basically designed for use with either the TCPPingReceiver or theUDPPingReceiver, to benchmark the communication speed between two machines.

6.4 UDPTimingSender

The UDPTimingSender (class edu.caltech.cs.infotest.net.UDPTimingSender) is a simple test pro-gram which sends a specified number of messages (of class java.lang.Long) to the Inbox “TestInbox” at a specified destination using the UDP protocol with reliable ordering, and waits forthem to be returned. It then calculates round-trip timing information for the messages, andpresents statistics. Though it uses the UDP protocol to send messages, the UDPTimingSenderis capable of receiving the returned messages via any low-level protocol (and any protocol stack).

The UDPTimingSender takes three arguments, all of which are required. The first is thehostname (or IP address) of the destination host, the second is the port on the destination hostto which the messages should be sent, and the third is the number of messages to send. Thus, to

6.5. CLASSLOADERTESTER 43

send 1000 messages to a listener on port 8888 of host “z-ha-dum.cs.caltech.edu”, one would typethe command line (all on the same line, though it is listed on multiple lines for readability):

java edu.caltech.cs.infotest.net.UDPTimingSender \

z-ha-dum.cs.caltech.edu 8888 1000

The UDPTimingSender is basically designed for use with either the UDPPingReceiver or theTCPPingReceiver, to benchmark the communication speed between two machines.

6.5 ClassLoaderTester

The ClassLoaderTester (class edu.caltech.cs.infotest.lang.ClassLoaderTester) is an interactive, menu-driven program which allows you to test the operation of the InfospheresClassLoader. This isuseful to test whether or not classes you have made available on web or file servers can actu-ally be loaded remotely. The program’s operation is quite self-explanatory, and so no detaileddescription will be given here.

The InfospheresClassLoader takes one optional argument, the initial classpath for remoteclass loading. It should be a space-delimited list of URLs from which to load classes. Thecommand line to start the ClassLoaderTester is:

java edu.caltech.cs.infotest.lang.ClassLoaderTester