- Undocumented Matlab - https://undocumentedmatlab.com -

Extending a Java class with UDD

Posted By Yair Altman On March 29, 2012 | 14 Comments

Once again I welcome Donn Shull, with another article about Matlab’s internal UDD mechanism

Extending a Java class with UDD

During the series on UDD [1], we have mentioned the connection between UDD and Java. In UDD Events and Listeners [2] we described how in Matlab, each Java object can have a UDD companion. In Hierarchical Systems with UDD [3] we briefly noted that a UDD hierarchy may be passed to Java. In the numerous posts on handle graphics and callbacks, Yair has discussed the UDD packages javahandle and javahandle_withcallbacks. Based on this information, it seems reasonable to speculate that it may be possible to extend a Java class with UDD using UDD’s class inheritance mechanism.
This can be extremely useful in two cases:

  • You don’t know Java but found a Java class you would like to use in Matlab, it just needs minor modifications for your specific needs
  • You do know Java, but don’t have access to the original source code, and choose to extend the Java class with Matlab code, rather than Java code

Today I will show how this can be done using a simple example. Our example will illustrate the following things:

  1. Subclassing a Java class with UDD
  2. Adding UDD properties to the to the subclass
  3. Overloading a Java method with Matlab code
  4. Directly accessing the superclass methods

The example will show extending Java socket classes to provide a simple method for communication between two Matlab sessions. The protocol has been kept purposely simple and is not robust. Additional work would need to be done to create a real-life socket-based communication between Matlab systems (see for example this [4] FEX submission).
Today’s example consists of two subclasses: a subclass of java.net.ServerSocket [5] and a subclass of java.net.Socket [6]. The protocol will be sending strings back and forth between the two sessions. In each direction the information exchange will consist of two bytes containing the string length, followed by the actual string. The entire source code can be downloaded from here [7].

Creating the simple.ServerSocket class

As in the UDD series, we will use the simple package for our classes and in this package create a ServerSocket class and a Socket class. Recall the simple package definition class is placed in a file named schema.m in a directory called @simple, placed somewhere on the Matlab path. schema.m consists of:

function schema()
%SCHEMA  simple package definition function.
   schema.package('simple');
end

In our ServerSocket class we will add three UDD properties and overload two of the Java class methods. It is worth noting that our final class will have all the parent Java classes public properties and methods and if necessary we can access the parent or super class methods directly. As before, we create a subfolder of the @simple folder named @ServerSocket; in this folder we place four files:

  1. schema.m – the class definition file
  2. ServerSocket.m – the class constructor
  3. accept.m – one of the Java methods that we will overload
  4. bind.m – the other Java method that we will overload

At the beginning of our schema.m file, we will use the following code to subclass the Java class:

function schema
%SCHEMA simple.ServerSocket class definition function.
    % parent schema.class definition
    javaPackage = findpackage('javahandle');
    javaClass = findclass(javaPackage, 'java.net.ServerSocket');
    % class package (schema.package)
    simplePackage = findpackage('simple');
    % class definition
    simpleClass = schema.class(simplePackage, 'ServerSocket', javaClass);

Here, we use findpackage and findclass to obtain the schema.class for the Java class that we are going to use as our parent. We then obtain a handle to the containing package, and finally use the subclass variation to define our ServerSocket as a variation of the Java parent’s schema.class.
Next, in the class definition file we place the code to define the signatures for the methods we are overloading:

    % accept.m overloads java accept method and adds communication protocol
    m = schema.method(simpleClass, 'accept');
    s = m.Signature;
    s.varargin    = 'off';
    s.InputTypes  = {'handle'};
    s.OutputTypes = {'string'};
    % bind.m overloads java bind method
    m = schema.method(simpleClass, 'bind');
    s = m.Signature;
    s.varargin    = 'off';
    s.InputTypes  = {'handle'};
    s.OutputTypes = {};be

