COMP 310 
		  
		Spring 2018
		 | 
		
		HW07: Remote task 
		execution
		 | 
	
	
		
		
		  
		
		  
		
		   
		
		  
		
		  
		
		  | 
	
Deprecated Web Page! This content has either been moved to Canvas or has been removed from the course.  Please inform the staff right away about any links that led to this page.
This project has a "compute engine" (ICompute) on the server  which is served 
out as a remote object.   The client then sends a "compute task" 
(ITask) to 
the server for it to execute, returning the result to the client.   The 
key issue here is that the server does not know a priori what the client will 
have it do.  
Before starting this assignment, be sure that you 
can run the demo from Lab08, 
which includes configuring your computer for use with RMI.
This assignment and all future assignments will use TCP 
ports in the ranges: 2001-2002 and 2099-2102.  Be sure they are all open on 
your computer!
This assignment page does NOT contain all the 
information necessary to successfully and properly complete the project! 
You MUST carefully read ALL THE DOCUMENTATION, including all Javadocs and 
README.txt's to fully understand the system and the project requirements.
 
References:
Obtaining the Provided Code
The code is available by setting the svn:externals 
property (under Team/Set Property) of your
src folder to "provided 
https://svn.rice.edu/r/comp310/course/ComputeEngine/provided" 
Be sure to set the working directory of both the 
client and server launch configurations to your the "bin" 
directory in your project.   
Also, set both the client and engine (server) launch configurations to be 
"shared" so that they will always be set correctly.
See 
the Eclipse Resources page for directions   
Ports that need to be opened (see the code for 
the values):
	- In-bound on Server:
		- IRemoteTaskViewAdapter.BOUND_PORT_SERVER 
		-- Used by IRemoteTaskViewAdapter sent from 
		server to client to display back on server's view.
 
		- ICompute.BOUND_PORT-- The port the
		ComputeEngine is bound in the Registry.
 
		- IRMI_Defs.CLASS_SERVER_PORT_SERVER -- 
		The class server the server uses.
 
		- IRMI_Defs.REGISTRY_PORT -- The port the 
		Registry uses on the server.
 
	
	 
	- In-bound on Client:
		- IRemoteTaskViewAdapter.BOUND_PORT_CLIENT 
		-- Used by IRemoteTaskViewAdapter sent from 
		client to server to display back on client's view.
 
		- IRMI_Defs.CLASS_SERVER_PORT_CLIENT -- 
		The class server the client uses.
 
	
	 
 
Click for full-size image

Features
	- Via the remote ICompute interface, the 
	server will execute any ITask implementation 
	sent to it, returning its results in a type-safe manner.
		- IMPORTANT:  The compute engine (
ICompute) 
		is NOT the same as the engine application's MVC "model".   
		The compute engine is just one of the objects managed by the engine 
		model.  
	
	 
	- A client connects to a server by retrieving the server's 
ICompute
	STUB from the server's Registry.
		- The 
