One big problem with the example program in its current form is the fact that sending clients can overwrite each other's data before receiving clients have a chance to get and process it. This lesson adapts the server code to ensure all orders are processed (nothing is overwritten), and all orders are processed in the order they are received by the server.
The example adapts the Part 2, Lesson 2: User Interfaces Revisited example to wrap the fruit order data into a single data object and send the data object over the network to the server. This is more efficient than sending each unit of data separately.
The DataOrder.java class is very simple. It defines the fields that wrap and store the fruit order data. It has no methods. It implements the Serializable
interface so its data can be serialized, and written to and read from a file as a single unit.
Object serialization transforms an object's data to a bytestream that represents the state of the data. The serialized form of the data contains enough information to recreate the object with its data in a similar state to what it was when saved.
import java.io.*;
class DataOrder implements Serializable{
String apples, peaches, pears, cardnum, custID;
double icost;
int itotal;
}
The RMIClient1.java program is modified to use the DataOrder
class to send the order data over the net. The RMIClient1.actionPerformed
method creates an instance of the DataOrder
class and initializes its fields with order data retrieved from the user interface text fields and areas.
public void actionPerformed(ActionEvent event){
Object source = event.getSource();
Integer applesNo, peachesNo, pearsNo, num;
Double cost;
String number, text, text2;
DataOrder order = new DataOrder();
if(source == purchase){
order.cardnum = creditCard.getText();
order.custID = customer.getText();
order.apples = appleqnt.getText();
order.peaches = peachqnt.getText();
order.pears = pearqnt.getText();
order.icost
if(order.apples.length() > 0){
try{
applesNo = Integer.valueOf(order.apples);
order.itotal += applesNo.intValue();
} catch (java.lang.NumberFormatException e) {
appleqnt.setText("Invalid Value");
}
} else {
order.itotal += 0;
}
order.itotal
num = new Integer(order.itotal);
text = num.toString();
this.items.setText(text);
order.icost
order.icost = (order.itotal * 1.25);
cost = new Double(order.icost);
text2 = cost.toString();
this.cost.setText(text2);
try{
send.sendOrder(order);
} catch (Exception e) {
System.out.println("Cannot send data to server");
}
The Send.java and RemoteServer.java classes are much simpler in this lesson. They have one getXXX
method that returns an instance of DataOrder
, and one setXXX
method that accepts an instance of DataOrder
.
Send.java
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Send extends Remote {
public void sendOrder(DataOrder order)
throws RemoteException;
public DataOrder getOrder() throws RemoteException;
}
The RemoteServer.sendOrder
method accepts a DataOrder
instance as input, and stores each order in a separate file where the file name is a number. The first order received is stored in a file named 1, the second order is stored in a file named 2, and so forth.
To keep track of the file names, the value
variable is incremented by 1 each time the sendOrder
method is called, converted to a String
, and used for the file name in the serialization process.
Objects are serialized by creating a serialized output stream and writing the object to the output stream. In the code, the first line in the try
block creates a FileOutputStream
with the file name to which the serialized object is to be written.
The next line creates an ObjectOutputFileStream
from the file output stream. This is the serialized output stream to which the order
object is written in the last line of the try
block.
RemoteServer.java
public void sendOrder(DataOrder order){
value += 1;
num = new Integer(value);
orders = num.toString();
try{
FileOutputStream fos =
new FileOutputStream(orders);
ObjectOutputStream oos =
new ObjectOutputStream(fos);
oos.writeObject(order);
}catch (java.io.FileNotFoundException e){
System.out.println(e.toString());
}catch (java.io.IOException e){
System.out.println(e.toString());
}
}
RemoteServer.getOrder
sendOrder
get
But first, this method checks the value
variable. If it is equal to zero, there are no orders to get from a file and view, and if it is greater than the value in the get
variable, there is at least one order to get from a file and view. As each order is viewed, the get
variable is incremented by 1.
public DataOrder getOrder(){
DataOrder order = null;
if(value == 0){
System.out.println("No Orders To Process");
}
if(value > get){
get += 1;
num = new Integer(get);
orders = num.toString();
try{
FileInputStream fis =
new FileInputStream(orders);
ObjectInputStream ois =
new ObjectInputStream(fis);
order = (DataOrder)ois.readObject();
}catch (java.io.FileNotFoundException e){
System.out.println(e.toString());
}catch (java.io.IOException e){
System.out.println(e.toString());
}catch (java.lang.ClassNotFoundException e){
System.out.println(e.toString());
}
}else{
System.out.println("No Orders To Process");
}
return order;
}
The RMIClient2.actionPerformed method gets an order
object and references its fields to display data in the user interface.
if(source == view){
try{
order = send.getOrder();
creditNo.setText(order.cardnum);
customerNo.setText(order.custID);
applesNo.setText(order.apples);
peachesNo.setText(order.peaches);
pearsNo.setText(order.pears);
cost = order.icost;
price = new Double(cost);
unit = price.toString();
icost.setText(unit);
items = order.itotal;
itms = new Integer(items);
i = itms.toString();
itotal.setText(i);
} catch (Exception e) {
System.out.println("Cannot send data to server");
}
}
You can find more information on serialization in the Reading and Writing (but no 'rithmetic) lesson in The Java Tutorial.