Skip navigation links
Contents

See: Description

Packages 
Package Description
com.mathworks.mps.client
Provides library support for Java clients to communicate with MATLAB Production Server.
com.mathworks.mps.client.annotations
Contains custom annotations for MATLAB Production Server Java client
com.mathworks.mps.client.rest
RESTful API and Protocol Buffer Support

Contents

  1. Overview
  2. Quick Example
  3. Programming Guide
  4. Data Marshaling
  5. Marshaling MATLAB structures (or structs)
  6. Logging
  7. RESTful API and Protocol Buffer Support

Overview

MATLAB Production Server is a client-server framework created by MathWorks for deploying MATLAB code in scalable and robust manner. The server hosts one or more deployable archives, each exporting one or more MATLAB functions. These MATLAB functions can be invoked from a Java application using the Production Server Java client. The client sends request with all the information required to invoke the MATLAB function and receives the result in the response. The server may be running on the same machine as the client or on a remote machine.

The following example demonstrates how MATLAB Production Server can be used to create a scalable MATLAB application.


Quick Example

This example deploys a simple MATLAB function to a MATLAB Production Server instance and creates a Java application that accesses it:

STEP 1: Write a MATLAB function.

STEP 2: Create a deployable archive.

STEP 3: Start a MATLAB Production Server instance.

STEP 4: Deploy the deployable archive to a MATLAB Production Server instance.

STEP 5: Write Java interface for MATLAB function.

STEP 6: Write Java application to call MATLAB function.

STEP 7: Enable Java client logging for information and debugging.

NOTE: For the sake of simplicity, we will assume that the client and server are running on the same machine. We will also assume that MATLAB and MATLAB Compiler SDK are installed on this machine.

STEP 1 : Write a MATLAB function

Create a simple MATLAB function, mymagic, that takes a single int32 input and returns a magic square as a 2-D double array:

    function m = mymagic(in)
      m = magic(in);
    end

STEP 2 : Create a Deployable Archive

Compile the MATLAB function into a deployable archive as follows:

  1. Start the Production Server Compiler app from MATLAB.
  2. Create a new project with the name magic with Deployable Archive as the target.
  3. Add the MATLAB function, mymagic, to this project.
  4. Build the project to create magic.ctf in the magic/for_redistribution_files_only directory.

STEP 3 : Start a MATLAB Production Server instance

Note: For this example the MATLAB Production Server is installed in $MPS_INSTALL directory and $MPS_INSTALL/script is on the system path. The directory where we want to create the MATLAB Production Server instance is /tmp/mpsInst1.

Note: The paths used in the instructions are for Linux.

Note: Before you can proceed:

  1. You must have installed a MATLAB Runtime instance.

  2. Configured MATLAB Production Server to use it by running mps-setup.

Before you can start a MATLAB Production Server instance, you must create it:

  1. Enter the following command in a terminal window to create a new instance of MATLAB Production Server at /tmp/mpsInst1.

    Note : The directory /tmp/mpsInst1 must not exist before entering the following command.

    % mps-new /tmp/mpsInst1
    %
    
  2. Verify the configuration of a MATLAB Production Server instance by editing /tmp/mpsInst1/config/main.config.

    This file has the default configuration parameters when a MATLAB Production Server instance is created for the first time.

Start the server instance:

  1. Change the current working directory to the new MATLAB Production Server instance.
  2. Start the server with the mps-start command.
    % cd /tmp/mpsInst1
    % mps-start
    %

    By default, the MATLAB Production Server instance listens to port number 9910 for client requests. This can be verified in the /tmp/mpsInst1/endpoint/http file:

    % cat /tmp/mpsInst1/endpoint/http
    127.0.0.1:9910
    
  3. Check status of MATLAB Production Server instance can be checked using the mps-status command:
    % mps-status
    'tmp/mpsInst1' STARTED
    license checked out
    

STEP 4 : Deploy the Deployable Archive on the MATLAB Production Server instance

Once the MATLAB Production Server instance is started, you need to deploy the deployable archive to it. To deploy the deployable archive, magic.ctf, copy it to the /tmp/mpsInst1/auto_deploy folder of the server instance.

Now, it is available to the MATLAB Production Server Java client at URL: http://localhost:9910/magic

The format of the URL is: http://<host_name>:<port_number>/<archive_file_name_without_extension>

STEP 5 : Write the Java Interface

In order to use the MATLAB function in a Java application, you need to define it using a Java interface. The interface must:

  1. include a method with the same name as the MATLAB function
  2. take parameters that are consistent with the MATLAB function's input arguments
  3. return an object the is consistent with the MATLAB function's output arguments
  4. declare two checked exceptions:
    1. com.mathworks.mps.client.MATLABException : For reporting MATLAB errors
    2. java.io.IOException : For any transport error during client-server communication

The Java interface for mymagicwill look like following:

   interface MatlabMagic {
       double[][] mymagic(int size) throws IOException, MATLABException;
   } 

The interface name can be any valid Java names.

STEP 6: Write Java application to call MATLAB function.

Note: The MATLAB Production Server Java client API is included in mps_client.jar. It is located in the $MPS_INSTALL/client/java directory where $MPS_INSTALL is the root directory under which MATLAB Production Server is installed.

Before you can call the method representing the MATLAB function, you must:

  1. Create an instance of MWHttpClient to manage connections to MATLAB Production Server instances.
  2. Create a proxy object for the deployed archive hosting the MATLAB function.
import java.net.URL;
import java.io.IOException;
import com.mathworks.mps.client.MWClient;
import com.mathworks.mps.client.MWHttpClient;
import com.mathworks.mps.client.MATLABException;

interface MatlabMagic {    
    double[][] mymagic(int size) throws IOException, MATLABException;
}

public class Magic {
    