Finally, we add three UDD properties to the class: The first will be used to hold a string representation of the address of our ServerSocket; the second will store the communication port number; the third is a handle property that will hold the reference to the socket used by the actual communication.

    % holds remote address as a matlab string
    p = schema.prop(simpleClass, 'address', 'string');
    p.FactoryValue = 'localhost';
    % holds remote port as a matlab int
    p = schema.prop(simpleClass, 'port', 'int16');
    p.FactoryValue = 2222;
    % holds a handle reference to the socket created in the accept method
    p = schema.prop(simpleClass, 'socket', 'handle');
end

We now need to write our overloaded methods. The bind method is simple: it first creates a Java internet address using the new address and port properties; then it uses the standard Java class methods to call the superclass’s bind method with the specified internet address:

function bind(this)
    % use the object socket and port port properties to bind this instance
    % to a address calling the superclass bind method
    inetAddress = java.net.InetSocketAddress(this.address, this.port);
    this.java.bind(inetAddress);
end

The overloaded accept method is a bit more complicated and crude: It starts by calling the superclass accept method to create a communication socket and stores the created socket in our class’s socket property. Then it goes into an infinite loop of waiting for incoming commands, uses evalc to execute them, and returns the captured result to the caller. The only way out of this loop is using Ctrl-C from the keyboard.

function accept(this)
    % use the superclass accept
    this.socket = handle(this.java.accept);
    % infinite loop use ctrl-c to exit
    while 1
        % wait for a command then execute it capturing output
        while this.socket.getInputStream.available < 2
        end
        msb = this.socket.getInputStream.read;
        lsb = this.socket.getInputStream.read;
        numChar = 256 * msb + lsb;
        cmd = uint8(zeros(1, numChar));
        for index = 1:numChar
            cmd(index) = this.socket.getInputStream.read;
        end
        result = evalc(char(cmd));
        % send the result back to the calling system
        len = numel(result);
        msb = uint8(floor(len/256));
        lsb = uint8(mod(len,256));
        this.socket.getOutputStream.write(uint8([msb, lsb, result]));
    end
end

Creating the simple.Socket class

The simple.Socket class is created like ServerSocket, this time in the @Socket folder under the @simple folder. In this subclass we add properties for the address and port, just as in ServerSocket. We overload the superclass's connect method with our own variant, and add a new method to make the remote calls to the ServerSocket running in another Matlab instance. Beginning with the schema.m file we have:

function schema
%SCHEMA simple.Socket class definition function.
    % package definition
    simplePackage = findpackage('simple');
    javaPackage = findpackage('javahandle');
    javaClass = findclass(javaPackage, 'java.net.Socket');
    % class definition
    simpleClass = schema.class(simplePackage, 'Socket', javaClass);
    % define class methods
    % connect.m overloads java connect method
    m = schema.method(simpleClass, 'connect');
    s = m.Signature;
    s.varargin    = 'off';
    s.InputTypes  = {'handle'};
    s.OutputTypes = {};
    % remoteEval.m matlab method for remote evaluation of Matlab commands
    m = schema.method(simpleClass, 'remoteEval');
    s = m.Signature;
    s.varargin    = 'off';
    s.InputTypes  = {'handle', 'string'};
    s.OutputTypes = {'string'};
    % add properties to this class
    % holds remote address as a Matlab string
    p = schema.prop(simpleClass, 'address', 'string');
    p.FactoryValue = 'localhost';
    % holds remote port as a Matlab int
    p = schema.prop(simpleClass, 'port', 'int16');
    p.FactoryValue = 2222;
end

The class constructor Socket.m is simply:

function skt = Socket
%SOCKET constructor for the simple.Socket class
    skt = simple.Socket;
end

The overloaded connect method is almost identical to the overloaded bind method we used for ServerSocket. We form a Java internet address from our new properties and then invoke the superclass’s connect Java method:

function connect(this)
%CONNECT overload of the java.net.Socket connect method
    % use the object address and port properties to connect to the remote
    % session via the superclass connect method
    inetAddress = java.net.InetSocketAddress(this.address, this.port);
    this.java.connect(inetAddress);
end

Finally, our remoteEval method is very similar to the loop portion of the overloaded accept method we wrote for simple.ServerSocket. We take the command string input and convert it into a series of bytes prepended by the length of the string, send it to the other Matlab session and wait for a response:

function result = remoteEval(this, cmd)
%REMOTEEVAL evaluate a Matlab command on a remotely connected Matlab
    % The command string is sent as a series of bytes preceded by a pair of
    % bytes which represents the length of the string
    cmd = uint8(cmd);
    len = numel(cmd);
    msb = uint8(floor(len/256));
    lsb = uint8(mod(len,256));
    this.getOutputStream.write([msb, lsb, cmd]);
    % We will expect the remote session to return a string in the same format
    % as the command
    while this.getInputStream.available < 2
    end
    msb = this.getInputStream.read;
    lsb = this.getInputStream.read;
    numChar = 256 * msb + lsb;
    result = uint8(zeros(1, numChar));
    for index = 1:numChar
        result(index) = this.getInputStream.read;
    end
    result = char(result);
end

Using simple.ServerSocket and simple.Socket to communicate between Matlab sessions

To use this example, add the zip contents to your Matlab path, then open an instance of Matlab and issue the following commands:

>> ss = simple.ServerSocket;
>> ss.bind;
>> ss.accept;

Then open another Matlab instance and issue these commands:

>> s = simple.Socket;
>> s.connect;

At this point you can send commands from this Matlab instance (the client) to the first instance (the server) using the remoteEval method. The command will then be transmitted to the server, executed, and the server will return the captured string result to the client:

>> remoteResult = s.remoteEval('pi')
remoteResult =
    3.1416

The defaults are for localhost and port 2222. These can be changed prior to using the server's bind method and the client's connect method. To keep things as simple as possible, error checking etc. has been left out, so this is just a demonstration and is far from robust.
There are some things to note about our new classes. If we type methods(s) or s.methods at the Matlab command prompt in our simple.Socket session we obtain:

>> s.methods
Methods for class simple.Socket:
Socket                     getOOBInline               isClosed                   setReuseAddress
bind                       getOutputStream            isConnected                setSendBufferSize
close                      getPort                    isInputShutdown            setSoLinger
connect                    getReceiveBufferSize       isOutputShutdown           setSoTimeout
equals                     getRemoteSocketAddress     java                       setSocketImplFactory
getChannel                 getReuseAddress            notify                     setTcpNoDelay
getClass                   getSendBufferSize          notifyAll                  setTrafficClass
getInetAddress             getSoLinger                remoteEval                 shutdownInput
getInputStream             getSoTimeout               sendUrgentData             shutdownOutput
getKeepAlive               getTcpNoDelay              setKeepAlive               toString
getLocalAddress            getTrafficClass            setOOBInline               wait
getLocalPort               hashCode                   setPerformancePreferences
getLocalSocketAddress      isBound                    setReceiveBufferSize

This shows that our simple.Socket class has all of the methods of the Java superclass, plus our added remoteEval method and the java method that was automatically added by Matlab. This means that all of the Java methods are methods of our class instance and the added java means that we can access the superclass methods from our class instance if the need arises. If we use the struct function which Yair has previously discussed [8], we obtain:

>> struct(s)
ans =
              OOBInline: 0
                  Bound: 1
                Channel: []
                  Class: [1x1 java.lang.Class]
                 Closed: 0
              Connected: 1
            InetAddress: [1x1 java.net.Inet4Address]
          InputShutdown: 0
            InputStream: [1x1 java.net.SocketInputStream]
              KeepAlive: 0
           LocalAddress: [1x1 java.net.Inet4Address]
              LocalPort: 51269
     LocalSocketAddress: [1x1 java.net.InetSocketAddress]
         OutputShutdown: 0
           OutputStream: [1x1 java.net.SocketOutputStream]
      ReceiveBufferSize: 8192
    RemoteSocketAddress: [1x1 java.net.InetSocketAddress]
           ReuseAddress: 0
         SendBufferSize: 8192
               SoLinger: -1
              SoTimeout: 0
             TcpNoDelay: 0
           TrafficClass: 0
                address: 'localhost'
                   port: 2222

We see that we have access to all of the public properties of the Java superclass, as well as the UDD properties that we have added.

Conclusion

