What it Does
The second sample application is based around a simple GUI application which stores a list of contacts. The application uses the MVP design pattern, and MethodInvocationRemoting is used to send method calls between the view and presenter layers. Both the presenter and model layers are implemented in C#, but the view is written in Java. As methods calls between the view and presenter layers can be initiated from either layer, the sample application demonstrates how MethodInvocationRemoting can be used bidirectionally.
Code Analysis
On the C# side of the application, the interface IMainView defines methods that can be called on the main view of the application. The class MainViewRemoteAdapterActiveMQ implements this interface, but rather than creating and populating a form, it sends and receives IMainView method calls to and from Java code remotely using MethodInvocationRemoting.
In Java, the interface IContactListPresenter defines all methods that can be called on the presenter layer. The class ContactListPresenterRemoteAdapterActiveMQ implements the IContactListPresenter interface, and uses MethodInvocationRemoting to receive and send method calls. The send and receive operations are performed using MethodInvocationRemoteSender and MethodInvocationRemoteReceiver objects. Initial setup of these objects is performed inside the constructor of the ContactListPresenterRemoteAdapterActiveMQ class...
outgoingMethodSerializer = new MethodInvocationSerializer(new SerializerOperationMap());
outgoingSender = new ActiveMqRemoteSender(connectUriName, outgoingQueueName, requestFilter);
outgoingReceiver = new ActiveMqRemoteReceiver(connectUriName, outgoingQueueName, responseFilter, 200);
methodInvocationSender = new MethodInvocationRemoteSender(outgoingMethodSerializer, outgoingSender, outgoingReceiver);
incomingMethodSerializer = new MethodInvocationSerializer(new SerializerOperationMap());
incomingSender = new ActiveMqRemoteSender(connectUriName, incomingQueueName, responseFilter);
incomingReceiver = new ActiveMqRemoteReceiver(connectUriName, incomingQueueName, requestFilter, 200);
methodInvocationReceiver = new MethodInvocationRemoteReceiver(incomingMethodSerializer, incomingSender, incomingReceiver);
Inner class MessageReceivedHandler handles receiving of any method invocations. In all cases these method invocations are simply routed to the MainView class...
public void MethodInvocationReceived(IMethodInvocationRemoteReceiver source, IMethodInvocation receivedMethodInvocation) {
String methodName = receivedMethodInvocation.getName();
Object[] parameters = receivedMethodInvocation.getParameters();
try {
if (methodName.equals("Initialise")) {
mainView.Initialise();
source.SendVoidReturn();
}
else if (methodName.equals("Show")) {
mainView.Show();
source.SendVoidReturn();
}
else if (methodName.equals("Close")) {
mainView.Close();
source.SendVoidReturn();
}
else if (methodName.equals("AddUpdateContactInGrid")) {
mainView.AddUpdateContactInGrid((String)parameters[0], (String)parameters[1], (String)parameters[2], (String)parameters[3]);
source.SendVoidReturn();
}
else if (methodName.equals("DeleteContactFromGrid")) {
mainView.DeleteContactFromGrid((String)parameters[0]);
source.SendVoidReturn();
}
else if (methodName.equals("PopulateCategories")) {
mainView.PopulateCategories((String[])parameters[0]);
source.SendVoidReturn();
}
else if (methodName.equals("DisplayErrorMessage")) {
mainView.DisplayErrorMessage((String)parameters[0]);
source.SendVoidReturn();
}
else {
throw new Exception("Received unhandled method invocation '" + methodName + "'.");
}
}
catch (Exception e) {
MethodInvocationReceiveException(methodInvocationReceiver, e);
}
}
A reference to the MessageReceivedHandler object is passed to the MethodInvocationRemoteReceiver object through method setReceivedEventHandler()...
methodInvocationReceiver.setReceivedEventHandler(new MessageReceivedHandler());
The Connect() method on the ContactListPresenterRemoteAdapterActiveMQ class connects all the sender and receiver objects to ActiveMQ, and calls the Receive() method on the MethodInvocationRemoteReceiver to start waiting for incoming method invocations...
public void Connect() throws Exception {
outgoingSender.Connect();
outgoingReceiver.Connect();
incomingSender.Connect();
incomingReceiver.Connect();
methodInvocationReceiver.Receive();
}
Similarly the Disconnect() method performs the necessary disconnect and cleanup steps...
public void Disconnect() throws Exception {
methodInvocationReceiver.CancelReceive();
incomingReceiver.CancelReceive();
incomingReceiver.Disconnect();
incomingSender.Disconnect();
outgoingReceiver.CancelReceive();
outgoingReceiver.Disconnect();
outgoingSender.Disconnect();
}
Finally IContactListPresenter interface methods like AddUpdateContact(), use the MethodInvocationRemoteSender object to send the method invocations to C#...
public void AddUpdateContact(String name, String category, String phoneNumber, String emailAddress) {
MethodInvocation addUpdateContactInvocation = new MethodInvocation("AddUpdateContact", new Object[] { name, category, phoneNumber, emailAddress });
try {
methodInvocationSender.InvokeVoidMethod(addUpdateContactInvocation);
} catch (Exception e) {
SetOccurredException(e);
}
}
The MainViewRemoteAdapterActiveMQ class in C# is setup in the same way as Java's ContactListPresenterRemoteAdapterActiveMQ, however receiving of method invocations is handled via a private method and delegate rather than an inner class. Method ReceiveMethodInvocation() calls any received method on the presenter layer...
private void ReceiveMethodInvocation(object sender, MethodInvocationReceivedEventArgs e)
{
if (e.MethodInvocation.Name == "AddUpdateContact")
{
object[] parameters = e.MethodInvocation.Parameters;
presenter.AddUpdateContact((string)parameters[0], (string)parameters[1], (string)parameters[2], (string)parameters[3]);
((IMethodInvocationRemoteReceiver)sender).SendVoidReturn();
}
else if (e.MethodInvocation.Name == "DeleteContact")
{
presenter.DeleteContact((string)e.MethodInvocation.Parameters[0]);
((IMethodInvocationRemoteReceiver)sender).SendVoidReturn();
}
else if (e.MethodInvocation.Name == "Exit")
{
presenter.Exit();
((IMethodInvocationRemoteReceiver)sender).SendVoidReturn();
}
else
{
throw new NotImplementedException("Received unhandled method invocation '" + e.MethodInvocation.Name + "'.");
}
}
...and the method is set on the MethodInvocationRemoteReceiver class via a delegate, and subscribing to event 'MethodInvocationReceived'...
methodInvocationReceiver.MethodInvocationReceived += new MethodInvocationReceivedEventHandler(ReceiveMethodInvocation);
Similar to the Java code, the class contains IMainView interface methods which simply send the method calls to Java via the MethodInvocationRemoteSender class...
public void AddUpdateContactInGrid(string name, string category, string phoneNumber, string emailAddress)
{
Object[] parameters = new Object[4];
parameters[0] = name;
parameters[1] = category;
parameters[2] = phoneNumber;
parameters[3] = emailAddress;
methodInvocationSender.InvokeVoidMethod(new MethodInvocation("AddUpdateContactInGrid", parameters));
}
The following diagram shows how data flows between the presenter in Java, ActiveMQ, and the view layer in C#...
Setup and Running
First start the ActiveMQ broker, and ensure the 'connectUriName' parameter is correctly configured for the broker in both the MainViewRemoteAdapterActiveMQ and ContactListPresenterRemoteAdapterActiveMQ objects. Next start the compiled java code, using a command similar to the following...
java -cp "[path containing MethodInvocationRemoting]\MethodInvocationRemoting\Java\SampleApplication2\bin";"[path containing MethodInvocationRemoting]\MethodInvocationRemoting\Java\MethodInvocationRemoting\bin";"[path containing MethodInvocationRemoting]\MethodInvocationRemoting\Java\OperatingSystemAbstraction\bin";"[path containing MethodInvocationRemoting]\MethodInvocationRemoting\Java\ApplicationLogging\bin";"[path containing MethodInvocationRemoting]\MethodInvocationRemoting\Java\ApplicationMetrics\bin";"[path containing MethodInvocationRemoting]\MethodInvocationRemoting\Java\Referenced Libraries\activemq-all-5.7.0.jar" Program
The following should be displayed on the console...
Press [ENTER] to stop the application.
The Java side of the application can be stopped at any time by pressing 'Enter' at the console. The compiled C# code can be started by executing the SampleApplication2.exe file. This will display the following text in the console...
Application running...
...and display the main view of the application...
From this window simple details for contact persons can be stored, updated, and deleted. Whilst the window itself is created by Java code, the actual storage of each contact in the data layer, and the coordination between the data layer and the window (the presenter layer) is occurring in C#. Click the 'Exit' button to end the application. The C# code will terminate automatically. Press 'Enter' at the Java side console to stop the Java code.
Note
I experienced problems running the Java side code directly from the Eclipse IDE in Windows. It seems that the statement...
inputReader.readLine();
...in the Program.CancelRequestReceiver inner class somehow prevents the main window from drawing properly. This could be as a result of the application and Eclipse itself contending for the swing event dispatch thread. However, compiling the application and running it from the command line should work fine.
Running with the FileRemoteSender and FileRemoteReceiver Classes
As of MethodInvocationRemoting version 1.1.0.0, the sample application can be run using the FileRemoteSender and FileRemoteReceiver classes to transport the method invocations
via the file system. In the C# code, the Program class must be changed to instantiate the mainView as a MainViewRemoteAdapterFile object, by uncommenting the relevant code...
MainViewRemoteAdapterFile mainView = new MainViewRemoteAdapterFile(@"C:\Temp\FromC#Request.txt", @"C:\Temp\FromC#Request.lck", @"C:\Temp\FromC#Response.txt", @"C:\Temp\FromC#Response.lck", @"C:\Temp\FromJavaResponse.txt", @"C:\Temp\FromJavaResponse.lck", @"C:\Temp\FromJavaRequest.txt", @"C:\Temp\FromJavaRequest.lck");
In the Java Program class, the code which declares and instantiates the presenter must be uncommented in a similar way...
ContactListPresenterRemoteAdapterFile presenter = null;
MainView mainView = null;
try {
presenter = new ContactListPresenterRemoteAdapterFile("C:\\Temp\\FromJavaRequest.txt", "C:\\Temp\\FromJavaRequest.lck", "C:\\Temp\\FromJavaResponse.txt", "C:\\Temp\\FromJavaResponse.lck", "C:\\Temp\\FromC#Response.txt", "C:\\Temp\\FromC#Response.lck", "C:\\Temp\\FromC#Request.txt", "C:\\Temp\\FromC#Request.lck");
Ensure that the C:\Temp\ directory exists and allows read and write access on the test machine (or adjust the MainViewRemoteAdapterFile and ContactListPresenterRemoteAdapterFile
constructor parameters with suitable file paths). The application can then be started in the same way as described in the 'Setup and Running' section above.
Running with the TcpRemoteSender and TcpRemoteReceiver Classes
As of MethodInvocationRemoting version 1.1.0.0, the sample application can be run using the TcpRemoteSender and TcpRemoteReceiver classes to transport the method invocations via
a TCP network. In the C# code, the Program class must be changed to instantiate the mainView as a MainViewRemoteAdapterFile object, by uncommenting the relevant code...
MainViewRemoteAdapterTcp mainView = new MainViewRemoteAdapterTcp(System.Net.IPAddress.Loopback, 55000, 55001, 55002, 55003, 15, 2000, 30000, 25, 50);
In the Java Program class, the code which declares and instantiates the presenter must be uncommented in a similar way...
ContactListPresenterRemoteAdapterTcp presenter = null;
MainView mainView = null;
try {
presenter = new ContactListPresenterRemoteAdapterTcp(InetAddress.getLoopbackAddress(), 55003, 55002, 55001, 55000, 15, 2000, 30000, 50, 25);
Ensure that the specified TCP ports (55000-55003) are not in used by another application (or adjust the MainViewRemoteAdapterTcp and ContactListPresenterRemoteAdapterTcp
constructor parameters with suitable ports). The application can then be started in the same way as described in the 'Setup and Running' section above.
The following diagram shows how data flows between the presenter in Java, the TCP/IP network, and the view layer in C#...