    public static void main(String[] args){
    
        // Create a non-interruptible MWHttpClient instance
        MWClient client = new MWHttpClient();
        
        try{
            // Create the proxy object that represents magic.ctf
            MatlabMagic m = client.createProxy(new URL("http://localhost:9910/magic"), MatlabMagic.class );
            
            // The proxy object has mymagic as one of its public methods. Invocation of mymagic
            // results in a server request that gets the magic square in response
            double[][] result = m.mymagic(3);
            
            // Let's print the magic square
            printResult(result);
            
        }catch(MATLABException ex){
        
            // This exception represents errors in MATLAB and provides useful information
            // like the MATLAB stack trace or the error ID associated with this error.
            System.out.println(ex);            
        }catch(IOException ex){
        
            // This exception can represent network issues. It is also thrown when the 
            // HTTP response received from the server has a status of 4XX or 5XX
            System.out.println(ex);                    
        }finally{
        
            // We should close the client when we know that we are not going to need it any more
            // Once the client is closed, an exception will be thrown if a MATLAB function is 
            // invoked using the proxy object that was created using client.
            client.close();        
        }
    }
    
    private static void printResult(double[][] result){
        for(double[] row : result){
            for(double element : row){
                System.out.print(element + " ");
            }
            System.out.println();
        }        
    }
}

STEP 7: Enable Java client logging for information and debugging.

You can quickly enable logging of the Java client operations by providing a log4j configuration file at the start of the Java virtual machine (JVM). The file can be provided using the -D JVM startup option to set the log4j.configuration property to point to the file.

A default log4j.properties file is provided along with the Java client library mps_client.jar at the $MPS_INSTALL/client/java location. This default logging configuration will output information of INFO level or higher to the standard output.

Note: Files should be provided with the file: prefix before the path.

    java -cp ./mps_client.jar:./Magic.jar -Dlog4j.configuration=file:/$MPS_INSTALL/client/java/log4j.properties Magic

For more information on logging, see the Logging section.


Programming guide

  1. Required JAR files
  2. Life cycle management of MWHttpClient
  3. Configure the Client-Server Connection
  4. Invoke MATLAB Functions Dynamically
  5. Invoke MATLAB Functions Asynchronously
  6. Exception Handling
  7. Accessing secure CTFs using HTTPS
  8. Handling MATLAB functions with multiple outputs
  9. Handling MATLAB functions with varargin and varargout

Required JAR files

The following JAR file is required to work with the MATLAB Production Server Java client API:

$MPS_INSTALL/client/java/mps_client.jar

Life cycle management of MWHttpClient

A single Java client can connect to one or more servers available at different URL's. Even though users can create multiple instances of MWHttpClient, a single instance can be used to establish connections with multiple servers.

Proxy objects created by using an instance of MWHttpClient can communicate with MATLAB Production Server only until the close method of that instance is invoked. It is important to call the close method once the MWHttpClient instance is no more needed to reclaim the system resources.

For a locally scoped instance of MWHttpClient (e.g a standalone application), the Java client code will look like following:

MWClient client = new MWHttpClient();
try{
    // Code that uses client to communicate with the server
}finally{
    client.close();
}

If MATLAB Production Server Java client is used in a servlet, MWHttpClient instance can be tied to the life cycle of the servlet instance by initializing it in the HttpServlet.init() method and can be closed in the HttpServlet.destroy() method.

public class ExampleServlet extends HttpServlet {
    private final MWClient client;
    
    public void init(ServletConfig config) throws ServletException {
        client = new MWHttpClient();
    }
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
    throws ServletException,java.io.IOException {
    
       // Code that uses client to communicate with the server    
    }
    
    public void destroy() {
        client.close();
    }
}

Configure the Client-Server Connection

You configure the client-server connection using an object that implements the MWHttpClientConfig interface. This interface defines the following properties:

  1. Interruptable - determines if MATLAB functions can be invoked asynchronously
  2. TimeOutMs - determines the amount of time, in milliseconds, the client waits for a response before timing out
  3. MaxConnections - determines the maximum number of connections the client opens to fulfil multiple requests
  4. ResponseSizeLimit - determines the maximum size, in bytes, of the response a client accepts.

The API provides a default implementation,MWHttpClientDefaultConfig, that is automatically used when an HTTP/HTTPS client is instantiated. To modify the configuration, extend MWHttpClientDefaultConfig and pass it to the HTTP client's constructor.

Using the Default Connection Configuration

When you create a client connection using the default constructor, MWHttpClient(), an instance of MWHttpClientDefaultConfig is automatically used to configure the client-server connection. The default configuration sets the connection properties as follows:

  1. Interruptable = false
  2. TimeOutMs = 120000
  3. MaxConnections = -1 specifying that the client will use as many connections as allowed by the system
  4. ResponseSizeLimit = 64*1024*1024 (64MB)

NOTE : The default connection configuration only allows a blocking call to MATLAB function. For a non-blocking invocation of MATLAB function, please refer to the section Invoke MATLAB Functions Asynchronously.

Using a Custom Connection Configuration

To change one or more connection properties, use the following steps:

  1. Implement a custom connection configuration by extending MWHttpClientDefaultConfig.
  2. Create the client connection using one of the constructors that accepts a configuration object.

    MWHttpClient(MWHttpClientConfig config)
    MWHttpClient(MWHttpClientConfig config, MWSSLConfig sslConfig)

The following code sample creates a client connection with a timeout value of 1000ms:

class MyClientConfig extends MWClientDefaultConfig
{
  public long getTimeOutMs()
  {
    return 1000;
  }
}
...
MWClient client = new MWHttpClient(new MyClientConfig());
...

Implement a Custom Connection Configuration

You implement a custom connection configuration by extending MWHttpClientDefaultConfig. MWHttpClientDefaultConfig with one getter method for each configuration property:

  1. public int getMaxConnectionsPerAddress() - returns the value for the maximum number of connections a client can use to handle simultaneous requests.
  2. public long getTimeOutMs() - returns the number of milliseconds the client will wait for a response before generating an error.
  3. public boolean isInterruptible() - returns a boolean specifying if the MATLAB function can be invoked asynchronously. If this method returns false, getMaxConnectionsPerAddress() must return -1.
  4. public int getResponseSizeLimit() - returns the maximum number of bytes the client can accept in a response.

You only need to override the getters for properties you wish to change. For example to specify that a client's MATLAB functions can be interrupted you provide an override for isInterruptible():

class MyClientConfig extends MWClientDefaultConfig
{
  public boolean isInterruptible()
  {
    return true;
  }
}

To specify that a client times out after one second and can only accept 4MB responses you override getTimeOutMs() and getResponseSizeLimit():