ICompute stub should be the ONLY entity ever bound 
		in the Registry! 
		- The server does not need to ever initiate a connection to the 
		client.  
		
			- Two-way communication between the client and server is 
			established when the client first connects to the server.
 
		
		 
	
	 
	- When a task arrives at the server, it's 
	setTaskViewAdapter() method is called to attach it to the server's 
	view by providing the task with a local view adapter, so that any status output from the task is displayed on the server's 
	view.
 
	- Not only are the tasks sent from the client to the server, but also, the
	ICompute interface provides a 
	setTextAdapter()  method enables the client to send a stub of a 
	remote view 
	adapter to the server so that the server can display information on the 
	client's view plus that same method will return a stub of a remote view adapter 
	that will enable the client to display information on the server's view.
 
	- Object serialization:
		- Serializable interface tells the 
		compiler that this object can be serialized, i.e. its data (not its 
		code!) can be turned into a byte stream and sent over the network.
 
		- Code, if needed, is transmitted separately and automatically.   
			The client's java.rmi.server.codebase 
		value is imbedded in the serialized byte stream.   The 
			receiver's Java class loader will look at this value and find the 
			class file server on the sender and then issue an http-type request 
			to download the associated class file.
 
		- Note that the class file server does not have to be co-located 
			with the serialized object's sender.   The class file 
			server can be a separate machine altogether, e.g. a central class 
			file repository. 
 
		- transient fields are not serialized.
 
		- Optional readObject method in
		Pi2 and GetInfo is used for 
		custom deserialization processes, here to reinitialize the
		transient taskView 
		field.   The server will attach its own ITaskView adapter so the transmitted task can communicate to the 
		server.
 
	
	 
	- IRMIUtils:
		- Has services for creating and/or locating the Registry for the 
		client and server use, plus to obtain the local IP address in a 
		consistent manner across multiple operating systems.
 
		- Has services to start and stop the RMI sub-system with minimal 
		effort and maximum reliability.    
 
		- Abstracts and encapsulates the Registry obtainment process.
 
		- Uses techniques for composing lambdas to enable the methods to send 
		multiple messages to multiple output targets.
 
		- Note:  There are some typos still running 
		around where "IRMIUtils" (or its concrete 
		implementation, RMIUtils) are still referred 
		to by their old name,  "RegistryFactory"
 
	
	 
	- The client can send a message that will appear on the compute engine's 
	view and vice versa. 
 
Read the code documentation!  
(Be sure to read the package documentation too:  In the Javadoc web page, 
click the "Overview" tab and then click the package name in the right-side 
box.)
(Package Javadocs can also be found in the 
package-info.java file in the package.)
 
Demo JARs
The provided code includes fully operational demos for both the client and 
the engine server as executable JAR files in the provided.demo 
package.   See the 
Javadocs for that package for complete directions 
on how to run the demos and their capabilities.
It is an Honor Code violation to attempt to 
decompile the provided JAR files.
The demos are fully compliant with the required inter-computer API as defined 
by the provided.compute package.   Likewise, the provided 
sample tasks are API compliant.   The demo 
client, the demo engine and/or the provided sample tasks can thus all be used to 
test student solutions.    This will enable each piece of 
the project to be tested separately.   This is NOT, 
however, a substitute for the required testing with other students!  
Note: The demo client is only capable of loading ITask 
implementations that have a public static FACTORY field of type
ITaskFactory.    The sample ITasks are 
pre-loaded into the demo client for convenience only (loaded by its controller 
-- not hardwired into the view or model!).
Use the demos as part of your development 
process:
One great advantage of having these fully operational 
demos at your disposal is that you can now completely separate the 
development of your client from your engine server from your custom tasks.  
One of the goals of the project is to demonstrate how all these parts are 
completely decoupled, so that is exactly how you should structure your 
development process!  
 
Important concepts:
Be sure you are absolutely clear on 
exactly which objects in the system are
	- RMI Server objects -- These objects provide remote 
	processing capabilities and are never transmitted anywhere; only their stubs 
	are.   RMI Server objects should NOT be 
Serializable! 
	- RMI Stub objects -- These are stubs made from RMI 
	Server objects.  These stubs are sent to remote computers to enable 
	them to access the associated RMI Server object.
 
	- Serializable objects -- These objects are transmitted 
	in whole to remote computers.   Stubs are NOT made for these 
	objects!   While RMI stubs are 
Serializable, not all
	Serializableobjects are RMI stubs. 
	- Non-serializable objects -- These objects are not 
	designed to be transmitted to any remote computer, i.e. are for local use 
	only.   Be sure that if any 
Serializable 
	object holds a reference to a non-serializable object, that field is marked 
	as transient so 
	that the non-serializable object is not transmitted with it! 
	Misunderstanding which objects are which is the #1 
	stumbling block and the #1 source of errors in this project!
 
