 |
| |
| The
Flash debugger, available within Macromedia Flash MX, is an
incredibly useful tool. Figure 6 is a screen shot of the Flash
debugger showing some of the Course data returned by the server.
Does this data look familiar? |
| |
 |
| Figure
6: The Flash debugger with returned Course data |
| |
| The
data that was created on the server and saved in Java objects
is delivered to the client in ActionScript objects, the client's
native format. The benefits of this capability are truly profound.
Think of the amount of work this saves! |
| |
| Here
are some examples of how course data can be accessed in ActionScript: |
| |
var
courseName1 = course.name; //
"Anthropology 101" var courseName2
= course["name"]; //
"Anthropology 101" var user11email
= course.users[11].email; // "hjones@abc.com" |
| |
| So
easy! |
| |
| Automatic
Java-ActionScript translation (the actual term is serialization)
works for many other data types, and it works going from client
to server as well. |
| |
| The
following table contains supported ActionScript data types
and their Java equivalents: |
| |
| ActionScript data
type |
Java data type |
| Number (primitive data
type) |
Double |
| Boolean (primitive data
type) |
Boolean |
| String |
String |
| ActionScript (AS) |
object Map |
| null |
null |
| undefined |
null |
| Ordered array |
ArrayList |
| Named array |
Map |
| Date object |
Date |
| XML object |
org.w3c.dom Document |
|
|
|
| The
following table contains the supported Java data types and
their ActionScript equivalents: |
| |
| Java data type |
ActionScript data
type |
| Object[] |
Array |
| Array of primitive types |
Array |
| Boolean |
boolean |
| Number |
number |
| Character |
String |
| Date |
Date |
java.sql.ResultSet,
flashgateway.sql.PageableResultSet |
Recordset |
| Throwable |
ActionScript Object |
| org.w3c.dom.Document |
XML |
| flashgateway.io.ASObject
|
ActionScript Object |
| flashgateway.io.ASXMLString |
XML |
| Map |
ECMA Array |
| Collection |
Array |
| Dictionary |
ActionScript Object |
| Serializable |
ActionScript Object |
|
|
|
Notification
about newly-arrived data
After dataAccess receives
the Course Object, it notifies the UI that course data is
available. The notification framework was intentionally omitted
in the code examples above, in order to show how dataAccess
is initialized. Now we will backtrack to show how notification
is initialized. |
| |
| This
code is executed when BBGMDataAccess.as is
included in the main (UI) program: |
| |
//
make an event source w/ listener support
AsBroadcaster.initialize(BBGMDataAccess.prototype); |
| |
| This
adds event notification capability to our data access class.
When the main (UI) program creates the data access object,
it registers itself as a listener: |
| |
if (dataAccess == undefined){
dataAccess
= new BBGMDataAccess(gatewayURL, service);
dataAccess.addListener(this);
|
| |
DataAccess
adds the UI program object (passed as "this" in
the example) to its list of listeners. When the data access
layer receives a reply from the server, it uses the ASBroadcaster
method broadcastMessage
to notify its listeners that data has been received. This
code from the data access class shows how replies to the getCourse
request are handled. |
| |
BBGMDataAccess.prototype.getCourse_result
= function(course){
this.broadcastMessage("onBBGMSGetCourse",
course);
} |
| |
BroadcastMessage
specifies two parameters: the name of the listener method
and the data that has changed. Here is the listener method
in the main (UI) program: |
| |
onBBGMSGetCourse
= function(obj){
this.courseObj =
obj;
updateGroupDisplay();
} |
| |
Using
ASBroadcaster (rather than
using a direct call, to updateGroupDisplay
in this example) permits a clean separation between the data
access layer and the component(s) that use the data provided
by the data access layer. One advantage of this approach is
that the data access layer can be developed without knowing
how the data it delivers is used. The connection between the
UI and the data access layer is established at run time, rather
than at compile time. This makes the solution more flexible.
Additionally, the UI can be changed without impacting the
data access layer. This allows a completely different UI to
be delivered. For example, if PDA or device delivery is also
desired, the data access layer would not be changed at all.
This approach also permits parallel development. For more
information on this topic, search the web for MVC or model-view-controller
architecture. |
| |
Using
the Façade
Taking advantage of Java's treatment of interfaces proved
to be very advantageous on this project. This approach facilitates
parallel development of client and server components. Additionally,
this eliminates potential problems that could occur when the
components are integrated. |
| |
| In
Java, an interface specifies the contract between two programs:
a caller and a callee. In the Blackboard project, we used
an interface to specify the contract between the client and
server programs. |
| |
| This
is the Java code that specifies the BBGMFacadeInterface.
|
| |
public
interface BBGMFacadeInterface {
public Course getCourse(String pkcourseId) throws
Exception ;
public CreateGroupResponse createGroup(int requestId,
String
pkcourseId,
HashMap
parameters)
throws Exception;
public Response updateGroup(int requestId,
String
pkcourseId,
String
groupId,
HashMap
parameters) throws Exception;
public Response removeGroup(int requestId,
String
pkcourseId,
String
groupId) throws Exception;
public Response createAssignments(int requestId,
String
pkcourseId,
ArrayList
assignments)
throws Exception;
public Response removeAssignment(int requestId,
String
pkcourseId,
HashMap
assignment) throws Exception;
} |
| |
| The
interface specifies all methods that are available to the
client. For each method, it lists all inputs and outputs.
Note that it does not include implementation logic (in other
words, nothing is within curly braces). The Java compiler
verifies that any class claiming to implement an interface
provides all interface methods with exactly the correct input
and outputs. |
| |
|
Remember this line from BBGMFacadeDevelopmentImpl
earlier? |
| |
public
class BBGMFacadeDevelopmentImpl implements BBGMFacadeInterface,
Serializable |
| |
| This
part is new: |
| |
public
class BBGMFacadeProductionImpl implements
BBGMFacadeInterface,
Serializable |
| |
| |
 |
Figure 7: A diagram of the programs that were built around
the BBGMFaçadeInterface. |
| |
| We
created two implementations for BBGMFaçadeInterface:
FaçadeDevelopmentImpl for development,
testing and demonstration; and BBGMFaçadeProductionImpl
to interface with the Building Blocks API. We also wrote two
clients. The development work proceeded as follows: |
| |
| Phase one – infrastructure: |
TestClient —> FacadeDevelopmentImpl |
| Phase two – component development: |
TestClient —> FacadeProductionImpl |
| |
Flash client —> FacadeDevelopmentImpl |
| Phase three – integration: |
Flash client —> FacadeDevelopmentImpl |
|
| |
| In
Phase 1, all the details of the Façade were ironed
out, and test programs for client and server were developed.
In Phase 2, the more challenging client and server component
development was done in parallel, using the solid interface
specification and test harnesses created in Phase 1. The client
and server components were "hooked up" in Phase
3. This proved to be a very effective development strategy—that
would not have been possible without Java interface technology. |
| |
Request/Reply
Tracking
The BBGM Façade implements 6 methods. This article
has examined only one of these: getCourse.
GetCourse is called only
once, when the BBGM client initializes. The other methods
can be called any number of times and in any order. We created
a Response class to keep track of the replies, with the following
code: |
| |
public
class Response implements Serializable{
public int requestId;
public Response(int requestId)
{ //constructor
this.requestId = requestId;
}
} |
| |
The
BBGMFacadeInteface specifies that for all methods (except
getCourse) the first input
parameter is a request ID, and a Response object, which contains
the request ID, is returned. If you look at the code, you
will see that each request is sent with a unique request ID,
and that ID is returned in the Response object. Thus, a unique
identifier makes the entire round trip. This was important
for keeping track of outstanding updateGroup
requests on the "All Groups In Course" window. It
was also very useful for debugging and QA work. |
| |
| In
the final section, we'll take a look at error handling. |
| |
| |
| |
|
|
 |
|
| |