class MyClientConfig extends MWClientDefaultConfig
{
  public long getTimeOutMs()
  {
    return 60000;
  }
  public int getResponseSizeLimit()
  {
    return 4*1024*1024;
  }
}

Invoke MATLAB Functions Dynamically

Dynamically invoking MATLAB functions does not require the creation of an interface modelling the contents of a deployable archive. This means that you do not need to recompile your application every time you add a function to a deployed archive.

Dynamic invocation uses a reflection-based proxy to construct the MATLAB function request that is passed to the server instance. When invoking a function, you specify the function name as one of the parameters to the method invoking the request.

To dynamically invoke a MATLAB function:

  1. Instantiate an instance of the MWHttpClient class.
  2. Create a reflection-based proxy object using one of the client connection's createComponentProxy() methods.
  3. Invoke the function using one of the reflection-based proxy's invoke() methods.

Create a Reflection-Based Proxy

A reflection-based proxy implements the MWInvokable interface and provides methods that allow you directly invoke any MATLAB function deployed as part of a deployable archive. Like the interface-based proxy, it is created from the client connection object. The MWHttpClient class has two methods for creating a reflection-based proxy:

  1. MWInvokable createComponentProxy(URL archiveURL) - creates a proxy that uses standard MATLAB data types
  2. MWInvokable createComponentProxy(URL archiveURL, MWMarshalingRules marshalingRules) - creates a proxy that uses structures

To create a proxy for dynamically invoking functions in the archive myMagic hosted on your local computer:

MWClient myClient = new MWHttpClient();

URL archiveURL = new URL("http://localhost:9910/myMagic");
MWInvokable myProxy =  myClient.createComponentProxy(archiveURL);

Invoke a MATLAB Function with a Reflection-Based Proxy

A reflection-based proxy has three methods that can be used to invoke functions on a server:

  1. Object[] invoke(final String functionName, final int nargout, final Class<T> targetType, final Object... inputs) - invoke a function that returns nargout values
  2. <T> T invoke(final String functionName, final Class<T> targetType, final Object... inputs) - invoke a functions that returns a single value
  3. invokeVoid(final String functionName, final Object... inputs) - invoke a function that returns no values

All of the methods map to the MATLAB function as follows:

  1. The first argument is the function's name
  2. The middle set of arguments, nargout and targetType, represent the function's return values
  3. The last arguments are the function's inputs
Return Multiple Outputs

The MATLAB function myLimits returns two values.

function [myMin,myMax] = myLimits(myRange)
 myMin = min(myRange);
 myMax = max(myRange);
end

To invoke myLimits from a Java client you use the invoke() method that takes the number of return arguments:

double[] myRange = new double[]{2,5,7,100,0.5};
try
{
  Object[] myLimits = myProxy.invoke("myLimits", 2, Object[].class, myRange);
  double myMin = ((Double) myLimits[0]).doubleValue(); 
  double myMax = ((Double) myLimits[1]).doubleValue();
  System.out.printf("min: %f max: %f",myMin,myMax);
}
catch (Throwable e)
{
  e.printStackTrace();
}

Because Java cannot determine the proper types for each of the returned values, this form of invoke always returns Object[] and always takes Object[].class as the target type. You are responsible for casting the returned values into the proper types.

Return a Single Output

The MATLAB function addmatrix returns a single value.

function a = addmatrix(a1, a2)
a = a1 + a2;

To invoke addmatrix from a Java client you use the invoke() method that does not take the number of return arguments:

double[][] a1={{1,2,3},{3,2,1}};
double[][] a2={{4,5,6},{6,5,4}};
try
{
  Double[][] result = myProxy.invoke("addmatrix", Double[][].class, a1, a2);

  for(Double[] row : result)
  {
    for(double element : row)
    {
      System.out.print(element + " ");
    }
  }
} catch (Throwable e)
{
  e.printStackTrace();
}
Return No Outputs

The MATLAB function foo does not return value.

function foo(a1)
min(a1);

To invoke foo from a Java client you use the invokeVoid() method:

double[][] a={{1,2,3},{3,2,1}};
try
{
  myProxy.invokeVoid("foo", (Object)a);
}
catch (Throwable e)
{
  e.printStackTrace();
}

Marshal Structs

If any of the MATLAB functions in a deployable archive uses structures, you need to provide marshaling rules to the reflection-based proxy. You provide the marshaling rules to the proxy by:

  1. Implementing a new set of marshaling rules by extending MWDefaultMarshalingRules to use a list of the classes being marshaled.
  2. Creating the proxy using createComponentProxy(URL archiveURL, MWMarshalingRules marshalingRules).

The deployable archive studentChecker includes functions that use a MATLAB structure of the form

S = 
name: 'Ed Plum'
score: 83
grade: 'B+'

Your Java client code represents the MATLAB structure with a class named Student. To create a marshaling rule for dynamically invoking the functions in studentChecker, you create a class named studentMarshaler.

class studentMarshaler extends MWDefaultMarshalingRules
{
  public List<Class> getStructTypes() {
    List structType = new ArrayList<Class>();
    structType.add(Student.class);
    return structType;
  }
}

You create the reflection-based proxy for studentChecker by passing studentMarshaler to createComponentProxy().

URL archiveURL = new URL("http://localhost:9910/studentCheck");
myProxy =  myClient.createComponentProxy(archiveURL, new StudentMarshaler());

For more information about using MATLAB structures see Marshaling MATLAB structures (or structs).


Invoke MATLAB Functions Asynchronously

