COMP 310
Fall 2015

Lec32: ChatApp Design...

Home  Info  Owlspace   Java Resources  Eclipse Resources  Piazza

Current design issues:

  1. Use case for adding a user
  2. Processing DataPacket<T>
  3. Return value of sending a data packet?   
  4. Comparing remote stubs and serialized objects.
  5. Processing new commands

Weekend advice:   Write code to try to get your proposed API to work.

  1. Create an implementation of your group's interfaces in your own rmiChat package and build your code off of that.  
  2. Add/modify methods, interfaces as you think are necessary to make the chat system work.  
  3. Document your changes and suggestions in the wiki.
  4. Post change and modification ideas toPiazza and send out e-mail telling everyone to please look at your suggestions.

 

 

Multiple chat room support

Multiple Chat Room Support

(See the Java Resources web page on How To Make Mini-MVC's Inside a Main MVC)

Comparing IUsers

Update 12/2015:  Java 8 RMI stubs do support equals() and hashCode() comparisons, up to a point and can be used successfully as keys in hash tables, etc.   See the Java Resource site page on Comparing Deserialized Objects for more information and possible restrictions.   This will eliminate the need for the various solutions discussed below for many, but not all situations.

Comparing RMI stubs is a tricky business because the stubs are separately serialized objects, so using trying to compare to stubs for equality using the usual equals() method always fails:  stub1of User.equals(stub2of User) always returns false.

The only way to compare 2 RMI stubs is to compare all the available public values obtainable through the stubs.   There are (at least) two different ways to achieve this, depending on whether or not you have access to the actual comparison process:

  1. Your code always performs the comparison:   write a utility method, ala "public boolean compareUsers(IUser user1, IUser user2){...}" that will return true or false depending on if the two stubs are "equal" or not.   The rest of the code always calls this invariant, encapsulated comparison method. 
  2. You do NOT have access to the comparison process:   For instance, you are using a dictionary, where the comparison takes place deep inside the dictionary using the objects' equals() and hashCode() methods.  In that case, you need decorate the IUser with something that overrides the equals() and hashCode() methods to retrieve transparently retrieve the necessary information but without throwing RemoteException. A private class like such will do the trick:
	/**
	 * Private class to decorate an IUser to override the equals() and hashCode() 
	 * methods so that a dictionary, e.g. Hashtable, can properly compare IUsers.
	 * @author swong
	 *
	 */
	private class ProxyUser implements IUser, Serializable {
		
		/**
		 * Required for proper serialization
		 */
		private static final long serialVersionUID = 5682755540794448769L; // regenerate this!
		
		/**
		 * The decoree
		 */
		private IUser stub;

		/**
		 * Constructor for the class
		 * @param stub The decoree
		 */
		public ProxyUser(IUser stub){
			this.stub = stub;
		}
		
		/**
		 * No-op decoration of the getName method.   Just pass the request to the decoree.
		 * @return The name of the user.
		 * @throws RemoteException
		 */
		@Override
		public String getName() throws RemoteException {
			return stub.getName();
		}

		/**
		 * Get the decoree
		 * @return the decoree
		 */
		public IUser getStub() {
			return stub;
		}


		/**
		 * No-op decoration of the sendMsg method.   Just pass the request to the decoree.
		 * @param data The data to be sent
		 * @param sender  The sender of the data
		 * @return The status of the transmission
		 * @throws RemoteException
		 */
		@Override
		public IStatus sendMsg(ADataPacket data, IUser sender) throws RemoteException {
			return stub.sendMsg(data, sender);
		}


		/**
		 * Overriden equals() method to simply compare hashCodes.
		 * @return  Equal if the hashCodes are the same.  False otherwise.
		 */
		@Override
		public boolean equals(Object other){
			return hashCode() == other.hashCode();
		}
		

		/**
		 * Overriden hashCode() method to create a hashcode from all the accessible values in IUser.
		 * @return a hashCode tied to the values of this IUser.	
		 */
		@Override
		public int hashCode(){
			try {
				// Only name is available for now.
				return stub.getName().hashCode();
			} catch (RemoteException e) {
				// Deal with the exception without throwing a RemoteException.
				System.err.println("ProxyStub.hashCode(): Error calling remote method on IUser stub: "+e);
				e.printStackTrace();
				return super.hashCode();
			}
		}
		
	}

Wrap any incoming IUser objects with a ProxyUser instance and use ProxyUser objects whenever comparisons need to be done.

Strip off the decorator by returning only the stub inside of it when any outside entity requests the IUser object.    This keeps the decoration fully private and encapsualated.

 For more information on comparing remote objects, please see the Resources web page on Comparing Deserialized Objects.

 

 


© 2015 by Stephen Wong