Sometimes we are given requirements to create a system that needs to have objects move from one computer to the other. One way to do this would be to turn the objects into String objects, send them via the socket to the other computer, then re-create the object on the other machine. This will work, but there is a better way. Listings 20.3 and 20.4 used the ObjectInputStream and the ObjectOutputStream classes to transfer objects back and forth. The object being sent was just a String, however. You might be wondering how you would send a complex object using sockets. Listings 20.5, 20.6, and 20.7 show you a fairly complex set of objects that can be sent using these same streams. Listing 20.5 The ComplexEmployee.java File /* * ComplexEmployee.java * * Created on October 3, 2002, 3:42 PM */ package ch20; import java.io.*; import java.util.*; /** * * @author Stephen Potts */ public class ComplexEmployee implements Serializable { private String name; private int salary; /** Creates a new instance of ComplexEmployee */ public ComplexEmployee(String name, int salary) { this.name = name; this.salary = salary; } public String getName() { return name; } public int getSalary() { return this.salary; } } This class is ordinary except for the fact that it is declared to be serializable. The serializability of this class is not hard to understand; its data consists of one String and one int, both of which are serializable. The Serializable interface does not require us to implement any special methods. It is what is called a marker interface. By implementing this interface, we are stating that moving this class from one machine to another will not break it because it contains no machine-specific data. An example of machine-specific data would be a handle to a Socket. The data in the Socket object pertains to addresses on this specific machine. Moving it to another machine would render the values in the object useless. public class ComplexEmployee implements Serializable Listing 20.6 contains the code for the ComplexDepartment class. Listing 20.6 The ComplexDepartment.java File /* * ComplexDepartment.java * * Created on October 3, 2002, 3:54 PM */ package ch20; import java.io.Serializable; /** * * @author Stephen Potts */ public class ComplexDepartment implements Serializable { private String name; private ComplexEmployee manager; public ComplexDepartment(String name) { this.name = name; } public String getName() { return this.name; } public ComplexEmployee getManager() { return this.manager; } public void addManager(ComplexEmployee e) { manager = e; } } The only really interesting feature of this class is the fact that one of its private class-level variables is of type ComplexEmployee. private String name; private ComplexEmployee manager; This was done to make the situation more difficult for the Java serialization mechanism to handle. The third class that we will need for this experiment is called ComplexCompany. It is shown here in Listing 20.7. Listing 20.7 The ComplexCompany Example /* * ComplexCompany.java * * Created on October 3, 2002, 3:39 PM */ package ch20; /** * * @author Stephen Potts */ import java.util.Vector; import java.util.Iterator; import java.util.Set; import java.io.Serializable; public class ComplexCompany implements Serializable { private String name; private ComplexEmployee president; private Vector departments; /** Creates new Company */ public ComplexCompany(String name) { this.name = name; departments = new Vector(); } public String getName() { return this.name; } public void addDepartment(ComplexDepartment dept) { departments.addElement(dept); } public ComplexEmployee getPresident() { return this.president; } public void addPresident(ComplexEmployee e) { this.president = e; } public Iterator getDepartmentIterator() { return departments.iterator(); } public void printCompanyObject() { System.out.println("The company name is " + getName()); System.out.println("The company president is " + getPresident().getName()); System.out.println(" "); Iterator i = getDepartmentIterator(); while (i.hasNext()) { ComplexDepartment d = (ComplexDepartment)i.next(); System.out.println(" The department name is " + d.getName()); System.out.println(" The department manager is " + d.getManager().getName()); System.out.println(" "); } } } This class is designed specifically as a brain buster. It contains three different variables. The first one is a String, the second one is a ComplexEmployee, and the third is a Vector of ComplexDepartments. private String name; private ComplexEmployee president; private Vector departments; The Vector is the most interesting. We have placed it here to show how well the serialization mechanism in the JVM works. If it can serialize a Vector of class objects that is a member variable of another class properly, we can be duly impressed. Add to that the fact that the classes in the vector each contain a member variable that is a class, and you have a very good test of the strength of the serialization mechanism. /** Creates new Company */ public ComplexCompany(String name) { this.name = name; departments = new Vector(); } The addElement() method is how you add objects to the vector. public void addDepartment(ComplexDepartment dept) { departments.addElement(dept); } The recommended way to step through a Vector is by using the methods of the Iterator class. The responsibility for returning the Iterator is placed on the class that contains the vector. public Iterator getDepartmentIterator() { return departments.iterator(); } The ComplexCompany class knows how to print itself out to standard output for us to inspect. public void printCompanyObject() { System.out.println("The company name is " + getName()); System.out.println("The company president is " + getPresident().getName()); System.out.println(" "); The department Vector must be stepped through and each item read to make sure that we have all the departments printed out. Iterator i = getDepartmentIterator(); while (i.hasNext()) { Notice the explicit cast that is done here. The Vector stored its members as being of type Object. You must perform a downcast to the correct datatype. ComplexDepartment d = (ComplexDepartment)i.next(); System.out.println(" The department name is " + d.getName()); System.out.println(" The department manager is " + d.getManager().getName()) The client class creates the object, opens a socket, and writes the object to the socket. Listing 20.8 shows this class. Listing 20.8 The ComplexSocketClient.java File /* * ComplexSocketClient.java * * Created on October 2, 2002, 5:34 PM */ package ch20; import java.net.*; import java.io.*; /** * * @author Stephen Potts */ public class ComplexSocketClient { Socket socket1; int portNumber = 1777; String str = ""; /** Creates a new instance of ComplexSocketClient */ public ComplexSocketClient() { try { socket1 = new Socket(InetAddress.getLocalHost(), portNumber); ObjectInputStream ois = new ObjectInputStream(socket1.getInputStream()); ObjectOutputStream oos = new ObjectOutputStream(socket1.getOutputStream()); ComplexCompany comp = new ComplexCompany("The Blazer Company"); ComplexEmployee emp0 = new ComplexEmployee("Leslie Waller", 1000); comp.addPresident(emp0); ComplexDepartment sales = new ComplexDepartment("Sales"); ComplexEmployee emp1 = new ComplexEmployee("Grant Jackson", 1200); sales.addManager(emp1); comp.addDepartment(sales); ComplexDepartment accounting = new ComplexDepartment("Accounting"); ComplexEmployee emp2 = new ComplexEmployee("Clay Cain", 1230); accounting.addManager(emp2); comp.addDepartment(accounting); ComplexDepartment maintenance = new ComplexDepartment("Maintenance"); ComplexEmployee emp3 = new ComplexEmployee("Greg Hladlick", 1020); maintenance.addManager(emp3); comp.addDepartment(maintenance); oos.writeObject(comp); while ((str = (String) ois.readObject()) != null) { System.out.println(str); oos.writeObject("bye"); if (str.equals("bye bye")) break; } ois.close(); oos.close(); socket1.close(); } catch (Exception e) { System.out.println("Exception " + e); } } public static void main(String args[]) { ComplexSocketClient lsp = new ComplexSocketClient(); } } We first create the ComplexCompany object. ComplexCompany comp = new ComplexCompany("The Blazer Company"); Next, we add the president. ComplexEmployee emp0 = new ComplexEmployee("Leslie Waller", 1000); comp.addPresident(emp0); Next, we add a department. ComplexDepartment sales = new ComplexDepartment("Sales"); ComplexEmployee emp1 = new ComplexEmployee("Grant Jackson", 1200); We add the department manager. sales.addManager(emp1); Then, we add the department to the company object. Following that, we add two more departments in the same fashion. Then, we write the object to the stream that is connected to the Socket. oos.writeObject(comp); Finally, we wait in a while loop for a response. while ((str = (String) ois.readObject()) != null) { System.out.println(str); oos.writeObject("bye"); if (str.equals("bye bye")) break; } The server is fairly simple also. Listing 20.9 shows the code for this application. Listing 20.9 The ComplexSocketServer.java File /* * ComplexSocketServer.java * * Created on October 2, 2002, 5:24 PM */ package ch20; import java.net.*; import java.io.*; import java.util.*; /** * * @author Stephen Potts */ public class ComplexSocketServer { ServerSocket servSocket; Socket fromClientSocket; int cTosPortNumber = 1777; String str; ComplexCompany comp; /** Creates a new instance of ComplexSocketServer */ public ComplexSocketServer() { // Create ServerSocket to listen for connections try { servSocket = new ServerSocket(cTosPortNumber); // Wait for client to connnect, then get Socket System.out.println("ServerSocket created"); System.out.println("Waiting for a connection on " + cTosPortNumber); fromClientSocket = servSocket.accept(); System.out.println("fromClientSocket accepted"); // Use ObjectOutputStream to send String to the client ObjectOutputStream oos = new ObjectOutputStream(fromClientSocket.getOutputStream()); //Use ObjectInputStream to get String from client ObjectInputStream ois = new ObjectInputStream(fromClientSocket.getInputStream()); while ((comp = (ComplexCompany) ois.readObject()) != null) { comp.printCompanyObject(); oos.writeObject("bye bye"); break; } oos.close(); // Close Sockets fromClientSocket.close(); } catch (Exception e) { System.out.println("Exception " + e); } } public static void main(String args[]) { ComplexSocketServer css = new ComplexSocketServer(); } } The really amazing thing about this application is how little it does. All the work of turning the object into a stream, and then turning it back into an object again is done by the JVM. After reading the object, it is cast to a handle of type ComplexCompany. while ((comp = (ComplexCompany) ois.readObject()) != null) { We then use this handle to call the printCompanyObject() method. In a real-world application, we would obviously do more with this object. comp.printCompanyObject(); Following that, we write a message back to the client so that it will know that we got the objects and processed them. The output from running client is shown here: Server returns initialize bye bye The output from running the server is shown here. ServerSocket created Waiting for a connection on 1777 fromClientSocket accepted The company name is The Blazer Company The company president is Leslie Waller The department name is Sales The department manager is Grant Jackson The department name is Accounting The department manager is Clay Cain The department name is Maintenance The department manager is Greg Hladlick Notice that the object contained all the information, including what was stored in the Vector. |