MATLAB functions can be invoked asynchronously using the invokeAsync method provided by the MWInvokable interface. As can be seen in the example later, the asynchronous feature uses the approach of dynamic invocation of MATLAB functions only and not the interface based approach. The invokeAsync method is a non-blocking method that fires the MATLAB execution request and returns the control back to the client application. This is particularly useful for long running MATLAB functions. There are 2 ways to get to the response of MATLAB function execution request:

  1. The MWRequest instance returned by invokeAsync provides access to java.util.concurrent.Future using the getFuture method. Client application can invoke the get method provided by java.util.concurrent.Future to get the MATLAB result. The call to get is a blocking call and will wait for MATLAB to finish execution if it is not already done. Once MATLAB result is received, it is cached and future calls to the get method will result the cached response.
  2. import java.net.URL;
    import java.util.concurrent.Future;
    import com.mathworks.mps.client.*;
    
    class MyConfig extends MWHttpClientDefaultConfig{
        public boolean isInterruptible() { return true; }
        public int getMaxConnectionsPerAddress() { return 10; }
    }
    
    public class MagicAsync{
    
        public static void main(String[] args){
            MWClient client = new MWHttpClient( new MyConfig() );
    
            try{
                MWInvokable invokable = client.createComponentProxy(new URL("http://localhost:9910/magic"));
    
                MWInvokeRequest httpRequest = new MWInvokeRequest("mymagic", double[][].class);
                httpRequest.setInputParams(4);
                httpRequest.setNargout(1);
    
                MWRequest request = invokable.invokeAsync(httpRequest, null);
                Future f = request.getFuture();
    
                double[][] res = f.get();
                printResult(res);
    
            }catch(Exception ex){
    
                System.out.println(ex);
            }
            finally{
                client.close();
            }
        }
    
        private static void printResult(double[][] result){
            for(double[] row : result){
                for(double element : row){
                    System.out.print(element + " ");
                }
                System.out.println();
            }
        }
    }
    

  3. Users can register a callback using the MWRequestListener instance passed as an input to invokeAsync. This way, they will be notified of various state changes that a request goes through during its life time. However, it should be noted that the client is not guaranteed to receive notification of every state depending on how busy the server is. It is possible that request is ready with response before client gets a chance to ask for status of intermediate states.

  4. import java.net.URL;
    import com.mathworks.mps.client.*;
    
    class MyClientConfig extends MWHttpClientDefaultConfig{
        public boolean isInterruptible() { return true; }
        public int getMaxConnectionsPerAddress() { return 10; }
    }
    
    class ReqListener implements MWRequestListener{
    
        public ReqStateVisitor visitor;
    
        public ReqListener(ReqStateVisitor visitor) { this.visitor = visitor; }
    
        public void notify(MWRequest request) {
            MWRequestState state = request.getState();
            state.visit(visitor);
        }
    }
    
    class ReqStateVisitor implements MWRequestStateVisitor {
    
        private MWClient client;
    
        public ReqStateVisitor(MWClient client){ this.client = client; }
    
        public void cancelled() { }
    
        public void expired() { }
    
        public void failed(Exception ex) { }
    
        public void interrupted() { }
    
        public void inQueue(long timeStamp, URL requestURL) { }
    
        public void processing(long timestamp, URL requestURL) { }
    
        public void sending(byte[] data, URL serviceURL) { }
    
        public void ready(T responseData) {
            printResult( (double[][])responseData );
            client.close();
        }
    
        public void printResult(double[][] result){
            for(double[] row : result){
                for(double element : row){
                    System.out.print(element + " ");
                }
                System.out.println();
            }
        }
    }
    
    public class MagicAsyncWithListener{
    
        public static void main(String[] args){
    
            MWClient client = new MWHttpClient(new MyClientConfig());
            ReqStateVisitor v = new ReqStateVisitor(client);
            ReqListener listener = new ReqListener(v);
    
            try{
                MWInvokable invokable = client.createComponentProxy(new URL("http://localhost:9910/magic"));
    
                MWInvokeRequest httpRequest = new MWInvokeRequest("mymagic", double[][].class);
                httpRequest.setInputParams(4);
                httpRequest.setNargout(1);
    
                invokable.invokeAsync(httpRequest, listener);
    
            }catch(Exception ex){
                System.out.println(ex);
            }
        }
    }
    

NOTE : invokeAsync is only usable when the MWHttpClient instance is created using an interruptible MWHttpClientConfig. Users can either implement the MWHttpClientConfig interface or extend the MWHttpClientDefaultConfig class to override the isInterruptible method so that it returns true.


Exception handling

MATLAB Production Server Java client application has to handle following checked exceptions:

  1. com.mathworks.mps.client.MATLABException : This represents a MATLAB error. It is thrown if there is an error in MATLAB when a proxy object method is executed. Following MATLAB information can be obtained from an instance of this class :
    1. Stack trace
    2. Error ID
    3. Error message
  2. java.io.IOException : This is thrown if there are either any network related failures or if the server returns with a response status of either 4XX or 5XX.
    1. com.mathworks.mps.client.MWHttpException, a subclass of java.io.IOException is also available if one needs to handle response status of 4XX or 5XX in a special manner.


Accessing secure Programs using HTTPS

Connecting to a MATLAB Production Server instance over HTTPS provides a secure channel for executing MATLAB functions. To establish an HTTPS connection with a MATLAB Production Server instance:

  1. Ensure that the server is configured to use HTTPS.
  2. Install the required credentials on the client system.
  3. Configure the client's Java environment to use the credentials.
  4. Create the proxy using the program's https:// URL.

MATLAB Production Server's Java client API also provides:

  1. a hook for providing your own HostnameVerifier
  2. a hook for implementing server authorization beyond that provided by HTTPS

Configure the client environment for SSL

Use keytool to manage the key store and trust stores on the client machine. At a minimum the client requires the server's root CA (Certificate Authority) in its truststore.

If the client needs to connect to a server that requires client-side authentication, it also needs a signed certificate in its key store.

For information on using keytool see Oracle's keytool documentation.

Establish a secure proxy connection

You can create a secure proxy connection with a MATLAB Production Server instance by using the https:// URL for the desired program:

MWClient client = new MWHttpClient();
URL sslURL = new URL("https://hostname:port/myProgram");
MyProxy sslProxy = client.createProxy(sslURL, MyProxy.class);

sslProxy will use the default Java trust store, stored in JAVA_HOME\lib\security\cacerts, to perform the HTTPS server authentication. If the server requests client authentication, the HTTPS handshake will fail because the default SSLContext object created by the JRE does not provide a key store.

To enable your client to connect with a server instance requiring client authentication, you set the key store location and password using Java system properties:

System.setProperty("javax.net.ssl.keystore", "PATH_TO_KEYSTORE");
System.setProperty("javax.net.ssl.keystorePassword", "keystore_pass");
MWClient client = new MWHttpClient();
URL sslURL = new URL("https://hostname:port/myfun");
MyProxy sslProxy = client.createProxy(sslURL, MyProxy.class);