At the beginning of this post I said that this would be a simple non-robust communications method. In order to make this anything more than that, a number of things would need to be implemented, for example:

  • Improve the accept method to exit after a timeout or when a connection has been made and then terminated
  • Add checksums and timeouts for communication to determine the reliability of the communication
  • Add a retry request protocol for instances of communication failure
  • Add support for any serializable Matlab type, not just strings

The intent here was just to show that extending Java classes with Matlab is possible, relatively simple, and can be extremely useful. After all, with over 10 million Java developers out there, chances are that somebody somewhere has already posted a Java class that answers your exact need, or at least close enough that it can be used in Matlab with only some small modifications.

Categories: Guest bloggers, Java, Medium risk of breaking in future versions, Undocumented feature


14 Comments (Open | Close)

14 Comments To "Extending a Java class with UDD"

#1 Comment By Michael On March 22, 2013 @ 08:24

Excellent series on UDD. Thank you so much for taking the time to write this! A quick question. How would one extend a user defined java class. For example, say that I have a java class “AddressBook” in the package “mypackage”. Calling the following doesn’t work.

javaPackage = findpackage('javahandle');
javaClass = findclass(javaPackage, 'mypackage.AddressBook')
javaClass = 
    []

How would I obtain the super schema.class to use when creating my subclass

javaClass = ???
% class package (schema.package)
addressPackage = findpackage('address');
 
% class definition
addressClass = schema.class(addressPackage, 'MatlabAddressBook', javaClass);

#2 Comment By Donn Shull On March 23, 2013 @ 08:10

Hi Michael,

When you use mypackage.AddressBook can you create an instance without any arguments? If the constructor requires arguments then you will not be able to use find class to get your superclass. A work around would be to create an instance of the class in schema.m and get the schema.class from the instance using classhandle.

Donn

#3 Comment By Augustin On December 28, 2013 @ 15:25

Hi Donn,

Many thanks for the very interesting and insightful articles. I have just tried to create a class myTableModel with superclass javax.swing.table.DefaultTableModel, to override the isCellEditable method to always return false, and it works perfectly.

However when I try to set the model of a javax.swing.JTable to an instance of my class myTableModel I get the following error message:

>> configGear_Data={'-1','15','2','500';'1','14','3','500';...
    '2','7','4','500';'3','4.5','5','500';'4','3.4','6','500';...
    '5','2.5','7','500';'6','2.4','8','500';'7','2.3','9','500';...
    '8','2.2','9','500';'9','2.1','9','500';'10','2','10','500';};
>> model = myPackage.myTableModel(configGear_Data,{'Col1','Col2','Col3','Col4'})
model =
	myPackage.myTableModel
>> model.isCellEditable(1,1) % Method overridden
ans =
     0
>> model.java.isCellEditable(1,1) % Original method
ans =
     1
>> jTable=javaObjectEDT('javax.swing.JTable');
>> jTable.setModel(model)
??? Not enough Java memory to construct Java arguments.

Whereas I can easily set:

>> jTable.setModel(model.java)
>> jTable.getModel
ans =
javax.swing.table.DefaultTableModel@726343c4

Do you know why I may be getting this error? I really appreciate your help.

Many thanks

Augustin

#4 Comment By Donn Shull On December 30, 2013 @ 07:55

@Augustin – Unfortunately when you extend a java class using UDD it becomes a UDD class and is no longer a java class that you can use the same way as before. In your application it would be easier to simply extend javax.swing.table.DefaultTableModel with java.

#5 Comment By Augustin On December 31, 2013 @ 05:25

@Donn,

Thanks for your help, that’s what I had done before reading your article. It would have been nice to keep all the code in matlab but if it’s not possible..

Thanks anyway, Happy new year.

#6 Comment By Edward Maros On August 26, 2016 @ 19:25

I have enjoyed this series and have found it to be very useful as I am creating a MATLAB wrapping of my java classes. The one question that I have concerns constants.

My java class defines several static constants that I want to expose to the matlab class. I want them to be referenced without having to have instantiated the MATLAB class. If this is possible, what is the syntax.

Thanks,
Ed

#7 Comment By Yair Altman On August 26, 2016 @ 19:43

@Ed – assuming your static constants are public (which they must be, otherwise you cannot use them in Matlab), you can simply use the classname:

myField = MyJavaClassName.static_field_name;

#8 Comment By Edward Maros On August 26, 2016 @ 21:52

Yair,

If I need to use the java class directly, I need help on one other piece. My MATLAB class is derived from one of two java classes. This decision is made in the schema.m file and is based on an environment variable. Using UDD, can I get the java class that is used without having to instantiate an object?

PS. Sorry if this is posted out of order, but there was no reply button under your helpful suggestion.

#9 Comment By Edward Maros On August 26, 2016 @ 21:56

Correction. Using MATLAB, can I get the JAVA base class of my UDD based extension?

#10 Comment By Yair Altman On August 27, 2016 @ 22:47

@Ed – I’m not exactly sure what you mean, but if you’re saying that you only have the class name and wish to avoid instantiating a class object, then you can use Matlab’s eval function to evaluate it in run-time. If you need additional assistance then contact me by email (altmany at gmail) for a short consultancy.

#11 Comment By Yuri Shirman On September 4, 2016 @ 11:22

Yair,

I have a couple of questions:
1. Is this sample compatible with Matlab 2014b?
2. How this sample would look with +package and classdef definitions?

Currently I get the following error when I try your sample (everything relevant is added on the matlab path)

ss = simple.SeverSocket

Undefined variable “simple” or function “simple.SeverSocket”.

#12 Comment By Donn Shull On September 4, 2016 @ 21:31

Hi Yuri,

This example works with most MATLAB releases through R2016a. If you download the zip file and use those files it should work. If you are copying the code from the text of the article there is a syntax error in the schema.m file for simple.ServerSocket. Line 1 should be:

function schema

rather than:

function schema.m

Donn

#13 Comment By Yair Altman On September 4, 2016 @ 21:57

I fixed the post text

#14 Comment By Yuri Shirman On September 13, 2016 @ 15:05

It appears that only with static path I was able to run the example.
However the virtual function override does not catch: is it parameters problem?

onRequest.m:

function res  = onRequest(this, cmd)
res = cmd;

schema.m:

% ...
% define class methods
% onRequest.m overloads java onRequest method
m = schema.method(svrClass, 'onRequest');
s = m.Signature;
s.varargin    = 'off';
s.InputTypes  = {'string'};
s.OutputTypes = {'string'};

However, I see only the method of the base class running (the override does not catch):

@Override
public String onRequest(String request) {
    System.out.println("### (onRequest)");
    return "";
}

Article printed from Undocumented Matlab: https://undocumentedmatlab.com

URL to article: https://undocumentedmatlab.com/articles/extending-a-java-class-with-udd

URLs in this post:

[1] series on UDD: http://undocumentedmatlab.com/?s=UDD

[2] UDD Events and Listeners: http://undocumentedmatlab.com/blog/udd-events-and-listeners/

[3] Hierarchical Systems with UDD: http://undocumentedmatlab.com/blog/hierarchical-systems-with-udd/

[4] this: http://www.mathworks.com/matlabcentral/fileexchange/24524-tcpip-communications-in-matlab

[5] java.net.ServerSocket: http://docs.oracle.com/javase/6/docs/api/java/net/ServerSocket.html

[6] java.net.Socket: http://docs.oracle.com/javase/6/docs/api/java/net/Socket.html

[7] from here: http://undocumentedmatlab.com/files/simple2.zip

[8] previously discussed: http://undocumentedmatlab.com/blog/matlab-java-memory-leaks-performance/#struct

[9] Java class access pitfalls : https://undocumentedmatlab.com/articles/java-class-access-pitfalls

[10] Accessing internal Java class members : https://undocumentedmatlab.com/articles/accessing-internal-java-class-members

[11] UDD and Java : https://undocumentedmatlab.com/articles/udd-and-java

[12] Creating a simple UDD class : https://undocumentedmatlab.com/articles/creating-a-simple-udd-class

[13] JMI – Java-to-Matlab Interface : https://undocumentedmatlab.com/articles/jmi-java-to-matlab-interface

[14] Setting class property types – take 2 : https://undocumentedmatlab.com/articles/setting-class-property-types-2

Copyright © Yair Altman - Undocumented Matlab. All rights reserved.