Stubs retrieved from remote Registries 
-- The ICompute stub that the client retrieves via 
the Registry delegates its calls back to its
ComputeEngine implementation on the server.
Stubs sent and returned by stubs  (stubs as factories) 
-- IRemoteTaskViewAdapter stubs are both sent to the 
server via a method in the ICompute stub in the 
client as well as returned by the ICompute stub as a 
return value.  Thus a single stub can serve as a factory for all the other 
stubs that are needed, eliminating the need for multitudes of objects bound in 
the Registry--only a single factory needs to be 
bound.
Entire objects being serialized and sent via stubs 
-- the ITask objects are being completely serialized 
and sent over the network from the client to the server.  They are not sent 
as stubs.   
Automatic remote dynamic class loading of 
serialized objects -- If the server does not have the code for the 
concrete ITask sent to it, the RMI system settings 
that the client and server set up automatically cause the code for the task to 
be dynamically loaded from across the network, from the client's class server to 
the server.  Likewise, this is done for the stubs that are sent back and 
forth.
Task factories -- Abstractions of the 
task instantiation process that enable dynamic loading of tasks.  The 
provided ITaskFactory interface is designed to encapsulate the 
conversion of a String (as would be passed from a user interface) 
to whatever type of constructor parameter, i.e. not necessarily a String, 
the associated ITask implementation requires.  Typically, task 
factories do not require constructor parameters for their own instantiation, 
which helps further decouple the specific tasks from the rest of the system.    
Student solutions are not required to use the provided ITaskFactory 
but justifications on the architecture used to enable dynamic task loading 
should be included in the documentation and/or README.txt file.
Task factory loaders -- An encapsulation 
of the process of dynamically loading a task factory.  While individual 
task factories are are generically typed to the return type of  the tasks 
they produce, i.e. ITaskFactory<T>,  task factory loaders'
load() methods are typed to an unbounded wildcard return type, i.e.
ITaskFactory<?>.   In terms of dynamic task loading, this 
enables the system architecture to be decoupled from the task return type.    
The provided code includes an implementation of ITaskFactoryLoader 
that is able to load ITask implemenations that include a field,
public static ITaskFactory FACTORY, that is the singleton task 
factory instance for that specific type of task.   Student solutions 
are not required to use the provided ITaskFactoryLoader but 
justifications on the architecture used to enable dynamic task loading should be 
included in the documentation and/or README.txt file.
Custom task result formatters -- The 
client application should be designed to be independent of the return type of 
the tasks that it loads and runs but the tasks need to keep the strong typing of 
their return types.  The ITaskResultFormatter interface 
provides a means for the client application to transparently convert the 
strongly-typed task results to useful and informative task-specific strings that 
can be displayed to the user.  Since the formatting of a result is specific 
to each instance of a task, the ITask interface provides a 
factory method for the task to generate its own custom formatter.   
The client application only needs to deal with the formatters as a unbounded 
wildcard type:  ITaskResultFormatter<?>. or as a generic type,
ITaskResultFormatter<T>, in a generic method (recommended, e.g. see 
IClientModel.runtTask()).    Theoretically, the compute 
engine could also use the task's result formatter, but on the engine side, one 
is generally more interested in the raw task results rather than a nicely 
formatted output.
Local vs. remote task view adapters -- 
There are two kinds of task view adapters in use by the system for two very 
different purposes:
	ILocalTaskViewAdapter -- A 
	non-serializable adapter used by an ITask to display text 
	messages on the local system, e.g. the 
	engine server when it is processing a task.  It is crucial that this 
	adapter NOT be transmitted with a task when it is sent from one computer to 
	another.    When an application, e.g. the engine server, 
	receives a task, e.g. from the client, the first thing it should do is to 
	set the task's local view adapter to that local system's 
	ILocalTaskViewAdapter instance.   Technically, 
	this adapter would connect the task to the local system's model, 
	not its view, as the local model would then do whatever it takes to pass the 
	message on to its view. 
	IRemoteTaskViewAdapter -- An RMI Server 
	object whose stubs are used by a remote system to 
	display messages on the local system's view.  Again, technically, this 
	adapter would connect the task to the local system's model, not its 
	view, as the local model would then do whatever it takes to pass the message 
	on to its view.   The compute engine interface (ICompute) 
	has the ability for the local client system to exchange 
	IRemoteTaskViewAdapter stubs with the remote engine server, thus 
	enable both sides to display text messages on the other. 
 