To use a non-default location for the client trust store, you set the trust store location and password using Java system properties:

System.setProperty("javax.net.ssl.truststore", "PATH_TO_TRUSTSTORE");
System.setProperty("javax.net.ssl.truststorePassword", "truststore_pass");
MWClient client = new MWHttpClient();
URL sslURL = new URL("https://hostname:port/myfun");
MyProxy sslProxy = client.createProxy(sslURL, MyProxy.class);

To use a custom SSLContext implementation, add a custom HostnameVerifier implementation, or use the MATLAB Production Server Java API's server authorization, you must provide a custom implementation of MWSSLConfig.

SSL API Configuration

The Java API uses a MWSSLConfig object to get the information it needs to use HTTPS and perform the additional server authorization. The MWSSLConfig interface has three methods:

  1. getSSLContext() - returns the SSLContext object
  2. getHostnameVerifier() - returns the HostnameVerifier object to use if HTTPS hostname verification fails
  3. getServerAuthorizer() - returns the MWSSLServerAuthorizer object to perform server authorization based on the server's certificate

The Java API provides a default MWSSLConfig implementation, MWSSLDefaultConfig, which it uses when no SSL configuration is passed to the MWHTTPClient constructor. The MWSSLDefaultConfig is implemented such that:

  1. getSSLContext() returns the default SSLContext object created by the JRE.
  2. getHostnameVerifier() returns a HostnameVerifier implementation that always returns false. If the HTTPS hostname verification fails, this will not override the HTTPS layer's decision.
  3. getServerAuthorizer() returns a MWSSLServerAuthorizer implementation that authorizes all MATLAB Production Server instances.

Override default hostname verification

As part of the SSL handshake, the HTTPS layer attempts to match the hostname in the provided URL to the hostname provided in the server's certificate. If the two hostnames do not match, the HTTPS layer calls HostnameVerifier.verify() as an additional check. The return value of HostnameVerifier.verify() determines if the hostname is verified.

The implementation of HostnameVerifier.verify() provided by the MWSSLDefaultConfig object always returns false. The result is that if the hostname in the URL and the hostname in the server certificate do not match, the HTTPS handshake fails.

To use a hostname verification scheme that is more robust, you can extend the MWSSLDefaultConfig class to return a version of HostnameVerifier.verify() that uses custom logic. For example, if you only wanted to generate one certificate for all of the servers on which MATLAB Production Server instances run, you could specify MPS for the certificate's hostname. Then your implementation of HostnameVerifier.verify() returns true if the certificate's hostname is MPS.

public class MySSLConfig extends MWSSLDefaultConfig {
  public HostnameVerifier getHostnameVerifier() {
    return new HostNameVerifier() {
      public boolean verify(String s, SSLSession sslSession) {
        if (sslSession.getPeerHost().equals("MPS"))
          return true;
        else
          return false;
      }
    }
  }
}

For more information on HostnameVerify see Oracle's Java Documentation.

Use additional server authentication

After the HTTPS layer establishes a secure connection, a client can perform an additional authentication step before sending requests to a server. This additional authentication os performed by an implementation of the MWSSLServerAuthorizer interface. A MWSSLSServerAuthorizer implementation performs two checks to authorize a server:

  1. isCertificateRequired() determines if server's must present a certificate for authorization. If this returns true and the server has not provided a certificate, the client does not authorize the server.
  2. authorize(Certificate serverCert) uses the server's certificate to determine if the client authorizes the server to process requests.

The MWSSLSServerAuthorizer implementation returned by MWSSLDefaultConfig authorizes all servers without performing any checks.

To use server authentication extend MWSSLDefaultConfig and override the implementation of getServerAuthorizer() to return a MWSSLSServerAuthorizer implementation that does perform authorization checks.


Handling MATLAB functions with multiple outputs

MATLAB allows users to write functions with multiple outputs. Java does not support methods with multiple outputs. Following is a simple MATLAB function signature with 2 outputs and 2 inputs. Let's assume that the first input is of type double and the second is a char array and for the sake of simplicity, let's assume the same types for the first and the second output of this function:

function [out1 out2] = multipleOutputs(in1, in2)

The Java method signature for such a MATLAB function for MATLAB Production Server Java client must include the number of output arguments as the first input, of type int, followed by the remaining inputs. The return type of this Java method must be Object[]. Thus the Java method signature for MATLAB function multipleOutputs will look like :

public Object[] multipleOutputs(int nargout, double in1, String in2);

The actual invocation of this method from the Java application may look something like:

Object[] result = mycomp.multipleOutputs(2, 1.2, "test");

The output result will be an array of length 2 with the first element of Java type double and the second element of Java type char.


Handling MATLAB functions with varargin and varargout

MATLAB Production Server Java client supports MATLAB's variable number of inputs, varargin, and outputs, varargout. Following is a MATLAB function signature that uses varargin and varargout:

function varargout = vararginout(in1, in2, varargin)

Let's assume that the above MATLAB function has double as the type for first input and char array as the type for second input. The type of data being passed as part of varargin can be any supported type. Let's assume that this function returns 2 outputs as varargout with types double and char.

The Java method signature for MATLAB function vararginout will look almost similar to the one discussed in the previous section. The Java method signature supported by MATLAB Production Server Java client for this MATLAB function will look like :

public Object[] vararginout(int nargout, double in1, String in2, Object... vararg);

The actual invocation of this method from the Java application may look something like:

Object[] result = mycomp.vararginout(2, 1.2, "test", true, "another string");
or
Object[] result = mycomp.vararginout(2, 1.2, "test", new Object[]{true, "another string"});


Data Marshaling

  1. MATLAB Data Types
  2. Conversion of Java types to MATLAB types
  3. Conversion of MATLAB types to Java types
  4. Unsupported MATLAB data types
  5. Numeric type coercion
  6. Dimension coercion
  7. Handling MATLAB empty data and Java null
  8. Use of Java boxed types

When MATLAB Production Server Java client invokes a MATLAB function through a request and receives result in the response, data conversion takes place between MATLAB and Java data types.

