Skip to content
Nathan Jent edited this page Dec 4, 2015 · 19 revisions

Network Data Message Model

The network model has been implemented using JavaBeans for serialization and reflection. Currently the networking-chat branch contains test classes implementing a partially functional chat message application. I encourage you to check out the branch and look at the code. I plan to add polling to the clients to fetch updates from the server periodically such as chat messages from other users and updated schedules.

Here's how it works:

  1. Client sends a data object (JavaBean) with a specified channel to the server using the NetworkManager class to encode it to XML and transmit it through a socket connection.
  2. Server receives the XML and decodes it back into a data object using NetworkManager.
  3. The NetworkManager looks through the resulting object for static methods annotated with a NetworkHandler channel. The data object classes and enclosed NetworkHandler methods must be registered with the NetworkManager prior to sending or receiving data objects. Otherwise the data object is ignored.
  4. If the NetworkManager finds qualified methods it will invoke them.
  5. Depending on the method implementation the server can update the database or process the object in some other way.
  6. If the method returns a response message to the client it is sent back over the same socket connection. Otherwise the socket is disconnected.
  7. The returned object are processed the same way when received by the client.
  8. The message can loop continuously sending messages back and forth until one of the methods returns a message with the channel set to "end" or a null data object.

I will now discuss how you should implement data objects that you need to send over the network. These data objects can serve several purposes; ie. persistence to the database, processing data on the server, sending files.

public class ChatData implements Serializable {

	private static final long serialVersionUID = -5939310825171066784L;

First a Javabean should implement Serializable or some other interface that implements Serializable. We discussed in class how the serialVersionUID is used.

NetworkManager.registerNetworkHandlerClass(ChatData.class);

The ChatData JavaBean object is then registered with the NetworkManager. If the NetworkManager doesn't know about an object it will ignore it. This register method also tests that the class has been implemented correctly.

NetworkManager.sendMessage("chatPost", new ChatData(text, from, to));

Data objects are sent over the network inside of a MessageHolder which takes a channel argument and the data object argument.

	private String text;
	private String from;
	private String to;

Next define some properties. 'Primitive' Java types will be automatically handled by the XMLEncoder used in the NetworkManager. If you include properties that are not primitives, then you will need to add a persistence delegate that will tell the encoder how to handle the 'non-primitive' property.

	public ChatData() { }

A default constructor is required for the XMLDecoder to recreate the object from the xml.

	public ChatData(String messageText, String from, String to) {
		this.text = messageText;
		this.from = from;
		this.to = to;
	}

This constructor helps for easy initialization.

	/**
	 * Posts a chat message to the server.
	 * @return The message
	 */
	@NetworkHandler(channel = "chatPost")
	public static MessageHolder postChat(ChatData message) {
		// TODO log chat messages to database
		return new MessageHolder("chatPostConfirmation", message);
	}
	
	/**
	 * Receives a chat message, and updates the user's chat panel.
	 * @return The message
	 */
	@NetworkHandler(channel = "chatPostConfirmation")
	public static MessageHolder receiveChat(ChatData message) {
		// TODO update chat panel
		String text = message.to  + ": " + message.text + "\n";
		TestClient.getChatPanel().appendToChatLog(text);
		return new MessageHolder("end", null);
	}

postChat and receiveChat methods are examples of reflection. Each method is given a NetworkHandler annotation with a unique channel. This channel is used when sending the object over the network it tells the receiver which method it should invoke in the object. These methods can be used to do things with the object on the server or client. For example you could access the database and write the object properties to database fields.

You can see in the postChat method that a new MessageHolder is returned. The MessageHolder is supplied with the channel argument "chatPostConfirmation" and the original message argument. This means that when the server receives this message it will invoke the postChat method and return the same message to the client on a different channel, the client will then receive the message and invoke the receiveChat method and not the postChat method.

The receiveChat method returns a MessageHolder with the channel "end" and a null. This causes the receiver to end the network message loop and close the socket.

Finally, but not shown here, getter and setter methods are included for each property in the JavaBean object. These are used during the encoding/decoding process. I usually override the toString method as well including the properties in the returned string. It helps with debugging.

Client Database Requests Through Server

A database integration branch has been created to show an example of how clients can request database resources over the network. Review.java has been implemented with NetworkHandler methods that a client can request to invoke on the server. The server will then execute the SQL statement and return the results to the requesting client. There is much left to be implemented, but this design should prove functional.

The following is an example SELECT statement from Review.java.

private static final String FETCH_STMT = 
		"SELECT id, reviewerId, subjectId, jobId, date, text, rating "
		+ "FROM reviews WHERE id = ?";

@NetworkHandler(channel = "fetchReview")
	public MessageHolder fetch(Review review) {
		boolean success = true;
		try {
			Database db = new Database();
			PreparedStatement fetchStmt = null;
			fetchStmt = db.getConnection().prepareStatement(FETCH_STMT);
			try {
				fetchStmt.setInt(1, review.id);
				ResultSet rs = fetchStmt.executeQuery();
				while (rs.next()) {
					review.id = rs.getInt(1);
					review.reviewerId = rs.getInt(2);
					review.subjectId = rs.getInt(3);
					review.jobId = rs.getInt(4);
					review.date = rs.getString(5);
					review.text = rs.getString(6);
					review.rating = rs.getInt(7);
				}
			} catch (SQLException e) {
				success = false;
			} finally {
				try {
					fetchStmt.close();
				} catch (NullPointerException e) {
					// ignore
				}
			}
		} catch (SQLException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		if (success) {
			return new MessageHolder("returnedReview", review);
		}
		return new MessageHolder("sqlFailed", review);
	}

	@NetworkHandler(channel = "returnedReview")
	public MessageHolder returned(Review review) {
		//TODO update UI with requested review details
		return new MessageHolder("end", review);
	}

OpenID login

Live chat

Live chat will feature fully editable text and document transfers. Files and chat will be fully encrypted with AES 256 (Luke will do this) and will feature some kind of handoff system between the client in the server if both ends agree. So, instead of communicating through the server, you can communicate directly with the other person.

Proposed Network Data Transfer

[{8 bit ID(Dynamic)}|{8 bit time}|{message queue #, 16 bits}| Data(Variant)]

This would be a UDP-style data transfer that I'm proposing. It allows for API entension through a negative time passage, as well as in-order messaging like TCP.

Asynchronous messages

Email notifications from the server to a users profile email.

JDBC

  • Derby library includes a JDBC driver.

XML

  • Parser for XML/JSON object serialization
  • We(as in all groups) should get a plan together for the XML schema that is going to be passed from client to server and server to client Homeowner UI