Requirements:
You will need to create your OWN client and engine MVC 
implementations (two separate applications) plus your own custom ITask
implementations.   Please see the 
IClientModel and IEngineModel interfaces for guidelines on 
building one's client and server implementations.   
	- Do NOT copy or modify the  
provided.compute  
	or provided.rmiUtils packages!
	
	 
	- The 
IClientModel 
	and IEngineModel interfaces 
	are provided as a guide to help you get started by creating methods 
	that do the necessary operations.  
	
		- It is recommended that you start by creating client and engine 
		models that implement these interfaces.
 
		- Use of these interfaces is not required, so once you get a simple 
		solution operational, then remove the references to these interfaces 
		which will free you to customize your system to your needs.
 
	
	 
	- Dynamic loading of 
ITask 
	implementations is provided by ITaskFactory
	and ITaskFactoryLoader
	(concrete implementation: 
	SingletonTaskFactoryLoader).   It is NOT 
	required that this exact mechanism be used for dynamic ITask 
	loading -- students are free to design their own dynamic loading systems 
	
		- Please make a note in the README.txt file if the student client 
		solution is not capable of loading the sample tasks (recomendation: 
		create compatible versions), though the student engine solution must 
		be able to run the unmodifed sample tasks.
 
	
	 
You may NOT change any of the provided code 
-- you will receive a commit error if you do so!
Student work:
Students are required to design and implement both client 
and engine server applications plus custom tasks.
	- The client application must 
	have at least the following behaviors and capabilities:
		- Is able to connect to any engine server 
		implemtation that adheres to the task and compute engine specifications 
		detailed by the 
provided.compute package and the RMI 
		configuration as created by the provided.rmiUtils package.
			- Is able to send a status message to the the engine server 
			immediately and automatically upon connection.
 
			- Is able to display a status message from the server received 
			immediately after connection.
 
			- Is able to send a text message to the engine server at any time.
 
			- Is able to display a text message from the engine server at any 
			time. 
 
		
		 
		- Dynamically load any "compatible" 
ITask implementation 
		from anywhere in the Java classpath.
		
			- Where "compatible" means that the task class is compatible with 
			the dynamic class loading architecture being used and not anything 
			about the processing operation the task performs when it is run.
 
			- Is able to construct different instances of the same 
ITask
			implementation using different constructor parameters, where 
			the value of those parameters clearly affects the processing 
			behavior of the task when it is run. 
		
		 
		- Is NOT hardwired to any specific task return type.  
		
			- A task instance-specific 
ITaskResultFormatter
			must be used to convert a task's strongly typed return value 
			to a String for display. 
		
		 
		- Is able to compose and run any two or more tasks who share a valid 
		constructor value(s) that can be represented by the same string, i.e. 
		the same string value can be converted into the different constructor 
		types needed by the composed tasks.  For instance, "3.14" can be 
		converted into a double, a string and an array of characters.
		
			- Any composed task must be treated by the system as an 
			encapsulated whole, no differently than any other task, including 
			being sent to the compute engine as a single task object.
 
		
		 
		- Is able to shut down gracefully such that the RMI system is 
		explicitly shutdown as the application quits.
 
	
	 
	- The engine application must 
	have at least the following behaviors and capabilities:
		- Enables connection by any client 
		implemtation that adheres to the task and compute engine specifications 
		detailed by the 