MATLAB Data Types

There are many different data types, or classes, that you can work with in MATLAB. You can build matrices and arrays of floating-point and integer data, characters and strings, and logical true and false states. Function handles connect your code with any MATLAB function regardless of the current scope. Structures and cell arrays, provide a way to store dissimilar types of data in the same array.

There are 15 fundamental classes in MATLAB. Each of these classes is in the form of a matrix or array. With the exception of function handles, this matrix or array is a minimum of 0-by-0 in size and can grow to an n-dimensional array of any size. A function handle is always scalar (1-by-1). All of the fundamental MATLAB classes are circled in the diagram below:

A structure consists of data containers, called fields, and each of these fields stores an array of some MATLAB data type. Every field has a unique name. A field in a structure can have a value of any of the MATLAB data types, including a cell array or another structure.

A cell array is a collection of containers called cells in which you can store different types of MATLAB data including other cell arrays and structures.

In MATLAB, dimensionality is an attribute of the fundamental types and does not add to the number of types as it does in Java. In Java, double, double[] and double[][][] are three different data types. Where as in MATLAB, there is just double data type and there can be a scalar instance, a vector instance or a multi dimensional instance of this type.

Also, numeric classes in MATLAB include signed and unsigned integers. Java does not have unsigned types.


Conversion of Java types to MATLAB types

The table below shows the data marshaling rules applied when data is passed from Java to MATLAB.

Values passed to Java methodInput type received by MATLABDimension of data in MATLAB
java.lang.Byte, byteint8{1,1}
byte[] data{1, data.length}
java.lang.Short, shortint16{1,1}
short[] data{1, data.length}
java.lang.Integer, intint32{1,1}
int[] data{1, data.length}
java.lang.Long, longint64{1,1}
long[] data{1, data.length}
java.lang.Float, floatsingle{1,1}
float[] data{1, data.length}
java.lang.Double, doubledouble{1,1}
double[] data{1, data.length}
java.lang.Boolean, booleanlogical{1,1}
boolean[] data{1, data.length}
java.lang.Character, charchar{1,1}
char[] data{1, data.length}
java.lang.String data{1, data.length()}
java.lang.String[] datacell{ 1, data.length}
java.lang.Object[] data{ 1, data.length}
In addition, for any convertible data type T above, the following Java type is also a convertible data type
T[] dataMATLAB type for T{ data.length, dimensions(T[0]) }, if T is an Array
{ 1, data.length }, if T is not an Array
NOTE : If T is an array type, then all elements of data must have exactly the same length

Conversion of MATLAB types to Java types

The table below shows the data marshaling rules applied when data is passed from MATLAB to Java.

NOTE: Since Java does not have unsigned numeric types, MATLAB unsigned types are converted to the appropriate signed types in Java as shown in the following table. Also, data marshaling for MATLAB structures is discussed separately in its own section.

What MATLAB returnsDimension of data in MATLABJava type that MATLAB data will be converted to
int8, unit8{1,1}byte, java.lang.Byte
{1,n}byte[n], java.lang.Byte[n]
{m,n,p,...}byte[m][n][p]... , java.lang.Byte[m][n][p]...
int16, unit16{1,1}short, java.lang.Short
{1,n}short[n], java.lang.Short[n]
{m,n,p,...}short[m][n][p]... , java.lang.Short[m][n][p]...
int32, unit32{1,1}int, java.lang.Integer
{1,n}int[n], java.lang.Integer[n]
{m,n,p,...}int[m][n][p]... , java.lang.Integer[m][n][p]...
int64, unit64{1,1}long, java.lang.Long
{1,n}long[n], java.lang.Long[n]
{m,n,p,...}long[m][n][p]... , java.lang.Long[m][n][p]...
single{1,1}float, java.lang.Float
{1,n}float[n], java.lang.Float[n]
{m,n,p,...}float[m][n][p]... , java.lang.Float[m][n][p]...
double{1,1}double, java.lang.Double
{1,n}double[n], java.lang.Double[n]
{m,n,p,...}double[m][n][p]... , java.lang.Double[m][n][p]...
logical{1,1}boolean, java.lang.Boolean
{1,n}boolean[n], java.lang.Boolean[n]
{m,n,p,...}boolean[m][n][p]... , java.lang.Boolean[m][n][p]...
char{1,1}char, java.lang.Character
{1,n}java.lang.String
{m,n,p,...}char[m][n][p]... , java.lang.Character[m][n][p]...
cell (containing only strings){1,1}java.lang.String
{1,n}java.lang.String[n]
{m,n,p,...}java.lang.String[m][n][p]...
cell (containing multiple types){1,1}java.lang.Object
{1,n}java.lang.Object[n]
{m,n,p,...}java.lang.Object[m][n][p]...

Unsupported MATLAB data types

Following data types in MATLAB are not supported for marshaling by MATLAB Production Server Java client:

  1. MATLAB function handles
  2. Complex (imaginary) data
  3. Sparse arrays

Numeric type coercion

With type coercion feature provided by MATLAB Production Server Java client, numeric MATLAB types can be assigned to multiple Java numeric types as long as there is no loss of data or precision. The main exception to this rule is the coercion of MATLAB double data into any Java numeric types. This is allowed because double is the default numeric type in MATLAB and this exception to the coercion rule provides more flexibility to the users of MATLAB Production Server. Following table describes the type compatibility for scalar and non-scalar numeric coercion.

MATLAB typesCompatible Java types supported by coercion
uint8short, int, long, float, double
int8short, int, long, float, double
uint16int, long, float, double
int16int, long, float, double
uint32long, float, double
int32long, float, double
uint64float, double
int64float, double
singledouble
doublebyte, short, int, long, float

NOTE: As non-scalar numeric coercion may impose a performance penalty, especially for large arrays, it is recommended to keep the Java return types consistent with MATLAB function.


Dimension coercion

  1. Dimension expansion
  2. Dimension narrowing

MATLAB Production Server Java client provides some flexibility in how data is received from MATLAB. This flexibility is in the form of the dimensionality of the target Java type corresponding to a result received from MATLAB i.e it is possible for the target Java type to have dimensions either less than or more than the dimensions of data received from MATLAB.

