Sample Application 1

What it Does

The first sample application demonstrates a simple unidirectional use case of MethodInvocationRemoting where Java code uses MethodInvocationRemoting to call a method in C#. The method calculates an approximate value of Pi. The result is returned to the Java side, and written to the console.

Code Analysis

On the Java side, first a MethodInvocationRemoteSender object is setup to send the method invocation to C#. The MethodInvocationRemoteSender object is constructed with an ActiveMqRemoteSender and ActiveMqRemoteReceiver object which take care of transporting the serialized method invocation, and the return value.

ActiveMqRemoteSender activeMqSender = new ActiveMqRemoteSender(connectUri, queueName, requestQueueFilter); ActiveMqRemoteReceiver activeMqReceiver = new ActiveMqRemoteReceiver(connectUri, queueName, responseQueueFilter, receiverConnectLoopTimeout); MethodInvocationSerializer methodSerializer = new MethodInvocationSerializer(new SerializerOperationMap()); MethodInvocationRemoteSender methodInvocationSender = new MethodInvocationRemoteSender(methodSerializer, activeMqSender, activeMqReceiver);

The ActiveMqRemoteSender and ActiveMqRemoteReceiver objects are then connected to the ActiveMQ broker and queues...

activeMqSender.Connect(); activeMqReceiver.Connect();

Finally a MethodInvocation object is created to represent a call to a method named 'ApproximatePi', with a single integer parameter, and returning a BigDecimal value. The method is called using the InvokeMethod() method on the MethodInvocationRemoteSender object...

Object[] parameters = new Object[1]; parameters[0] = (Integer)10000; MethodInvocation approximatePiCall = new MethodInvocation("ApproximatePi", parameters, BigDecimal.class); BigDecimal piApproximation = (BigDecimal)methodInvocationSender.InvokeMethod(approximatePiCall);

On the C# side, a MethodInvocationRemoteReceiver object is setup to receive method invocations from Java. As with the Java code, ActiveMqRemoteSender and ActiveMqRemoteReceiver objects are used to take care of transporting the method invocation. The ActiveMqRemoteSender in Java uses a queue filter 'Request'. The ActiveMqRemoteReceiver in C# receives messages via the same queue filter (this filter is used for transporting the method invocations). The queue filter 'Response' is used to transport the return values. This allows both method invocations and return values to be sent using the same message queue (named 'FromJava').

ActiveMqRemoteSender activeMqSender = new ActiveMqRemoteSender(connectUri, queueName, responseQueueFilter); ActiveMqRemoteReceiver activeMqReceiver = new ActiveMqRemoteReceiver(connectUri, queueName, requestQueueFilter, receiverConnectLoopTimeout); MethodInvocationSerializer methodSerializer = new MethodInvocationSerializer(new SerializerOperationMap()); MethodInvocationRemoteReceiver methodInvocationReceiver = new MethodInvocationRemoteReceiver(methodSerializer, activeMqSender, activeMqReceiver);

The following code subscribes to the 'MethodInvocationReceived' event on the MethodInvocationRemoteReceiver object. Each time a method invocation is received, the ReceiveMethodInvocation() method will be called.

methodInvocationReceiver.MethodInvocationReceived += new MethodInvocationReceivedEventHandler(ReceiveMethodInvocation);

Within ReceiveMethodInvocation(), the method name and parameters are extracted from the 'MethodInvocation' member of the MethodInvocationReceivedEventArgs object. The local method ApproximatePi() is called using the received parameters, and finally the return value is sent using the SendReturnValue() method.