provided.compute package and the RMI 
		configuration as created by the provided.rmiUtils package.
			- Is able to send a status message to the client  
			immediately and automatically upon connection by the client.
 
			- Is able to display a status message from the client immediately 
			after connection.
 
			- Is able to send a text message to the client at any time.
 
			- Is able to display a text message from the client at any time.
 
		
		 
		- Is able to receive and run any 
ITask sent to it using 
		its ICompute stub. 
		
			- Is able to display the raw task result value after the task is 
			run.
 
			- Is able to return the raw, strongly typed task result back to 
			the client.
 
			- During a task execution, is able to display any text message 
			sent from the task through its 
ILocalTaskViewAdapter. 
		
		 
		- Is able to shut down gracefully such that any entries bound into the 
		local Registry are unbound and the RMI system is explicitly shutdown as 
		the application quits.
 
		- An engine server should be able to support multiple simultaneous 
		connected clients, including running any task sent by any connected 
		client at any time.
 
	
	 
	- The 
ITask
	implementations 
	must have at least the following behaviors and capabilities:
		- At least 3 new ITasks must be 
		implemented
			- Across the new tasks, there should be at least 3 different 
			return value types represented.  
 
			- All tasks' processing behavior when run should be dynamically 
			customizable by a parameter supplied at the task's construction.
				- At least 2 tasks must use a constructor parameter 
				that is NOT a 
String. 
			
			 
		
		 
		- All tasks must be able to run on any 
		server implementation that adheres to the task and compute engine 
		specifications detailed by the 
provided.compute package and 
		the RMI configuration as created by the provided.rmiUtils 
		package. 
		- All tasks must generate a task-specific status message while it runs 
		that is displayed on the server engine user interface.
 
		- Neither the client nor the engine server can be hardwired to a 
		specific task return type.
 
	
	 
	- Both the client and engine server applications must run on all 
	team member's machines and of course, the staff machines.  
	This includes being able to:
		- Connecting the client to the server when both are running on the same machine
 
		- Connecting the client to the server when they are running on different 
		machine. All possible connection ways on team member machines must be 
		tested!
 
	
	 
	- Inter-team testing:  Both the client and engine 
	server applications must be tested with at least 10 
	people (on 10 different computers) on at least 10 different teams, i.e. 
	against at least 10 different client & engine implementations.
 
	- Client and engine launch configurations:  You must create and save in your repository, run (launch) configurations 
	for both the client and the computer engine.
 
Note that the provided.compute 
package contains all the interfaces that BOTH the client and the server need to 
talk to each other.   Do NOT copy or modify this package!
Testing
Testing RMI programs can be tricky.   You can 
easily be fooled into thinking that your code runs properly if you test 
insufficiently.   In particular, if both ends of your test contain all 
the code, the remote dynamic class loading will not be tested.  Here's a 
more complete test: 
	- Always test your client and engine applications against the provided  
	demo client and engine, including all possible tasks.   This will 
	help highlight and track down compatibility issues.
 
	- Create a separate project with just the 
	provided code in it.   Run the demo engine server 
	from this project and test your client against it.   
 
	- Create a separate project that is another checkout of your own code.   
	Delete your custom tasks from this code and then run the compute engine 
	only.      
	
		- DO NOT EVER COMMIT THIS CODE!  -- you may actually want to 
		branch your code, though this can be a hassle when you are updating your 
		code. 
 
		- Test 
	the client from your main project against this engine.   This will 
	test the remote dynamic class loading of your tasks as well as your 
	implementations of the missing 
provided code. 
	
	 
	- Be sure to do the above test across different machines!   This 
	will test all aspects of your code.    Testing 
	against someone else's code is NOT the same as testing against your own 
	code!    There are errors that will ONLY show up when 
	testing against a someone else's implementation! 
 