Consider following simple MATLAB function that returns an array of type double with values from 1 to 10.

function out = returnData
out = 1:10;

In MATLAB, out is of type double with dimensions 1x10 (number_of_dimensions = 2).

MATLAB Production Server Java client allows a Java programmer to look at out as any of the following:

  1. A 1-D array of length 10 i.e double[10] (number_of_dimensions = 1)
  2. A 2-D array with dimensions {1,10} i.e double[1][10] (number_of_dimensions = 2)
  3. A 3-D array with dimensions {1,10,1} i.e double[1][10][1] (number_of_dimensions = 3)
  4. An N-D array with dimensions {1,10,1,1,...,1} i.e double[1][10][1][1]..[1] (number_of_dimensions = N)

With MATLAB Production Server Java client, MATLAB function returnData can be represented in Java as any one of the following methods:

double[] returnData() throws MATLABException, IOException;
double[][] returnData() throws MATLABException, IOException;
double[][][] returnData() throws MATLABException, IOException;
double[][][][] returnData() throws MATLABException, IOException;
...
...

Thus, on top of the default behaviour of preserving the number of dimensions of data received from MATLAB, MATLAB Production Server Java client allows dimension coercion in the form of following :


Handling MATLAB empty data and Java null


Use of Java boxed types

MATLAB Production Server Java client will do the primitive to boxed type conversion if users have used boxed types as return types in the Java method signature. E.g following Java method signatures will work interchangeably:

double[] foo();      <->    Double[] foo();
double[][][] foo();  <->    Double[][][] foo();


Marshaling MATLAB structures (or structs)

  1. Simple example
  2. Define MATLAB structure in Java

Structures are MATLAB arrays with elements accessed by textual field designators. Following is an example of how structures are created in MATLAB:

S.name = 'Ed Plum';
S.score = 83;
S.grade = 'B+' 
creates a scalar structure with three fields:
S = 
     name: 'Ed Plum'
    score: 83
    grade: 'B+'
A multi dimensional structure array can be created in MATLAB by inserting additional elements:
S(2).name = 'Toni Miller';
S(2).score = 91;
S(2).grade = 'A-';
creates a structure array of dimensions {1,2}. One can also create structure arrays with more than 2 dimensions.

Since Java does not have built-in support for structure data type, marshaling MATLAB structures between MATLAB Production Server server and client involves extra work.

Simple Example

Following is a simple example of how MATLAB structure can be marshaled between MATLAB Production Server Java client and server

Consider a MATLAB function sortstudents that takes in an array of structures described above. Each element in this array is representing information about a student. This function sorts the input array in the ascending order by score of each student

function sorted = sortstudents(unsorted)
% Receive a vector of students as input
% Get scores of all the students
scores = {unsorted.score};
% Convert the cell array containing scores into a numeric array or doubles
scores = cell2mat(scores);
% Sort the scores array
[s i] = sort(scores);
% Sort the students array based on the sorted scores array
sorted = unsorted(i);

NOTE: Even though sortstudents is only using the score field of the input structure, name and grade fields are also available and can be accessed.

Let's assume that this MATLAB function is compiled into scoresorter.ctf and is now available for Java MATLAB Production Server client at URL: http://localhost:9910/scoresorter

Before defining the Java interface required by the MATLAB Production Server Java client, we need to define MATLAB structure, student, in Java. This will be achieved by a Java class Student with fields name, score and grade with appropriate types. It will also have public get and set functions to access these fields.

public class Student{

    private String name;
    private int score;
    private String grade;
    
    public Student(){
    }
    
    public Student(String name, int score, String grade){
        this.name = name;
        this.score = score;
        this.grade = grade;
    }

    public String getName(){
        return name;    
    }
    
    public void setName(String name){
        this.name = name;
    }
    
    public int getScore(){
        return score;    
    }
    
    public void setScore(int score){
        this.score = score;
    }
    
    public String getGrade(){
        return grade;    
    }
    
    public void setGrade(String grade){
        this.grade = grade;
    }
    
    public String toString(){
        return "Student:\n\tname : " + name + "\n\tscore : " + score + "\n\tgrade : " + grade;
    }   
}

NOTE: toString method is provided as a convenience method. It is not required for marshaling

Let's define Java interface StudentSorter that has method sortstudents and uses Student class for inputs and outputs. Student must be included in the MWStructureList annotation.

interface StudentSorter {
    @MWStructureList({Student.class})
    Student[] sortstudents(Student[] students) throws IOException, MATLABException;
}

The final Java application using MATLAB Production Server Java client will look like following:

import java.net.URL;
import java.io.IOException;
import com.mathworks.mps.client.MWClient;
import com.mathworks.mps.client.MWHttpClient;
import com.mathworks.mps.client.MATLABException;
import com.mathworks.mps.client.annotations.MWStructureList;

interface StudentSorter {
    @MWStructureList({Student.class})
    Student[] sortstudents(Student[] students) throws IOException, MATLABException;
}

public class ClientExample {

    public static void main(String[] args){
    
        MWClient client = new MWHttpClient();
        try{
            StudentSorter s = client.createProxy(new URL("http://localhost:9910/scoresorter"), StudentSorter.class );
            Student[] students = new Student[]{new Student("Toni Miller", 90, "A"), 
                                               new Student("Ed Plum",     80, "B+"),
                                               new Student("Mark Jones",  85, "A-")};
            Student[] sorted = s.sortstudents(students);
            System.out.println("Student list sorted in the ascending order of scores : ");
            for(Student st:sorted){
                System.out.println(st);
            }
        }catch(IOException ex){
            System.out.println(ex);
        }catch(MATLABException ex){
            System.out.println(ex);
        }finally{
            client.close();        
        }
    }
}

Define a MATLAB structure in Java

  1. Map Java Field Names to MATLAB Field Names
  2. Define input MATLAB structures
  3. Define output MATLAB structures

A structure in MATLAB is an ordered list of name-value pairs. It can be represented in Java as a class with fields with the same case-sensitive names. This class also needs to have public get and/or set methods for each of these fields as shown in the Student class above. Whether or not the class needs both get and set methods for the fields depends on whether it is being used as input or output or both.

Map Java Field Names to MATLAB Field Names

