Distributed Systems Laboratory Manual
Distributed Systems Laboratory Manual
School of Computing
Prepared By
Eyob Wondimkun
Reviewed By
Meareg Agegnehu
December 2014
0
BAHIR DAR UNIVERSITY
BAHIR DAR INSTITUTE OF TECHNOLOGY
School of Computing
I have certified and approved the Introduction to Distributed systems laboratory manual
prepared by Eyob Wondimkun. As I have reviewed and commented the manual, it carries out the
objectives, completes practical part of the course and fulfils the standard of the curriculum of the
course.
1
Table of Contents
0
1. Laboratory 1: Connection-oriented communication using TCP/IP
Objective:
This Lab is an introduction and review of the basics of client and server communication. As we
discussed in lecture classes clients and servers can communicate in one of two mechanisms. These
are namely connection-less and connection-oriented communication. We are going to see sockets as
a means of achieving a communication between the client and server in a connection-oriented
manner whereas we will see datagram as a means of achieving a communication between the client
and server in a connection-less manner.
We are going to use Java programming language for the rest of the course so it is advisable that you
review the basics of Java programming language. The followings are the primary objectives of this
lab session:
Understanding the basics of client and server communication.
Discussion of the basics of clients and servers.
Differentiate between sockets and datagrams.
Implementing simple client and server communication using connection-oriented
communication.
Implementing simple client and server communication using connection-less
communication.
Aim: Write a program for implementing Client Server communication model using both connection-
oriented and connection-less communication.
Definition: A socket is one endpoint of a two-way communication link between two programs
running on the network. A socket is bound to a port number so that the TCP layer can identify the
application that data is destined to be sent.
Definition: A datagram is an independent, self-contained message sent over the network whose
arrival, arrival time, and content are not guaranteed.
Practical 1:
Implement a simple client server TCP based chatting application (connection-oriented) using
sockets.
1
Procedures:
1. Implementing the server
1. Enable the server to listen connections on a specific port.
2. Enable the server to accept sockets coming on that port.
3. Create streams for writing and reading data to and from the socket.
4. Communicate with the client through the socket using the I/O streams.
5. Close the socket and the I/O streams when the communication is over.
2. Implementing the client
1. Create a socket from the client to the server using the address and listening port of the
server.
2. Create streams for writing and reading data to and from the socket.
3. Communicate with the server through the socket using the I/O streams.
4. Close the socket and the I/O streams when the communication is over.
2
Code Implementation of server:
import java.net.*;
import java.io.*;
class ChatServer{
public static void main(String args[]) {
try {
ServerSocket server = new ServerSocket(8000);
System.out.println("Waiting for client to connect..");
Socket connection = server.accept();
System.out.println("Connection created with the client....");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(connection.getOutputStream(),true);
BufferedReader in = new BufferedReader( new
InputStreamReader(connection.getInputStream()));
String receive, send;
do{
receive = in.readLine();
System.out.println("Client Says: "+receive);
if(receive.equals("STOP"))
break;
System.out.print("Server Says : ");
send = br.readLine();
out.println(send);
}
while(true);
br.close();
in.close();
out.close();
connection.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
2. Implementing the client:
3
Socket connection = new Socket( serverAddress, port );
Step 2: Get the Socket’s I/O Streams
PrintWriter out = new PrintWriter(connection.getOutputStream(),true);
BufferedReader in = new BufferedReader( new
InputStreamReader(connection.getInputStream()));
Step 3: Perform the Processing
Step 3 is the processing phase, in which the server and the client communicate via the
OutputStream and InputStream objects.
Step 4: Close the Connection
In Step 4, when the transmission is complete, the client closes the connection by invoking
the close method on the streams and on the Socket.
String msg;
System.out.println("To stop chatting with server type STOP");
do{
System.out.print("Client Says: ");
msg = br.readLine();
out.println(msg);
if(msg.equals("STOP"))
break;
response = in.readLine();
System.out.println("Server Says : "+response);
4
}
while(true);
br.close();
in.close();
out.close();
connection.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
Exercise:
1. Implement a server that can perform the four arithmetic operations of addition,
subtraction, division and multiplication. Enable the clients to connect with the server,
supply two numbers and get the result of the four operations.
2. Implement a time server that can return the current time, whenever a client requests for it.
5
2. Laboratory 2: Connection-less Communication using UDP
Objective:
This Lab is an introduction and review of the basics of client and server communication. As we
discussed in lecture classes clients and servers can communicate in one of two mechanisms. These
are namely connection-less and connection-oriented communication. We are going to see sockets as
a means of achieving a communication between the client and server in a connection-oriented
manner whereas we will see datagrams as a means of achieving a communication between the client
and server in a connection-less manner.
We are going to use Java programming language for the rest of the course so it is advisable that you
review the basics of Java programming language. The followings are the primary objectives of this
lab session:
Understanding the basics of client and server communication.
Discussion of the basics of clients and servers.
Differentiate between sockets and datagrams.
Implementing simple client and server communication using connection-oriented
communication.
Implementing simple client and server communication using connection-less
communication.
Aim: Write a program for implementing Client Server communication model using both connection-
oriented and connection-less communication.
Definition: A socket is one endpoint of a two-way communication link between two programs
running on the network. A socket is bound to a port number so that the TCP layer can identify the
application that data is destined to be sent.
Definition: A datagram is an independent, self-contained message sent over the network whose
arrival, arrival time, and content are not guaranteed.
Practical 2:
A client server based program using datagrams to find if the number entered is even or odd.
(Connection-less)
6
Procedures:
Implementing the server:
7
String response= new String();
if (a%2 == 0)
response = "Number is even";
else
response = "Number is odd";
byte b1[] = new byte[1024];
b1 = response.getBytes();
DatagramPacket dp1 = new
DatagramPacket(b1,b1.length,InetAddress.getLocalHost(),1000);
datagramSocket.send(dp1);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
8
Code implementation of the client:
import java.io.*;
import java.net.*;
public class UDPClient {
public static void main(String args[]) {
try {
DatagramSocket datagramSocket = new DatagramSocket(1000);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter a number : ");
String num = br.readLine();
byte b[] = new byte[1024];
b=num.getBytes();
DatagramPacket dp = new
DatagramPacket(b,b.length,InetAddress.getLocalHost(),2000);
datagramSocket.send(dp);
byte b1[] = new byte[1024];
DatagramPacket dp1 = new DatagramPacket(b1,b1.length);
datagramSocket.receive(dp1);
String str = new String(dp1.getData(),0,dp1.getLength());
System.out.println(str);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
Exercise:
1. Implement a server that can perform the four arithmetic operations of addition,
subtraction, division and multiplication. Enable the clients to connect with the server,
supply two numbers and get the result of the four operations.
2. Implement a time server that can return the current time, whenever a client requests
for it.
9
3. Laboratory 3: Remote Procedure Call (RPC)
Objective:
This Lab is a demonstration on how to implement remote procedure calls. We are going to use the
SUN RPC implementation in a Linux operating system. The interface definition language (IDL)
compiler for SUN RPC is included with all Linux and UNIX implementation so we don’t have to
install additional packages or programming environments in order to implement remote procedure
calls. First we are going to write the interface using SUN RPC IDL. Using the SUN RPC IDL
compiler (rpcgen), we are going to compile our interface definition into C language client stub,
server stub, a header file and additional necessary files. Once we have that files we will be
implementing the client and the server. The server needs to register the program, version, procedures
it implements and to which port it will listen to incoming requests using the portmapper. Once all
that is done, now the client can perform remote procedure calls. The following are the primary
objectives of this lab session:
Understanding the basics concepts of remote procedure calls.
Discussion of the importance of interface definition and interface definition language in
RPC.
Discussion of the steps involved to implement remote procedure calls in SUN RPC
implementation.
Understand how we can define interface definition using the SUN RPC IDL.
Implementing a simple RPC program and demonstrate how RPC is carried out.
Aim: Write a program for implementing remote procedure calls using SUN’s implementation of
RPC.
Requirements:
A pc running any distribution of UNIX or LINUX.
rpcgen(SUN RPC idl compiler to c) which by default is part of the UNIX or LINUX
OS
portmap or rpcbind (the portmapper) in some distributions, we have to install an
additional package in order to use this services.
C compiler and linker that are also included as part of the OS.
10
Interface Definition Language
Interface definition language is used by rpcgen to generate stub functions. It defines an RPC
program: which is a collection of RPC procedures.
General structure of an IDL file
1. The first part of IDL definition contains different types that are used in the program. This
can be normal built in data types, structures, strings, pointers or any other types.
2. The second part of IDL definition contains the program and the procedures defined in it.
The program is identified by a program identifier and a value. That specific program can
also be given a version by using version name and value. Finally in that block we will
define the procedures with their return values and argument list. Each procedure is also
given a numeric identifier to identify it from the other procedures within the same program
and version.
The Interface Definition Language (IDL) is the input needed by rpcgen to generate the stub
functions. The structure of IDL is vaguely similar to a set of C prototype definitions but RPC IDL is
a separate definition language which is independent of any programming language.
Each IDL program contains the following structure:
- Optional constant definitions and type-defs may be present
- The entire interface is enveloped in a program block.
The sample on the right gives a name PROG to the set of interfaces and a numeric value of
0x3a3afeeb. Sun decreed that each collection of RPC interfaces is identified by a 32 bit value that
you have to select. The restrictions given are:
0x00000000-0x1fffffff defined by sun
0x20000000-0x3fffffff defined by the user
0x40000000-0x5fffffff transient processes
0x60000000-0x7fffffff reserved
11
Within the program block, one or more sets of versions may be defined. A client program will
always request an interface by asking for a {program#, version#} tuple. Each version contains a
version name and number. In the sample on the right, the version name is PROG1 and the number is
1.
Within each version block, a set of functions is defined. These look similar to C prototypes and are
tagged with a function number (each function gets a unique number within a version block).
Types in SUN RPC IDL
Constants
- May be used in place of an integer value - converted to #define statement by rpcgen
const MAXSIZE = 512;
Structures
Similar to C structures - rpcgen transfers structure definition and adds a typedef for the name of
the structure
struct intpair { int a, b };
Is translated to:
struct intpair {
int a;
int b;
};
typedef struct intpair intpair;
Enumerations
Similar to C enumeration types
int proc_hits[100];
Defines a fixed size array of 100 integers.
long x_vals<50>
defines a variable-size array of a maximum of 50 longs
12
Pointers
Like C, but not sent over the network. What is sent is a Boolean value (true for pointer, false for
null) followed by the data to which the pointer points.
Strings
declared as if they were variable length arrays
string name<50>;
Declares a string of at most 50 characters.
string anyname<>;
Declares a string of any number of characters.
Boolean
can have the value of TRUE or FALSE:
bool busy;
Writing procedures using Sun RPC
- Create a procedure whose name is the name of the RPC definition eg if function is BLIP
– Write it in lowercase
– followed by an underscore, then version number, underscore, “svc”
For example, BLIP → blip_1_svc
- Argument to procedure is a pointer to the argument data type specified in the IDL
- Default behaviour: only one parameter to each function. if you want more, use a struct
– This was relaxed in later versions of rpcgen but remains the default
- Procedure must return a pointer to the data type specified in the IDL
- The server stub uses the procedure’s return value after the procedure returns, so the return
address must be that of a static variable.
Practical 1: Write an RPC program with the following functions.
i. A function called Greeting (), which calls the server function with the name of the
user and the server returns a string saying “<name>, Welcome to RPC server”.
ii. A function called Add (), which accepts two int values, calls the server function and
returns the summation of the two numbers.
Procedures:
Step 1: Write the Interface definition
1. Define two functions that run on server:
– Greeting (string) has one string input parameter and returns one string value.
– Add(int,int) accepts two int values as input and returns an int value.
13
2. IDL:
struct numbers{
int num1;
int num2;
};
program FIRST_PROG {
version FIRST_VERS {
string Greeting(string) = 1;
int Add(numbers) = 2;
} = 1;
} = 0x31423456;
- We have to envelope our two functions in a version block. This in turn has to be
enveloped in a program block.
- Each function is assigned an arbitrary number.
- We pick a number for the program number and hope that nobody on our server will
pick the same one.
3. IDL convention is to suffix the file with .x
- We name the file first.x
- It can be compiled with:
rpcgen first.x
When the file (first.x) is compiled with rpcgen first.x, we get:
first.h: header file
first_clnt.c: client stub
first_svc.c: server stub
- We can give –a command option to generate sample client and server C files, we can
edit those files to our appropriate needs then:
rpcgen –a first.x
When the file (first.x) is compiled with rpcgen -a first.x, we get:
first.h: header file
first_clnt.c: client stub
first_client.c: client code
first_svc.c: server stub
first_server.c: server code
14
Additional command line option can be given to rpcgen to perform different tasks. You can see
some of them by writing the command rpcgen --help
The header file (first.h): The header file contains any necessary definitions and initialization that are needed
by both the client and the server. It should be included by both client and server. Our type declarations,
structure declarations, function declarations and other data are located in here. You can open and check it out!
The client code (first_client.c): Explained (you will get this file automatically generated; you just need to
make a little bit of editing and modification)
1. The first part of the program is the include directive which includes the header file generated by
rpcgen and <stdio.h> which will be used for input and output
#include "first.h"
#include <stdio.h>
2. Second part is a function with the project name in small letters followed by underscore followed by
program version number. This function accepts one argument which is a character pointer (an array or
string). Check that this function is called from main function by supplying the argument which is the
host name or address of the server.
3. This function declares all necessary arguments for the remote procedure call arguments and for the
return values, remember that remote procedure calls result in a return of a pointer of the return value
type (think about that!!!), in addition to that it declares one CLIENT type variable which will be used
to locate the server, from the given host, using the given program name and version name and the
final argument is an option to specify the communication protocol which can be either udp or tcp.
void
first_prog_1(char *host)
{
CLIENT *clnt;
char * *result_1;
char *greeting_1_arg1;
int *result_2;
int add_1_arg1;
int add_1_arg2;
4. Creating the binding (look up to the server), this call is actually forwarded to the portmapper and it
will try to locate the port in which that server is running. If this call fails, the clnt variable will contain
NULL in which case an error message is displayed and the program exits. Otherwise we can make
procedure calls to the server by using that port.
15
clnt = clnt_create (host, FIRST_PROG, FIRST_VERS, "udp");
if (clnt == NULL) {
clnt_pcreateerror (host);
exit (1);
}
5. Declaring and accepting arguments that will be used in the procedure call.
greeting_1_arg1 = "Computer";
printf("\n\n\t\tWelcome to RPC program\n");
printf("\tEnter first number : ") ;
scanf("%d",&add_1_arg1);
printf("\tEnter second number : ");
scanf("%d",&add_1_arg2);
6. Performing the first procedure call. If the return value is NULL, the remote procedure call is not
performed and we simply display an error message otherwise we simply display the value returned
from the server.
result_1 = greeting_1(greeting_1_arg1, clnt);
if (result_1 == (char **) NULL) {
clnt_perror (clnt, "call failed");
}
else{
printf("\n\n\tserver responds for first call\n\t%s\n\t",*result_1);
}
7. Performing the second procedure call. If the return value is NULL, the remote procedure call is not
performed and we simply display an error message otherwise we simply display the value returned
from the server.
result_2 = add_1(add_1_arg1, add_1_arg2, clnt);
if (result_2 == (int *) NULL) {
clnt_perror (clnt, "call failed");
}
else{
printf("\n\tserver responds for second call\n\t%d\n\t",*result_2);
}
8. Finally we will release any resources held by our client program and that is the end of our function.
clnt_destroy (clnt);
}
16
9. Main function which accepts one command line argument given from the command line when the
program is executed or run. If the argument is not given the program exits otherwise that argument is
forwarded to first_prog_1() function as an argument.
int
main (int argc, char *argv[])
{
char *host;
if (argc < 2) {
printf ("usage: %s server_host\n", argv[0]);
exit (1);
}
host = argv[1];
first_prog_1 (host);
exit (0);
}
The server code (first_server.c): Explained (you will get this file automatically generated; you just need to
make a little bit of editing, modification and the actual implementation of the functions defined in the
interface definition)
1. The first part of the program is the include directive which includes the header file generated by
rpcgen and <stdio.h> and <string.h>which will be used for input and output and string processing.
#include "first.h"
#include<string.h>
#include<stdio.h>
2. Second part is the actual implementation of the functions defined in the interface definition. Try to
check the name of the functions used in here and the names used in the client side. The actual name
translation is done by the stub files.a function with the project name in small letters followed by
underscore followed by program version number. This function accepts one argument which is a
character pointer (an array or string). Check that this function is called from main function by
supplying the argument which is the host name or address of the server.
The first function accepts one string, and a struct (which we don’t bother ourselves with!). It displays
the given argument, concatenates with the server message, displays it again and finally returns the
result. Check also the return type of the function :-)
17
char **
greeting_1_svc(char *arg1, struct svc_req *rqstp)
{
static char * result;
char * temp = ", Welcome to server :";
printf(" \n\n\t Welcome to RPC Server : \n\n");
printf("\tThe server accepts the argument : %s\n",arg1);
result = strncat(arg1,temp,18);
printf("\tThe server result is : %s\n",result);
return &result;
}
3. The second function accepts the two int values, calculates the result, displays it and finally returns the
result. Here again check the return type of the result.
int *
add_1_svc(int arg1, int arg2, struct svc_req *rqstp)
{
static int result2;
printf("\n\n\tThe server accepts the arguments %d %d \n",arg1,arg2);
result2 = arg1 + arg2;
printf("\t The server result is %d\n",result2);
return &result2;
}
Exercise: Write an RPC program that performs addition, subtraction, multiplication and division
on the server side. Enable the client to accept the two input values and display a menu for the
user to select the operations that needs to be performed?
18
4. Laboratory 4: Remote Object Invocation using Java RMI
Objective:
This Lab is a demonstration on how to implement remote method invocation using Java RMI. We
will be defining remote interfaces, provide their implementation in a server computer, register the
remote object using java registry and finally make remote method invocations from the client
computer or programs. The following are the primary objectives of this lab session:
Understanding the basics concepts of remote method invocations.
Discussion of how interfaces are defined for Java RMI programs.
Be able to implement remote interface definitions.
Be able to register remote objects using Java registry.
Be able to make remote method invocations from the client
Aim: Write a program for implementing remote invocation using Java RMI.
Requirements:
A computer running Java and have Java Development kit (JDK).
Any Java IDE
19
Implementing the clients. Clients that use remote objects can be implemented at any time
after the remote interfaces are defined, including after the remote objects have been
deployed.
20
Define the constructor for the remote object
Provide an implementation for each remote method in the remote interfaces
Example
import java.rmi.*;
import java.rmi.server.*;
21
4. Implementing the clients. Clients that use remote objects can be implemented at any time
after the remote interfaces are defined, including after the remote objects have been deployed.
They simply make requests to access remote objects. The clients should locate the remote
object and perform binding before they can invoke methods so they will need to contact the
RMI registry to locate the remote object.
import java.rmi.Naming;
public class AddClient {
public static void main(String args[]) {
try {
AddInterface
ai=(AddInterface)Naming.lookup("rmi://localhost:1099/Add");
System.out.println("The sum of 2 numbers is: "+ai.sum(10,2));
}
catch(Exception e){
System.out.println("Client Exception: "+e);
}
}
}
5. Compile the java files: compile all java files using java compiler (javac)
6. Run the Java RMI registry service: In a new command window run the rmi registry. Make
sure that the RMI registry is run from within the directory in which the remote object (RMI
server) is implemented otherwise you will get the error ClassNotFoundException.
:> start rmiregistry
7. Run the RMI server: Use the java runtime to execute the RMI server
:> java AddServer
8. Run the RMI client: User the java runtime to execute the RMI Client
:> java AddClient
RMI works as follows:
1. A server object is registered with the RMI registry.
2. A client looks through the RMI registry for the remote object.
3. Once the remote object is located, its stub is returned in the client.
4. The remote object can be used in the same way as a local object. Communication between the
client and the server is handled through the stub and the skeleton.
22
Practical 1: Write RMI program to implement a simple calculator. The remote object should provide
functions that perform addition, subtraction, division and multiplication. The client should provide a
menu based interface in which the user can enter the operation to be performed and the numbers in
which the operation is going to be performed. The functions that need to be implemented in the
remote object are:
iii. float sum(float, float): for performing addition.
iv. float sub(float, float): for performing subtraction.
v. float div(float, float): for performing division.
vi. float mult(float, float): for performing multiplication.
Procedures:
Step 1: Write the Remote Interface
import java.rmi.*;
public interface CalcInterface extends Remote {
public float add(float n1,float n2) throws RemoteException;
public float sub(float n1,float n2) throws RemoteException;
public float div(float n1,float n2) throws RemoteException;
public float mult(float n1,float n2) throws RemoteException;
}
Step 2: Write the Remote Interface implementation (RMI Server), create a remote object and bind
the object.
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.Naming;
public class CalcServer extends UnicastRemoteObject implements CalcInterface {
public CalcServer() throws RemoteException {
super();
}
public float add(float num1,float num2) throws RemoteException {
return num1+num2;
}
public float sub(float num1,float num2) throws RemoteException {
return num1-num2;
}
23
public float div(float num1,float num2) throws RemoteException {
return num1/num2;
}
public float mult(float num1,float num2) throws RemoteException {
return num1*num2;
}
public static void main(String args[]) {
try {
CalcServer cs = new CalcServer();
Naming.rebind("rmi://localhost:1099/Calculator", cs);
System.out.println("Server is connected and waiting for the client ");
}
catch(Exception e) {
System.out.println("Server could not connect: "+e);
}
}
}
Step 3: Write the RMI Client.
import java.rmi.Naming;
import java.util.*;
public class CalcClient {
public static void main(String args[]) {
try {
Scanner in = new Scanner(System.in);
int choice;
float num1, num2, result=0;
CalcInterface cs
=(CalcInterface)Naming.lookup("rmi://localhost:1099/Calculator");
do {
System.out.println("\n\t\t Remote Calculator \n\t");
System.out.println("Enter 1 for Addition\n\t");
System.out.println("Enter 2 for Subtraction\n\t");
System.out.println("Enter 3 for Division\n\t");
System.out.println("Enter 4 for Multiplication \n\n\t");
24
System.out.println("Enter 5 to Exit \n\n\t");
System.out.print("Enter your choice : ");
choice = in.nextInt();
if (choice >= 1 && choice <= 4) {
System.out.print("\n\tEnter first number : ");
num1 = in.nextFloat();
System.out.print("\n\tEnter second number : ");
num2 = in.nextFloat();
switch(choice) {
case 1: result = cs.add(num1,num2) ; break;
case 2: result = cs.sub(num1,num2) ; break;
case 3: result = cs.div(num1,num2) ; break;
case 4: result = cs.mult(num1,num2) ; break;
}
System.out.print("\n\n\tThe result of the operation is : "+result);
}
}
while(choice != 5);
System.out.println("\n\n\tProgram Exiting..............");
}
catch(Exception e) {
System.out.println("Client Exception: "+e);
}
}
}
Step 4: Compile both the client and the server programs in the command line.
:> javac CalcServer.java
:> javac CalcClient.java
Step 5: Open a command prompt, go to the directory where the RMI server is located and start the
RMI registry service.
:> start rmiregistry
Step 6: Run the RMI server in a new command prompt.
:> java CalcServer
25
Step 7: Run the RMI Client in a new command prompt, and test the program if it works properly.
:> java CalcClient
Exercise:
1. From the previous program, try to check if the server can handle more than one client at
any given time.
2. Write a Java RMI Application which enables the client and server communicate by
sending text messages. It will be used to enable the server and client implement a simple
chat application. They communicate by sending and receiving string values or messages.
26
5. Laboratory 5: Object Serialization and Java RMI
Objective:
This Lab is a demonstration on how to implement remote method invocation using Java RMI and we
will demonstrate what object serialization is and why it is used with Java RMI. We will be defining
remote interfaces, provide their implementation in a server computer, register the remote object
using java registry and finally make remote method invocations from the client computer or
programs. The following are the primary objectives of this lab session:
Understanding the basics concepts object serialization.
Be able to write and read objects from files.
Be able to write and read object using network streams.
Implement a Java RMI program that uses object serialization
Aim: Write a program for implementing remote invocation using Java RMI and object serialization.
Requirements:
A computer running Java and have Java Development kit (JDK).
Any Java IDE
Serializing Objects
Serialization is the process of converting a set of object instances that contain references to each
other into a linear stream of bytes, which can then be sent through a socket, stored to a file, or simply
manipulated as a stream of data. Serialization is the mechanism used by RMI to pass objects between
JVMs, either as arguments in a method invocation from a client to a server or as return values from a
method invocation.
Another Java facility that supports RMI is object serialization. The java.io package includes classes
that can convert an object into a stream of bytes and reassemble the bytes back into an identical copy
of the original object. Using these classes, an object in one process can be serialized and transmitted
over a network connection to another process on a remote host. The object (or at least a copy of it)
can then be reassembled on the remote host.
An object that you want to serialize has to implement the java.io.Serializable interface. With this
done, an object can be written just as easily to a file, a string buffer, or a network socket. For
example, assuming that Foo is a class that implements Serializable, the following code writes Foo on
an object output stream, which sends it to the underlying I/O stream:
Foo myFoo = new Foo();
OutputStream out = ... // Create output stream to object destination
27
ObjectOutputStream oOut = new ObjectOutputStream(out);
oOut.writeObject(myFoo);
The object can be reconstructed just as easily:
InputStream in = ... // Create input stream from source of object
ObjectInputStream oIn = new ObjectInputStream(in);
Foo myFoo = (Foo)oIn.readObject();
We've simplified things a bit by ignoring any exceptions generated by these code snippets. The
object serialization facility allows an actual object to be serialized in its entirety, transmitted to any
destination, and then reconstructed as a precise replica of the original.
When you serialize an object, all of the objects that it references as data members will also be
serialized, and all of their object references will be serialized, and so on. If you attempt to serialize
an object that doesn't implement the Serializable interface, or an object that refers to non−serializable
objects, then a NotSerializableException will be thrown. Method arguments that aren't objects are
serialized automatically using their standard byte stream formats.
In RMI, the serialization facility is used to marshal and unmarshal method arguments that are
objects, but that are not remote objects. Any object argument to a method on a remote object in RMI
must implement the Serializable interface, since the argument will be serialized and transmitted to
the remote host during the remote method invocation.
Using Serialization
Serialization is a mechanism built into the core Java libraries for writing a graph of objects into a
stream of data. This stream of data can then be programmatically manipulated, and a deep copy of
the objects can be made by reversing the process. This reversal is often called deserialization.
In particular, there are three main uses of serialization:
As a persistence mechanism
If the stream being used is FileOutputStream, then the data will automatically be written to a file.
As a copy mechanism
If the stream being used is ByteArrayOutputStream, then the data will be written to a byte array in
memory. This byte array can then be used to create duplicates of the original objects.
As a communication mechanism
If the stream being used comes from a socket, then the data will automatically be sent over the wire
to the receiving socket, at which point another program will decide what to do.
28
As you might expect, serialization is implemented using a pair of streams. Even though the code that
underlies serialization is quite complex, the way you invoke it is designed to make serialization as
transparent as possible to Java developers. To serialize an object, create an instance of
ObjectOutputStream and call the writeObject( ) method; to read in a serialized object, create an
instance of ObjectInputStream and call the readObject( ) object.
Practical 1: Create a serializable object and write and read the objects from a file.
Step 1: Create the following class called SerializeObject which implements the Serializable interface
import java.io.*
public class SerializeObject implements java.io.Serializable{
public int first;
public char second;
public String third;
public SerializeObject (int first, char second, String third){
this.first= first;
this.second = second;
this.third = third;
}
}
Step 2: Create the following class called ObjWrite which will write objects of the SerializeObject
class to a file.
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjWrite {
public static void main (String args[]){
try{
FileOutputStream outputFile = new FileOutputStream("objFile");
ObjectOutputStream serializeStream = new ObjectOutputStream(outputFile);
SerializeObject obj = new SerializeObject (1,'c',new String ("Hi!"));
serializeStream.writeObject(obj);
serializeStream.flush();
}
catch (Exception e) {
System.out.println("Error in serialization");
} }}
29
Step 3: Create the following class called ObjRead which will read objects of the SerializeObject
class from a file.
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjRead {
public static void main (String args[]){
try{
FileInputStream inputFile = new FileInputStream("objFile");
ObjectInputStream serializeStream = new ObjectInputStream(outputFile);
SerializeObject obj = (SerializeObject) serializeStream.readObject(obj);
// insert code to display the contents of the object
}
catch (Exception e) {
System.out.println("Error in serialization");
}
}
}
Note: ObjectInputStream and ObjectOutputStream's constructor takes an OutputStream as an
argument. This is analogous to many of the streams. ObjectOutputStream and ObjectInputStream are
simply encoding and transformation layers. This enables RMI to send objects over the wire by
opening a socket connection, associating the OutputStream with the socket connection, creating an
ObjectOutputStream on top of the socket's OutputStream, and then calling writeObject( ).
Practical 2: Create an RMI program that will remotely calculate the net salary of an employee. The
server object will be passed one employee object and calculates the net salary for that object, assigns
the net salary to the object and returns the object to the client.
Step 1: The employee class
import java.io.*;
public class Employe implements Serializable{
protected String name;
protected double baseSalary;
protected double tax;
protected double netSalary;
public Employe(){
}
30
public Employe(String name, double baseSalary){
this.name = name;
this.baseSalary = baseSalary;
}
public void display(){
System.out.println("Name : "+this.name);
System.out.println("Salary : "+this.baseSalary);
System.out.println("Tax : "+this.tax);
System.out.println("Net Sal: "+this.netSalary);
}
public double getSalary(){
return baseSalary;
}
public double getTax(){
return tax;
}
public void setTax(double tax){
this.tax = tax;
}
31
Step 3: Create the Server implementation
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.Naming;
public class SalaryServer extends UnicastRemoteObject implements SalaryInterface {
private double tax=0.15;
public SalaryServer() throws RemoteException {
super();
}
public Employe calculateNetSalary(Employe emp) throws RemoteException {
double net;
emp.setTax(tax * emp.getSalary());
net = emp.getSalary() - emp.getTax();
emp.setNetSalary(net);
return emp;
}
public static void main(String args[]) {
try {
SalaryServer ss = new SalaryServer();
Naming.rebind("rmi://localhost:1099/Salary", ss);
System.out.println("Server is connected and waiting for the client ");
}
catch(Exception e) {
System.out.println("Server could not connect: "+e);
}
}
}
Step 4: Implement the client
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
32
public class SalaryClient {
public static void main(String[] args) {
try {
Employe emp = new Employe("Abebe",3250);
emp.display();
SalaryInterface si = (SalaryInterface)
Naming.lookup("rmi://localhost:1099/Salary");
emp = si.calculateNetSalary(emp);
emp.display();
}
catch (Exception e) {
System.out.println();
System.out.println("Exception");
}
}
}
Step 5: Run the program and test it if it works correctly. Try to make the Employee class not
Serializable and note down what will happen.
Exercise:
1. Write a program that calculates the total marks and the letter grade of a given students mark
in a given course. The client will create a student and courses and assign the marks the
student scores in the course and send the object to the server. The server calculates the total
mark and letter grade for that course assign the values on the object and returns the object.
The client finally will display the result of the calculation.
a. The student object will contain the Name, year, department, number of courses and an
array of courses initialized to the total number of courses the student takes.
b. The course class will contain course title, code, credit hours, mid, final, project, total
and grade.
33
6. Laboratory 6: CORBA
Objective:
This Lab is a demonstration on how to implement distributed objects in CORBA. We will use java to
CORBA mapping to implement our IDL definitions in CORBA. The process of interface definition
and, compiling the interface definition into a specific language implementation and then finally
implementing the client and servers using the specific language is similar with the other remote
method invocations that we have seen.
34
import org.omg.CORBA.*;
4. Classes needed for the Portable Server Inheritance Model
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
5. Properties to initiate the ORB
import java.util.Properties;
Java IDL Steps
Implementation of CORBA programs in java requires the following steps:
1. Write the IDL interface file
2. Compile the IDL file to generate stubs and skeletons
3. Write and compile the remote object implementation
4. Write and compile the remote server
5. Write and compile the client
35
module HelloApp
{
// Subsequent lines of code here.
};
When you compile the IDL, the module statement will generate a package statement in the Java
code.
36
Step 2: Mapping the interface definition into Java.
The tool idlj reads OMG IDL files and creates the required Java files. The idlj compiler defaults to
generating only the client-side bindings. If you need both client-side bindings and server-side
skeletons (as you do for our "Hello World" program), you must use the -fall option when running the
idlj compiler.
1. Make sure that the j2sdk/bin directory (or the directory containing idlj, java, javac, and orbd)
is in your path.
2. Go to a command line prompt.
3. Change to the directory containing your Hello.idl file.
4. Enter the compiler command:
idlj -fall Hello.idl
Understanding the generated files
Some of the generated files include the follwoing the following files:
- _HelloStub.java - This class is the client stub, providing CORBA functionality for the client.
It implements the Hello.java interface.
- Hello.java - This interface contains the Java version of our IDL interface. It contains the
single method sayHello. The Hello.java interface extends org.omg.CORBA.Object, providing
standard CORBA object functionality as well.
- HelloHelper.java - This final class provides auxiliary functionality, notably the narrow
method required to cast CORBA object references to their proper types.
- HelloHolder.java - This final class holds a public instance member of type Hello. It provides
operations for out and inout arguments, which CORBA has but which do not map easily to
Java's semantics.
- HelloOperations.java - This interface contains the methods that are defined in the interface.
The IDL-to-Java mapping puts all of the operations defined on the IDL interface into this file,
which is shared by both the stubs and skeletons.
- HelloPOA.java - This abstract class is the stream-based server skeleton, providing basic
CORBA functionality for the server. It extends org.omg.PortableServer.Servant , and
implements the InvokeHandler interface and the HelloOperations interface. The server class
extends HelloPOA.
37
1. Start your text editor or IDE and create a file named HelloServer.java in your main project
directory, Hello.
2. Enter the following code for HelloServer.java in the text file.
// HelloServer.java
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
import java.util.Properties;
class HelloImpl extends HelloPOA {
private ORB orb;
public void setORB(ORB orb_val) {
orb = orb_val;
}
// implement sayHello() method
public String sayHello() {
return "\nHello world !!\n";
}
// implement shutdown() method
public void shutdown() {
orb.shutdown(false);
}
}
public class HelloServer {
public static void main(String args[]) {
try{
// create and initialize the ORB
ORB orb = ORB.init(args, null);
// get reference to rootpoa & activate the POAManager
POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
rootpoa.the_POAManager().activate();
// create servant and register it with the ORB
HelloImpl helloImpl = new HelloImpl();
38
helloImpl.setORB(orb);
// get object reference from the servant
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(helloImpl);
Hello href = HelloHelper.narrow(ref);
// get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
// Use NamingContextExt which is part of the Interoperable
// Naming Service (INS) specification.
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
// bind the Object Reference in Naming
String name = "Hello";
NameComponent path[] = ncRef.to_name( name );
ncRef.rebind(path, href);
System.out.println("HelloServer ready and waiting ...");
// wait for invocations from clients
orb.run();
}
catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
System.out.println("HelloServer Exiting ...");
}
}
3. Save and close HelloServer.java.
39
5. The files HelloServer.class and HelloImpl.class are generated in the Hello directory.
40
e.printStackTrace(System.out);
}
}
}
3. Save and close HelloClient.java.
41
For this example, you can omit -ORBInitialHost localhost since the name server is running
on the same host as the Hello server. If the name server is running on a different host, use -
ORBInitialHost nameserverhost to specify the host on which the IDL name server is running.
Specify the name server (orbd) port as done in the previous step, for example, -
ORBInitialPort 1050.
3. Run the client application:
java HelloClient -ORBInitialPort 1050 -ORBInitialHost localhost
For this example, you can omit -ORBInitialHost localhost since the name server is running
on the same host as the Hello client. If the name server is running on a different host, use -
ORBInitialHost nameserverhost to specify the host on which the IDL name server is running.
Specify the name server (orbd) port as done in the previous step, for example, -
ORBInitialPort 1050.
4. The client prints the string from the server to the command line:
Hello world!!
42
7. Laboratory 7: Message Oriented Communication
Objective:
This Lab is a demonstration on how to implement message oriented communication by
implementing message passing interface (MPI). We are going to implement some of the primitives
defined in message passing interface by using java sockets. We will have two computers
communicating, a server and a client and they both will implement the MPI primitives. They can
then be able to communicate by using the defined primitives.
Understanding the basics concepts of message oriented communication.
Understanding the primitives that are used in message passing interface (MPI).
Write our own implementation of MPI primitives.
Aim: Write an implementation of message passing interface and enable the communication of a
client and server computers using MPI primitives.
Requirements:
We are going to use Java networking and sockets to implement MPI. So, we need a computer that
has JDK and some decent java IDE.
Background:
The need to be hardware and platform independent eventually led to the definition of a standard for
message passing, simply called the Message-Passing Interface or MPI. MPI is designed for parallel
applications and as such is tailored to transient communication. It makes direct use of the underlying
network. At the cores of MPI are messaging primitives to support transient communication, of which
the most intuitive ones are summarized in figure below.
Primitive Meaning
MPI_send Send a message and wait until copied to local or remote buffer
MPI_issend Pass reference to outgoing message, and wait until receipt starts
43
Fig. Some of the most common primitives used in MPI
Primitive description
MPI_bsend:
- Transient asynchronous communication is supported by means of the MPI_bsend
primitive. The sender submits a message for transmission, which is generally first
copied to a local buffer in the MPI runtime system. When the message has been
copied, the sender continues. The local MPI runtime system will remove the message
from its local buffer and take care of transmission as soon as a receiver has called a
receive primitive.
MPI_send:
- There is also a blocking send operation, called MPI_send, of which the semantics are
implementation dependent. The primitive MPI_send may either block the caller until
the specified message has been copied to the MPI runtime system at the sender's side,
or until the receiver has initiated a receive operation.
MPI_ssend:
- Synchronous communication by which the sender blocks until its request is accepted
for further processing is available through the MPI_ssend primitive.
MPI_sendrecv:
- Finally, the strongest form of synchronous communication is also supported: when a
sender calls MPI_sendrecv, it sends a request to the receiver and blocks until the latter
returns a reply. Basically, this primitive corresponds to a normal RPC.
MPI_recv:
- The operation MPI_recv is called to receive a message; it blocks the caller until a
message arrives. There is also an asynchronous variant, called MPI_irecv, by which a
receiver indicates that is prepared to accept a message. The receiver can check
whether or not a message has indeed arrived, or block until one does.
The semantics of MPI communication primitives are not always straight forward, and different
primitives can sometimes be interchanged without affecting the correctness of a program.
The official reason why so many different forms of communication are supported is that it gives
implementers of MPI systems enough possibilities for optimizing performance. MPI has been
designed for high-performance parallel applications, which makes it easier to understand its diversity
in different communication primitives.
Practical:
44
Write a server and client that implements the following MPI primitives.
1. MPI_send()
2. MPI_recv()
3. MPI_sendrecv()
Procedure 1: Both client and server should provide a user interface (Menu) to enable the user to
select which primitive to use.
Procedure 2: They will accept the user selection and call the appropriate MPI primitive
implementation.
Implementing the server:
Step 1: Importing the necessary packages:
import java.io.*;
import java.net.*;
Step 2: Writing the server class:
public class Server
{
PrintStream out=null;
BufferedReader in=null;
BufferedReader br=null;
String str="";
Step 3: Writing the server class constructor, everything is done in the constructor including the menu
and the method calls:
public Server()throws IOException
{
ServerSocket ss=new ServerSocket(7000);
System.out.println("Server Started");
Socket s = ss.accept();
out = new PrintStream(s.getOutputStream());
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
br = new BufferedReader(new InputStreamReader(System.in));
String str;
while(s!=null)
{
System.out.println("Select the operation to be performed");
45
System.out.println("1. To send message without any response
MPI_send");
System.out.println("2. To send message with response
MPI_sendrecv");
System.out.println("3. To receive message MPI_recv");
System.out.println("4. To end the program");
str = br.readLine();
out.println(str);
if(str.equals("1"))
{
MPI_send();
}
else if(str.equals("2"))
{
MPI_sendrec();
}
else if(str.equals("3"))
{
MPI_recv();
}
else
break;
}
}
Program:
EchoServer.java
import java.io.*;
import java.net.*;
public class EchoServer implements Runnable
{
Socket socket=null;
static ServerSocket ss;
EchoServer(Socket newSocket)
{
46
this.socket=newSocket;
}
public static void main(String args[]) throws IOException
{
ss=new ServerSocket(7000);
System.out.println("Server Started");
while(true)
{
Socket s = ss.accept();
EchoServer es = new EchoServer(s);
Thread t = new Thread(es);
t.start();
}
}
public void run()
{
try {
while(ss!=null)
{
BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream() ));
PrintStream out = new PrintStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
String str=in.readLine();
if(str.equals("1"))
{
System.out.println(in.readLine());
}
else if(str.equals("2"))
{
System.out.println(in.readLine());
System.out.println("Enter Data ");
str=br.readLine();
47
out.println(str);
}
else if(str.equals("3"))
{
System.out.println("Enter Data ");
str=br.readLine();
out.println(str);
}
}
System.out.println("Exiting...");
}
catch(Exception e)
{}
}
}
EchoClient.java
import java.io.*;
import java.net.*;
public class EchoClient
{
PrintStream out=null;
BufferedReader in=null;
BufferedReader br=null;
String str="";
public EchoClient()throws IOException
{
Socket s=new Socket("localhost",7000);
out = new PrintStream(s.getOutputStream());
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
br = new BufferedReader(new InputStreamReader(System.in));
while(s!=null)
{
System.out.println("Select the operation to be performed");
System.out.println("1. To send message without any response");
48
System.out.println("2. To send message with response");
System.out.println("3. To recieve message");
System.out.println("4. To end the program");
str=br.readLine();
out.println(str);
if(str.equals("1"))
{
MPI_send();
}
else if(str.equals("2"))
{
MPI_sendrec();
}
else if(str.equals("3"))
{
MPI_recv();
}
else
break;
}
}
public static void main(String args[])throws IOException
{
EchoClient ec = new EchoClient();
System.out.println("Exiting...");
}
void MPI_send() throws IOException
{
System.out.println("Enter Data ");
str=br.readLine();
out.println(str);
}
void MPI_sendrec() throws IOException
{
49
System.out.println("Enter Data ");
str=br.readLine();
out.println(str);
System.out.println(in.readLine());
}
void MPI_recv() throws IOException
{
System.out.println(in.readLine());
}
}
Exercises:
1. Implement the buffered versions of the above send and receive primitives.
50
8. Laboratory 8: Web Services
Objective:
This Lab is a demonstration on how to implement Web services. The objectives of this lab are the
following:
Understanding the basics concepts of Web services.
Understanding types of web services and when to use them.
Implement a simple web services using Netbeans.
Aim: Write an implementation of message passing interface and enable the communication of a
client and server computers using MPI primitives.
Requirements:
We are going to use Java networking and sockets to implement MPI. So, we need a computer that
have JDK and Netbeans IDE.
Background:
What Are Web Services?
Web services are client and server applications that communicate over the World Wide Web’s
(WWW) HyperText Transfer Protocol (HTTP). As described by the World Wide Web Consortium
(W3C), web services provide a standard means of interoperating between software applications
running on a variety of platforms and frameworks. Web services are characterized by their great
interoperability and extensibility, as well as their machine-processable descriptions, thanks to the use
of XML. Web services can be combined in a loosely coupled way to achieve complex operations.
Programs providing simple services can interact with each other to deliver sophisticated added-value
services.
Types of Web Services:
On the conceptual level, a service is a software component provided through a network-accessible
endpoint. The service consumer and provider use messages to exchange invocation request and
response information in the form of self-containing documents that make very few assumptions
about the technological capabilities of the receiver.
On a technical level, web services can be implemented in various ways. The two types of web
services can be distinguished as “big” web services and “RESTful” web services.
1) “Big” Web Services:
In Java EE 6, JAX-WS provides the functionality for “big” web services. Big web services use XML
messages that follow the Simple Object Access Protocol (SOAP) standard, an XML language
defining a message architecture and message formats. Such systems often contain a machine-
51
readable description of the operations offered by the service, written in the Web Services Description
Language (WSDL), an XML language for defining interfaces syntactically.
The SOAP message format and the WSDL interface definition language have gained widespread
adoption. Many development tools, such as NetBeans IDE, can reduce the complexity of developing
web service applications.
A SOAP-based design must include the following elements.
A formal contract must be established to describe the interface that the web service offers.
WSDL can be used to describe the details of the contract, which may include messages,
operations, bindings, and the location of the web service. You may also process SOAP
messages in a JAX-WS service without publishing a WSDL.
The architecture must address complex nonfunctional requirements. Many web service
specifications address such requirements and establish a common vocabulary for them.
Examples include transactions, security, addressing, trust, coordination, and so on.
The architecture needs to handle asynchronous processing and invocation. In such cases, the
infrastructure provided by standards, such as Web Services Reliable Messaging (WSRM),
and APIs, such as JAX-WS, with their client-side asynchronous invocation support, can be
leveraged out of the box.
2) RESTful Web Services:
In Java EE 6, JAX-RS provides the functionality for Representational State Transfer (RESTful) web
services. REST is well suited for basic, ad hoc integration scenarios. RESTful web services, often
better integrated with HTTP than SOAP-based services are, do not require XML messages or WSDL
service–API definitions.
Project Jersey is the production-ready reference implementation for the JAX-RS specification. Jersey
implements support for the annotations defined in the JAX-RS specification, making it easy for
developers to build RESTful web services with Java and the Java Virtual Machine (JVM).
Because RESTful web services use existing well-known W3C and Internet Engineering Task Force
(IETF) standards (HTTP, XML, URI, MIME) and have a lightweight infrastructure that allows
services to be built with minimal tooling, developing RESTful web services is inexpensive and thus
has a very low barrier for adoption. You can use a development tool such as NetBeans IDE to further
reduce the complexity of developing RESTful web services.
A RESTful design may be appropriate when the following conditions are met.
The web services are completely stateless. A good test is to consider whether the interaction
can survive a restart of the server.
52
A caching infrastructure can be leveraged for performance. If the data that the web service
returns is not dynamically generated and can be cached, the caching infrastructure that web
servers and other intermediaries inherently provide can be leveraged to improve performance.
However, the developer must take care because such caches are limited to the HTTP GET
method for most servers.
The service producer and service consumer have a mutual understanding of the context and
content being passed along. Because there is no formal way to describe the web services
interface, both parties must agree out of band on the schemas that describe the data being
exchanged and on ways to process it meaningfully. In the real world, most commercial
applications that expose services as RESTful implementations also distribute so-called value-
added toolkits that describe the interfaces to developers in popular programming languages.
Bandwidth is particularly important and needs to be limited. REST is particularly useful for
limited-profile devices, such as PDAs and mobile phones, for which the overhead of headers
and additional layers of SOAP elements on the XML payload must be restricted.
Web service delivery or aggregation into existing web sites can be enabled easily with a
RESTful style. Developers can use such technologies as JAX-RS and Asynchronous
JavaScript with XML (AJAX) and such toolkits as Direct Web Remoting (DWR) to consume
the services in their web applications. Rather than starting from scratch, services can be
exposed with XML and consumed by HTML pages without significantly refactoring the
existing web site architecture. Existing developers will be more productive because they are
adding to something they are already familiar with rather than having to start from scratch
with new technology.
53
grow), and architectural simplicity (use off-the-shelf components, such as proxies or HTTP
routers). You would choose to use JAX-RS for your web application because it is easier for
many types of clients to consume RESTful web services while enabling the server side to
evolve and scale. Clients can choose to consume some or all aspects of the service and mash
it up with other web-based services.
6. Name the web service CalculatorWS, type org.me.calculator in Package, and click Finish.
54
The Projects window displays the structure of the new web service and the visual designer is
shown in the editor area.
7. Click Add Operation in the visual designer. A dialog box appears where you can define the
new operation.
55
56
8. Click Source and notice that the source code that has been generated in the previous steps is as
follows:
57
double k = i * j;
return k;
}
@WebMethod(operationName = "div")
public double divx(@WebParam(name = "i") double i, @WebParam(name = "j") double j) {
double k = i / j;
return k;
}
1. Right-click the project and choose Deploy. The IDE starts the application server, builds the
application, and deploys the application to the server. You can follow the progress of these
operations in the CalculatorWSJava (run-deploy) and GlassFish V4 or Tomcat tabs in the Output
view.
2. In the IDE's Projects tab, expand the Web Services node of the CalculatorWSJava project. Right-
click the CalculatorWS node, and choose Test Web Service.
58
3. The IDE opens the tester page in your browser, if you deployed a web application to GlassFish.
For the Tomcat Web Server and deployment of EJB modules, the situation is different:
If you deployed to GlassFish, type two numbers in the tester page, as shown below:
If deployed to Tomcat, the page will look like what is shown as below:
**** You can click on (WSDL File) and get the URL of the Web Service because we will use this
URL for our C#.Net client.
59
Practical 2: Consuming Java Web Service from C#.NET Client
1. Open Visual Studio 2010, Choose File > New Project (Ctrl-Shift-N).
2. Under C# tab Select Windows -> Windows Forms Application and give a name to your
project. Click on OK.
3. You can add 3 label, 3 text box and 4 buttons for a basic calculator as follow:
4. Now it is time to add our Java Web Service in to our client. In order to add Java Web Service
right click on CalculatorClient6 in Solution Window and select Add Service Reference.
60
As mentioned, we need to get the URL for our Java Web Service by clicking (WSDL File) link.
After clicking on the link the page will be seen as follows:
Copy the URL from browser. For my example the URL is:
http://localhost:8080/CalculatorWSJava/CalculatorWS?wsdl
5. Paste the URL and click on Go.
61
6. Now you can see all of your methods. Now click on OK. (You can change web reference
name)
7. ** You have to pay attention of the name of the description because we will use it to create
object to use our Web Service. In this example, the description name is
CalculatorWSJavaReference.
8. Double click on Form1 and go to source code.
9. Add using CaculatorClient6.CalculatorWSJavaReference;
10. Create an object from Web Service using:
//the name of the object is your service name (i.e CalculatorWS) concatenated //with Client
(see figure above) .Therefore it becomes – CalculatorWSClient.
CalculatorWSClient proxy = new CalculatorWSClient();
Now, you can double click on each button and call your methods by using this line:
textBox3.Text = proxy.add(Double.Parse(textBox1.Text),Double.Parse(textBox2.Text)).ToString();
62
11. Now, you can build and compile your C# Client.
63
9. Laboratory 9: RESTful Web Services
Objective:
This Lab is a demonstration on how to implement Web services. The objectives of this lab are the
following:
Understanding the basics concepts of Web services.
Understanding types of web services and when to use them.
Implement a simple web services using Netbeans.
Aim: Write an implementation of message passing interface and enable the communication of a
client and server computers using MPI primitives.
Requirements:
We are going to use Java networking and sockets to implement MPI. So, we need a computer that
have JDK and Netbeans IDE.
3. In the New Project wizard, choose Web in the category list on the left and Web Application from the
project list on the right and click Next.
64
4.At the next prompt, enter RESTLab as the project name.
5. At the next prompt, accept the defaults for the project, making sure that GlassFish is set as the server and
click Finish.
65
6. Click the Services and right click the Java DB to choose Create Database…
66
Right click the jdbc:derby://localhost:1527/travel and choose Connect
67
Click the to run it. After doing that, you will see the Travel table as following,
8. When the newly created project opens, right-click on the top-level node RESTLab and choose New →
Other from the context menu to start the New File wizard.
68
9. In the New File wizard, choose Persistence from the category list on the left and choose Entity Classes
from Database from the file type list on the right and click Next.
69
10. At the next dialog, choose New Data Source from the drop-down menu.
11. In the Create Data Source dialog box, enter jdbc/travel as the JNDI name and choose the TRAVEL
database from the Database Connection drop-down list and click OK.
70
12. The list of Available Tables on the left will show several tables. Highlight the Flight table and click the
Add > button to move it and all of the tables it references to the Selected Tables list on the right. Click Next.
13. On the next screen, click the Create Persistence Unit button to create a persistence unit for this project.
Accept the default values and click OK.
14. Enter lab.restful in the package name field and click Finish to create the entity classes.
71
15. Right-click on the top-level node in the project again and choose New → Other from the menu to open the
New File wizard.
16. Select Web Services from the category list on the left and choose RESTful Web Service from Entity
Classes on the right. Click Next.
72
17. Click the Add All >> button to move all of the available entity classes from the Available Entity Classes
list to the Selected Entity Classes list and click Next.
18.Click Finish on the next dialog to accept the default package names and create the web service.
73
19. Test the newly-created RESTful web service by right-clicking on the top-level node in the project and
selecting Test RESTful Web Services from the context menu.
20.NetBeans will start GlassFish and open your default browser to a page that will allow you to test the web
74
service. On the right side of the page you will see the table resources in the Travel database.
Click on flights in the list and a menu will appear on the right. Choose GET (application/xml) from the drop-
down menu and click the Test button to view the first 10 records of the flights table in the database.
Exercise:
1. Implement an application that consumes the web service we defined in the above practical.
75
10. Laboratory 10: Java Threads
Objective:
This Lab is a demonstration on how to implement Threads. We will be using the Java Programming
Language and it’s threading classes for implementing threads and different concepts related with
threads.
Aim: Thread programming in Java. Implement the following programs.
1. Write a program that executes two threads. Create the threads by implementing Runnable
interface.
a. One thread display “Hello!” every 1000 milliseconds 10 times, and
b. Another thread display “Hey” every 2000 milliseconds 10 times.
2. Implement Multithreaded Echo Client-Server program using Socket.
3. Implement producer consumer example.
Requirements:
We are going to use Java Threading and Networking classes and packages. So, we need a computer
that has JDK and some decent java IDE.
Background:
A thread is the flow of execution, from beginning to end, of a task in a program. A task is a program
unit that is executed independently of other parts of the program. A thread provides the mechanism
for running a task. With Java, you can launch multiple threads from a program concurrently. These
threads can be executed simultaneously in multiprocessor systems. Multithreading can make your
program more responsive and interactive, as well as enhance performance.
(a) Multiple threads are running on multiple CPUs. (b) Multiple threads share a single
CPU.
When your program executes as an application, the Java interpreter starts a thread for the main
method. When your program executes as an applet, the Web browser starts a thread to run the applet.
You can create additional threads to run concurrent tasks in the program. In Java, each task is an
76
instance of the Runnable interface, also called a runnable object. A thread is essentially an object that
facilitates the execution of a task.
77
By extending the Thread class
The second method to create a new thread is to extend Thread Superclass and create an instance
of that class. The newly created (extended) class must override the run() method, which is the
entry point for the new thread. It must also call start( ) to begin execution of the new thread.
Same as Runnable Interface. The Thread class itself implements Runnable, though its run method
does nothing. An application can subclass Thread, providing its own implementation of run, as in
the next example:
public class HeyThread extends Thread {
public void run() {
System.out.println(" Hey!");
}
public static void main(String args[]) {
HeyThread t = new HeyThread();
t.start();
}
}
Notice that both methods invoke Thread.start() in order to start the new thread.
78
Yield: You can use the yield() method to temporarily release time for other threads. Consider the run
method of HelloThread:
79
try {
for (int i = 1; i <= 10; i++) {
System.out.print(" Hello! ");
if (i == 5) ;
ht.join();
}
}
catch (InterruptedException ex) {
}
}
A new thread ht is created. It prints “Hey” 10 times. The “Hello” from 5 to 10 are printed after thread
ht is finished.
Thread Priority: Java assigns every thread a priority. By default, a thread inherits the priority of the
thread that spawned it. You can increase or decrease the priority of any thread by using the
setPriority() method, and You can get the thread's priority by using the getPriority() method.
Priorities are numbers ranging from 1 to 10. The Thread class has the int constants MIN_PRIORITY,
NORM_PRIORITY, and MAX_PRIORITY, representing 1, 5, and 10, respectively. The priority of the
main thread is Thread.NORM_PRIORITY.
The JVM always picks the currently runnable thread with the highest priority. If several runnable
threads have equally high priorities, the CPU is allocated to all of them in round-robin fashion. A
lower-priority thread can run only when no higher-priority threads are running.
ht.setPriority(Thread.MAX_PRIORITY);
Practical 1: Write a program that executes two threads. Create the threads by implementing
Runnable interface.
a. One thread display “Hello!” every 1 second 10 times, and
b. Another thread display “Hey” every 2 seconds 10 times.
Step 1: Implement the HelloThread class
public class HelloThread implements Runnable {
public void run() {
try {
for (int i = 1; i <= 10; i++) {
System.out.print(" Hello! ");
Thread.sleep(1000);
}
80
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
Step 2: Implement the HeyThread class
public class HeyThread extends Thread {
public void run() {
try {
for (int i = 1; i <= 10; i++) {
System.out.print(" Hey! ");
Thread.sleep(2000);
}
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
Step 3: Implementing a Test class which creates and runs both threads.
81
Step 1: Modifying the main server.
import java.net.*;
import java.io.*;
class ChatServer{
public static void main(String args[]) {
try {
ServerSocket server = new ServerSocket(8000);
System.out.println("Waiting for client to connect..");
while(true) {
new ServerThread(server.accept()).start();
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
Step 2: Creating the threaded server that will handle the communication between the client
and the server.
class ServerThread extends Thread {
private Socket socket = null;
82
String receive, send;
do{
receive = in.readLine();
System.out.println("Client Says: "+receive);
if(receive.equals("STOP"))
break;
System.out.print("Server Says : ");
send = br.readLine();
out.println(send);
}
while(true);
br.close();
in.close();
out.close();
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
83
11. Laboratory 11: Mutual Exclusion
Objective:
In this Lab we will be implementing one of the Mutual Exclusion algorithms. The followings are the
primary objectives of this lab session:
Understanding the basics and need of Mutual Exclusion.
Implement one mutual exclusion algorithm in Java.
Aim: Write a program to execute any one mutual exclusion algorithm.
Background:
Mutual Exclusion Using Token Ring
Here is a program to apply mutual exclusion using Token Ring algorithm. The program
contains two or more clients and a server. Token is circulated among all client sequentially a client
which has a token has right to send message to the server.
Theory:
Systems involving multiple processes are often most easily programmed using critical regions.
When a process has to read or update certain shared data structures, it first enters a critical region to
achieve mutual exclusion and ensure that no other process will use the shared data structures
at the same time.
Token Ring Algorithm
It is a completely different approach to achieving mutual exclusion in a distributing system
illustrated in the below diagram.
84
Here in fig.(a),we have a bus network with no inherent ordering of the processes. In
software, a logical ring is constructed in which each process is assigned a position in a ring as shown
in fig. (b).The ring positions may be allocated in numerical order of network addresses or some other
means. When the ring is initialized, process 0 is given a token. The token circulates around the ring.
It is passed from process k to process k+1 in point-to-point messages. When a process
acquires the token from its neighbour, it checks to see if it is attempting to enter a critical
region. If so, the process enters the region, does all the work it needs to, and leaves the region. After
it has exited, it passes the token along the ring. It is not permitted to enter a second region using the
same token. If a process is handed the token by its neighbour and is not interested in entering a
critical region, it just passes it along. As a result, when no processes want to enter any critical
regions, the token just circulates at high speed around the ring.
Only one process has the token at any instant, so only one process can actually be in a critical region.
Once a process decides it wants to enter a critical region, it worst it will have to wait for every other
process to enter and leave one critical region.
If the token is ever lost, it must be regenerated. In fact, detecting that it is lost is difficult, since
the amount of time between successive appearances of the token on the network is
unbounded. The fact that token has not been spotted for an hour does not mean that it has
been lost; somebody may still be using it.
The algorithm also runs into trouble if a process crashes, but recovery is easier than in other cases. If
we require a process receiving the token to acknowledge receipt, a dead process will be
detected when its neighbour tries to give it the token and fails. At that point the dead process can be
removed from the group, and the token holder can throw the token over the head of the dead
process to the next number down the line, or the one that, if necessary. By doing so requires
that everyone maintains the current ring configuration.
Practical 1: Implement a simple ring based Mutual exclusion algorithm using a single server and two
clients. The clients pass around the token. A client that currently has the token can send a single message to
the server.
Step 1: Implementing the server.
import java.io.*;
import java.net.*;
85
public class TokenServer {
public static void main(String agrs[]) throws Exception {
System.out.println("Server ready to accept data\n");
while(true)
{
Server sr=new Server();
sr.recPort(9000);
sr.recData();
}
}
}
class Server {
boolean hasToken=false;
boolean sendData=false;
int recport;
DatagramSocket ds;
DatagramPacket dp;
String str;
ds=new DatagramSocket(recport);
dp=new DatagramPacket(buff,buff.length);
ds.receive(dp);
ds.close();
str=new String(dp.getData(),0,dp.getLength());
System.out.println("The message is "+str);
}
}
86
Step 2: Implementing the first client.
import java.io.*;
import java.net.*;
public class TokenClient1 {
static boolean hasToken ;
public static void main(String arg[]) throws Exception {
InetAddress lclhost=InetAddress.getLocalHost();
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));;
DatagramSocket ds = new DatagramSocket(8002);
TokenClient10 tkcl = new TokenClient21(lclhost,ds);
tkcl.setSendPort(9002);
TokenClient10 ser = new TokenClient21(lclhost,ds);
ser.setSendPort(9000);
String str1;
hasToken = true;
while(true) {
if(hasToken == true)
{
System.out.println("Do you want to enter the Data --> YES/NO");
str1=br.readLine();
if(str1.equalsIgnoreCase("yes"))
{
ser.sendData();
tkcl.sendToken();
hasToken=false;
}
else if(str1.equalsIgnoreCase("no"))
{
tkcl.sendToken();
hasToken=false;
}
}
else
{
System.out.println("entering recieving mode");
tkcl.recData();
87
hasToken=true;
}
}
}
}
class TokenClient10
{
InetAddress lclhost;
int sendport,recport;
DatagramSocket ds;
TokenClient10(InetAddress lclhost,DatagramSocket ds) throws Exception
{
this.lclhost = lclhost;
this.ds = ds;
this.recport = ds.getPort();
}
void setSendPort(int sendport)
{
this.sendport = sendport;
}
void sendData() throws Exception
{
BufferedReader br;
DatagramPacket dp;
System.out.println("Enter the Data");
br=new BufferedReader(new InputStreamReader(System.in));
String str = "Client one....." + br.readLine();
dp = new DatagramPacket(str.getBytes(),str.length(),lclhost,sendport);
ds.send(dp);
System.out.println("Data Sent");
}
void sendToken() throws Exception
{
BufferedReader br;
String str="Token";
88
DatagramPacket dp = new DatagramPacket(str.getBytes(),str.length(),lclhost,sendport);
ds.send(dp);
System.out.println("Token Sent");
}
void recData()throws Exception
{
String msgstr;
byte buffer[] = new byte[256];
DatagramPacket dp;
dp = new DatagramPacket(buffer,buffer.length);
ds.receive(dp);
msgstr = new String(dp.getData(),0,dp.getLength());
System.out.println("Recieved "+msgstr);
}
}
Step 3: Implementing the second client. The two clients are very similar so you can modify the
previous client than writing this one from scratch.
import java.io.*;
import java.net.*;
public class TokenClient1
{
static boolean hasToken ;
public static void main(String arg[]) throws Exception
{
InetAddress lclhost=InetAddress.getLocalHost();
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
DatagramSocket ds = new DatagramSocket(9002);
TokenClient31 tkcl = new TokenClient20(lclhost,ds);
tkcl.setSendPort(8002);
TokenClient31 ser = new TokenClient20(lclhost,ds);
ser.setSendPort(9000);
String str1;
while(true)
{
if(hasToken == true)
{
89
System.out.println("Do you want to enter the Data --> YES/NO");
str1=br.readLine();
if(str1.equalsIgnoreCase("yes"))
{
ser.sendData();
tkcl.sendToken();
hasToken=false;
}
else if(str1.equalsIgnoreCase("no"))
{
tkcl.sendToken();
hasToken=false;
}
}
else
{
System.out.println("entering recieving mode");
tkcl.recData();
hasToken=true;
}
}
}
}
class TokenClient20
{
InetAddress lclhost;
int sendport,recport;
DatagramSocket ds;
TokenClient20(InetAddress lclhost,DatagramSocket ds) throws Exception
{
this.lclhost = lclhost;
this.ds = ds;
this.recport = ds.getPort();
}
void setSendPort(int sendport)
{
this.sendport = sendport;
90
}
void sendData() throws Exception
{
BufferedReader br;
DatagramPacket dp;
System.out.println("Enter the Data");
br=new BufferedReader(new InputStreamReader(System.in));
String str = "Client Two....." + br.readLine();
dp = new DatagramPacket(str.getBytes(),str.length(),lclhost,sendport);
ds.send(dp);
System.out.println("Data Sent");
}
void sendToken() throws Exception
{
BufferedReader br;
String str="Token";
DatagramPacket dp = new DatagramPacket(str.getBytes(),str.length(),lclhost,sendport);
ds.send(dp);
System.out.println("Token Sent");
}
void recData()throws Exception
{
String msgstr;
byte buffer[] = new byte[256];
DatagramPacket dp;
dp = new DatagramPacket(buffer,buffer.length);
ds.receive(dp);
msgstr = new String(dp.getData(),0,dp.getLength());
System.out.println("Recieved "+msgstr);
}
}
Exercise:
1.Modify the above program to support any number of client computers in the system.
2.Implement the centralized and distributed mutual exclusion algorithms.
91
12. Laboratory 12: Election Algorithm
Objective:
In this Lab we are going to implement an election algorithm in a distributed environment. There will
be different computers in the environment and we want to elect a coordinator. The followings are the
primary objectives of this lab session:
Understanding the basics of election algorithms.
Discussion of how elections are held in a distributed environment.
Implement the election algorithms.
Aim: Write a program to implement any one election algorithm.
Theory:
An algorithm for choosing a unique process to play a particular role is called an election algorithm.
A ring-based election algorithm:
Each process pi has a communication channel to the next process in the ring, p (i + 1) mod N and all
messages are sent clockwise around the ring. We assume that no failures occur, and that the system
is asynchronous. The goal of this algorithm is to elect a single process called the coordinator, which
is the process with the largest identifier.
Initially, every process is marked as a non-participant in an election. Any process can begin an
election. It proceeds by marking itself as a participant, placing its identifier in an election message
and sending it to its clockwise neighbour. When a process receives an election message, it compares
the identifier in the message with its own. If the arrived identifier is greater, then it forwards the
message to its neighbour. If the arrived identifier is smaller and the receiver is not a participant, then
it substitutes its own identifier in the message and forwards it; but it does not forward the message if
it is already a participant. On forwarding an election message in any case, the process marks itself as
a participant.
If, however, the received identifier is that of the receiver itself, then this process’s identifier must be
the greatest, and it becomes the coordinator. The coordinator marks itself as a non-participant once
more and sends an elected message to its neighbour, announcing its election and enclosing its
identity. When a process pi receives an elected message, it marks itself as a nonparticipant, sets its
variable electedi to the identifier in the message and, unless it is the new coordinator, forwards the
message to its neighbour.
It is easy to see that condition E1 is met. All identifiers are compared, since a process must receive
its own identifier back before sending an elected message. For any two processes, the one with the
92
larger identifier will not pass on the other’s identifier. It is therefore impossible that both should
receive their own identifier back.
Condition E2 follows immediately from the guaranteed traversals of the ring (there are no failures).
Note how the non-participant and participant states are used so that duplicate messages arising when
two processes start an election at the same time are extinguished as soon as possible, and always
before the ‘winning’ election result has been announced.
If only a single process starts an election, then the worst-performing case is when its anti-clockwise
neighbour has the highest identifier. A total of N – 1 messages are then required to reach this
neighbour, which will not announce its election until its identifier has completed another circuit,
taking a further N messages. The elected message is then sent N times, making 3N – 1 messages in
all. The turnaround time is also 3N – 1 , since these messages are sent sequentially.
Bully Algorithm:
The bully algorithm allows processes to crash during an election, although it assumes that message
delivery between processes is reliable. Unlike the ring-based algorithm, this algorithm assumes that
the system is synchronous: it uses timeouts to detect a process failure. Another difference is that the
ring-based algorithm assumed that processes have minimal a priori knowledge of one another: each
knows only how to communicate with its neighbour and none knows the identifiers of the other
processes. The bully algorithm, on the other hand, assumes that each process knows which processes
have higher identifiers, and that it can communicate with all such processes.
There are three types of message in this algorithm: an election message is sent to announce an
election; an answer message is sent in response to an election message and a coordinator message is
sent to announce the identity of the elected process – the new ‘coordinator’. A process begins an
election when it notices, through timeouts, that the coordinator has failed. Several processes may
discover this concurrently.
Since the system is synchronous, we can construct a reliable failure detector. There is a maximum
message transmission delay, Ttrans , and a maximum delay for processing a message Tprocess .
Therefore, we can calculate a time T = 2Ttrans + Tprocess that is an upper bound on the time that
can elapse between sending a message to another process and receiving a response. If no response
arrives within time T, then the local failure detector can report that the intended recipient of the
request has failed.
The process that knows it has the highest identifier can elect itself as the coordinator simply by
sending a coordinator message to all processes with lower identifiers. On the other hand, a process
with a lower identifier can begin an election by sending an election message to those processes that
have a higher identifier and awaiting answer messages in response. If none arrives within time T, the
93
process considers itself the coordinator and sends a coordinator message to all processes with lower
identifiers announcing this. Otherwise, the process waits a further period T’ for a coordinator
message to arrive from the new coordinator. If none arrives, it begins another election.
If a process pi receives a coordinator message, it sets its variable electedi to the identifier of the
coordinator contained within it and treats that process as the coordinator. If a process receives an
election message, it sends back an answer message and begins another election – unless it has begun
one already.
When a process is started to replace a crashed process, it begins an election. If it has the highest
process identifier, then it will decide that it is the coordinator and announce this to the other
processes. Thus it will become the coordinator, even though the current coordinator is functioning. It
is for this reason that the algorithm is called the ‘bully’ algorithm.
Exercise:
1.Implement the ring based election algorithm. Consider the previous lab’s implementation of ring
based mutual exclusion and modify it to perform an election algorithm.
2.Implement the bully election algorithm.
94