Testing Requirements:
	- All team members must test both the team's client and server against 
	all other team members.
 
	- Each team member must test both the 
	team's client and server against at least 5 other team's 
	client and server applications.
 
	- The total number of other teams tested against by the whole team 
	must be at least 5 times the number of team members, e.g. 10 other 
	teams for a team of 2 or 15 other teams for a team of 3. 
 
	- The project's README.txt file must include a record of all 
	successful testings by listing each team member's NetID against the 
	NetID and team number of those with whom that team member has successfully 
	tested both their client and server.  
	
		- An included Excel spreadsheet or a reference to an online 
		spreadsheet is an acceptible record so long as it made clear in the 
		README.txt and the staff is access to the document.
 
		- Testing against one's own teammates should also be included in the 
		record.
 
		- The record should also include any unsuccessful tests with notes 
		about what went wrong.   Unsuccessful test do NOT count 
		towards the meeting the testing requirements.
 
		- If the required number of successful tests was not completed, the 
		records must indicate that an exhaustive testing search across the 
		entire class was performed in an effort to meet the testing 
		requirements.
 
	
	 
Most Common Errors
	- Firewall ports not open -- typically shows up a "connection 
	refused/timed out" error/li>
	
 - Run (launch) configurations not set properly -- working directory not 
	set, which typically shows up a security manager error on startup.  
 
	- Editing the provided code  -- shows up as a commit error
 
	- Transmitting an RMI server object instead of its stub.  -- 
	typically shows up a "(de)serialization" or "(un)marshalling" error.
 
	- Attempting to transmit a non-serializable entity -- always check if that 
	entity should have been transmitted in the first place!   
	Don't rely on Eclipse's "Quick Fix"!
 
	- Special note for teams where the members are running a mix of 
	32-bit and 64-bit Java computers (this is very rare these days):  You will need to use a 
	special "svn:ignore" property to enable the 
	different computers to separately and properly compile your code.   
	Please see the following directions on
	how to set the 
	svn:ignore parameter to ignore 
	the .classpath file.
 
 
Grading Guidelines
	- The at least 3 tasks implemented that must return at least 3 different  
	types of results.   - 15 pts
 
	- All tasks are dynamically loaded into the client.  No tasks are 
	hardwired into the client or engine applications. - 5 pts
 
	- Able to dynamically create,  send and execute a composite task.   - 3 
	pts
 
	- The tasks' results should be displayed on the client and engine's 
	views, with task-specific formatting, using the task's 
	ITaskResultFormatter object, appearing on the client.   - 5 pts 
	- The task execution must display some sort of status message on the server when it 
	is run.   That is, the task's 
execute method should 
	utilize the ILocalTaskViewAdapter given to it by the 
	compute engine to display a status message. - 5 pts 
	- An 
IRemoteTaskViewAdapter stub properly created on the 
	client and automatically transmitted to the server upon connection. - 5 pts 
	- An 
IRemoteTaskViewAdapter stub properly created on the 
	engine server and automatically transmitted to the client upon connection. - 
	5 pts  
	- Connection status messages sent both ways between the client and server 
	automatically upon connection. - 5 pts
 
	- Able to send string message from client's view to connected compute 
	engine's view. - 5 pts
 
	- Able to send string message from compute engine's view to connected 
	client's view.- 5 pts
 
	- The task should run properly when both the client and server are on the 
	same machine.   This implies the ability to connect. - 5 pts
 
	- The task should run properly when the client and server are on different 
	machines.  This implies the ability to connect. Test this between your and your partner's machines! Your code 
	will be tested against a course staff machine. - 5 pts
 
	- The above described testing requirements were met.  - 15 pts
 
	- Run configurations created and saved in repository for both client and 
	compute engine. - 5 pts
 
	- Full Javadocs and UML diagrams covering the both client and engine 
	server applications.  - 5 pts
 
	- Discretionary - 7 pts
 
Note that the above requirements mean that you  cannot change 
 
anything in the   
provided.compute or 
provided.rmiUtils packages!
© 2018 by Stephen Wong