Java classes that represent MATLAB structures use the Java Beans Introspector (http://docs.oracle.com/javase/6/docs/api/java/beans/Introspector.html) to map properties to fields and its default naming conventions are used.

This means that by default its decapitalize (http://docs.oracle.com/javase/6/docs/api/java/beans/Introspector.html#decapitalize(java.lang.String) method is used. This maps the first letter of a Java field into a lower-case letter. By default, it is not possible to define a Java field which will map to a MATLAB field which starts with an upper case.

You can override this behaviour by implementing a BeanInfo class with a custom getPropertyDescriptors() method. For example:

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;

public class StudentBeanInfo extends SimpleBeanInfo
{
  @Override
  public PropertyDescriptor[] getPropertyDescriptors()
  {
      PropertyDescriptor[] props = new  PropertyDescriptor[3];
  
      try
      {
          // name uses default naming conventions so we do not need to explicitly specify the accessor names.
          props[0] = new PropertyDescriptor("name",MyStruct.class);
          // score uses default naming conventions so we do not need to explicitly specify the accessor names.
          props[1] = new PropertyDescriptor("score",MyStruct.class);
          // Grade uses a custom naming convention so we do need to explicitly specify the accessor names.
          props[1] = new PropertyDescriptor("Grade",MyStruct.class,"getGrade","setGrade");
          return props;
      }
      catch (IntrospectionException e)
      {
          e.printStackTrace();
      }  
          
      return null;
  }
}

Define input MATLAB structures

When Student is passed as an input to method sortstudents, only the get methods for its fields are used by the data marshaling algorithm. As a result, if a Java class is being defined for a MATLAB structure that is only used as an input value, the set methods are not required. Thus a shorter version of Student class that is only used to represent input values will look like following:

public class Student{

    private String name;
    private int score;
    private String grade;
    
    public Student(String name, int score, String grade){
        this.name = name;
        this.score = score;
        this.grade = grade;
    }

    public String getName(){
        return name;    
    }
    
    public int getScore(){
        return score;    
    }
    
    public String getGrade(){
        return grade;    
    }  
}

Define output MATLAB structures

When Student is used as output only, the data marshaling algorithm needs to create new instances of it using the structure received from MATLAB. This can be achieved using the set methods or @ConstructorProperties annotation provided by Java. get methods are not required for a Java class defining output only MATLAB structure

  1. Defining output MATLAB structure using set methods
  2. An output only Student class using set methods will look like following:

    public class Student{
    
        private String name;
        private int score;
        private String grade;    
        
        public void setName(String name){
            this.name = name;
        }
        
        public void setScore(int score){
            this.score = score;
        }    
        
        public void setGrade(String grade){
            this.grade = grade;
        }
    }
    

  3. Defining output MATLAB structure using @ConstructorProperties annotation
  4. An output only Student class using @ConstructorProperties will look like following:

    import java.beans.ConstructorProperties;
    
    public class Student{
    
        private String name;
        private int score;
        private String grade;    
        
        @ConstructorProperties({"name","score","grade"})
        public Student(String n, int s, String g){
            this.name = n;
            this.score = s;
            this.grade = g;
        }    
    }
    

  5. If both set methods and @ConstructorProperties annotation are provided, set methods get precedence over @ConstructorProperties annotation.

Defining input and output MATLAB structure

If Student is used as both an input and output, one needs to provide get methods to take care of marshaling to MATLAB. For marshaling from MATLAB, one can either use set methods or @ConstructorProperties annotation.

Logging

  1. Logging overview
  2. Using the embedded log4j engine
  3. Using an existing logging engine

Logging overview

Logging capability is available in the Java client, to record details such as HTTP request statuses, server URLs, and output data as needed. It is implemented using slf4j, so it can work with multiple logging engines, such as log4j, logback, or java.util.logging.

It can utilize the logging engine used in your project, from one of the slf4j supported engines, or load its own embedded engine if none is provided.

Using the embedded log4j engine

In the case where your project is not using any logging engine, and you just want to log the Java client activity, the Java client has an embedded log4j engine it can use once activated. To utilize the embedded engine, pass in a log4j configuration file to the Java application at startup. This is done by adding the file location URL to the log4j.configuration JVM property. If the file is in a file system, the URL will look like: file:/path/to/file/filename

The embedded engine is only loaded if no engine is provided.

A default log4j configuration file that outputs to standard output is provided at $MPS_INSTALL/client/java/log4j.properties

Example

    java -cp ./mps_client.jar:./Magic.jar -Dlog4j.configuration=file:/$MPS_INSTALL/client/java/log4j.properties Magic

Using an existing logging engine

If your project has an existing engine, the Java client can use that to log. Any engine supported by slf4j is supported. To utilize the existing engine, you just need to have it on the Java classpath and available to be loaded into your Java application. You can also load your own slf4j library, if you have need for a different version, by setting it up on the classpath.

For java.util.logging, you'll need to load and use the java.util.logging.Logger class in your Java application code before the com.mathworks.mps.client.MWHttpClient class is loaded.

For logback, add both the logback-classic and logback-core jar files onto the classpath.

Note: If you encounter version mismatch issues between your engine and slf4j, it is best to load your own slf4j-api.jar of the appropriate version by setting it on the Java classpath. This can especially occur if using later versions of logback.

Example

    #Using existing log4j engine
    java -cp ./log4j.jar:./mps_client.jar:./MyApplication.jar -Dlog4j.configuration=file:/path/to/log4j.properties MainClass

    #Using existing logback engine
    java -cp ./logback-classic.jar:./logback-core.jar:./mps_client.jar:./MyApplication.jar -Dlogback.configurationFile=/path/to/config.xml MainClass

    #Using existing slf4j API
    java -cp ./slf4j-api.jar:./mps_client.jar:./MyApplication.jar MainClass

    #Using existing logback engine with existing slf4j
    java -cp ./slf4j-api.jar:./logback-classic.jar:./logback-core.jar:./mps_client.jar:./MyApplication.jar -Dlogback.configurationFile=/path/to/config.xml MainClass

Refer to the respective 3rd party logging engine documentation for more information on how to configure the logging behavior.

Skip navigation links

Copyright 2010-2016 The MathWorks, Inc.