private static void ReceiveMethodInvocation(object sender, MethodInvocationReceivedEventArgs e) { if (e.MethodInvocation.Name == "ApproximatePi") { object[] parameters = e.MethodInvocation.Parameters; Console.WriteLine("Received invocation of method 'ApproximatePi(" + (int)parameters[0] + ")'"); // Call the ApproximatePi method decimal result = ApproximatePi((int)parameters[0]); Console.WriteLine("Sending return value " + result.ToString()); // Send the result ((IMethodInvocationRemoteReceiver)sender).SendReturnValue(result); } else { throw new NotImplementedException("Received unhandled method invocation '" + e.MethodInvocation.Name + "'."); } }

As with the Java code, the ActiveMqRemoteSender and ActiveMqRemoteReceiver objects are connected to the ActiveMQ queues, before the Receive() method is called on the MethodInvocationRemoteReceiver to start waiting for incoming method invocations.

activeMqSender.Connect(); activeMqReceiver.Connect(); // Start receiving method invocations methodInvocationReceiver.Receive();

To disconnect and cleanup objects on the Java side, CancelReceive() should be called on the ActiveMqRemoteReceiver object, before calling Disconnect() on both the ActiveMqRemoteSender and ActiveMqRemoteReceiver...

// Stop receiving and disconnect activeMqReceiver.CancelReceive(); activeMqReceiver.Disconnect(); activeMqSender.Disconnect();

To correctly disconnect and cleanup the MethodInvocationRemoteReceiver object, the CancelReceive() method should be called on it and its underlying ActiveMqRemoteReceiver object. Additionally on the C# side, the Dispose() method should be called to correctly cleanup the unmanaged ActiveMQ code in the ActiveMqRemoteSender and ActiveMqRemoteReceiver objects...

// Stop receiving, disconnect, and dispose methodInvocationReceiver.CancelReceive(); activeMqReceiver.CancelReceive(); activeMqReceiver.Disconnect(); activeMqSender.Disconnect(); activeMqReceiver.Dispose(); activeMqSender.Dispose();

The diagram below describes the flow of data between the various objects and components in the first sample application...

ActiveMqRemoteSender and ActiveMqRemoteReceiver
  1. Java code calls method InvokeMethod() on MethodInvocationRemoteSender object to initiate the remote invocation of method ApproximatePi().
  2. MethodInvocationRemoteSender object serializes the method invocation as an XML document, and sends it to an ActiveMQ queue via an ActiveMqRemoteSender object.
  3. The ActiveMqRemoteReceiver object in C# code retrieves the serialized method invocation from the ActiveMQ queue.
  4. MethodInvocationRemoteReceiver object event 'MethodInvocationReceived' is triggered, and handled by C# client code, calling the ApproximatePi() method.
  5. C# client code calls method SendReturnValue() on the MethodInvocationRemoteReceiver object, to send the value returned by the ApproximatePi() method.
  6. MethodInvocationRemoteReceiver object serializes the return value as an XML document, and sends it to an ActiveMQ queue via an ActiveMqRemoteSender object.
  7. ActiveMqRemoteReceiver object in Java code retrieves the serialized return value from the ActiveMQ queue.
  8. The return value is deserialized by the MethodInvocationRemoteSender object (via a MethodInvocationSerializer object), and returned to the client code.

The diagram below shows more detail of the ActiveMqRemoteSender and ActiveMqRemoteReceiver objects, including the queue filters which allow a single ActiveMQ queue to be used for both method invocations (requests) and return values (responses).

ActiveMqRemoteSender and ActiveMqRemoteReceiver Detail

Setup notes

First start the ActiveMQ broker, and ensure the 'connectUri' member is correctly configured for the broker in the Program class in both C# and Java. Next start the C# code by executing the SampleApplication1.exe file. This will display the following text in the console...

Waiting for incoming method calls... Press [ENTER] to cancel.

Then start the compiled java code, using a command similar to the following...

java -cp "[path containing MethodInvocationRemoting]\MethodInvocationRemoting\Java\SampleApplication1\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 in the Java console...

10000 scenarios returned a value of 3.1420 100000 scenarios returned a value of 3.14128 1000000 scenarios returned a value of 3.141768 10000000 scenarios returned a value of 3.1424872 100000000 scenarios returned a value of 3.14162380

... and in the C# console, the following logging information is displayed to confirm the method invocations have been received...

Received invocation of method 'ApproximatePi(10000)' Sending return value 3.1420 Received invocation of method 'ApproximatePi(100000)' Sending return value 3.14128 Received invocation of method 'ApproximatePi(1000000)' Sending return value 3.141768 Received invocation of method 'ApproximatePi(10000000)' Sending return value 3.1424872 Received invocation of method 'ApproximatePi(100000000)' Sending return value 3.14162380

Note that if logging is enabled, additional log information may be output to the console along with the above information. Press 'Enter' at the C# console to stop the C# code.

The functionality that MethodInvocationRemoting provides in this sample application could also be provided by more standard and interoperable frameworks (e.g. by exposing the ApproximatePi() method through a webservice). However, MethodInvocationRemoting also allows for bidirectional sending of data, which is demonstrated in the second sample application.

Note that the sample application could be altered to use either the file system or a TCP network for transporting the method invocations. This could be achieved by substituting the ActiveMqRemoteSender and ActiveMqRemoteReceiver classes for either FileRemoteSender and FileRemoteReceiver, or TcpRemoteSender and TcpRemoteReceiver. Similar substitution of the transport classes is demonstrated in the second sample application.