See: Description
| 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
|
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.
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.
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
Compile the MATLAB function into a deployable archive as follows:
magic with Deployable Archive as the target.mymagic, to this project.magic.ctf in the magic/for_redistribution_files_only directory.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:
You must have installed a MATLAB Runtime instance.
Configured MATLAB Production Server to use it by running mps-setup.
Before you can start a MATLAB Production Server instance, you must create it:
/tmp/mpsInst1.
Note : The directory /tmp/mpsInst1 must not exist before entering the following command.
% mps-new /tmp/mpsInst1 %
/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:
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
mps-status command:
% mps-status 'tmp/mpsInst1' STARTED license checked out
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>
In order to use the MATLAB function in a Java application, you need to define it using a Java interface. The interface must:
com.mathworks.mps.client.MATLABException : For reporting MATLAB errorsjava.io.IOException : For any transport error during client-server communicationThe 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.
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:
MWHttpClient to manage connections to MATLAB Production
Server instances.
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();
}
}
}
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.
MWHttpClientThe following JAR file is required to work with the MATLAB Production Server Java client API:
$MPS_INSTALL/client/java/mps_client.jar
The library is hosted in a Maven™ repository at https://mvnrepository.com/artifact/com.mathworks.prodserver/mps_java_client. To use the jar in your Maven project, include the following coordinates in the pom.xml file:
<!-- https://mvnrepository.com/artifact/com.mathworks.prodserver/mps_java_client -->
<dependency>
<groupId>com.mathworks.prodserver</groupId>
<artifactId>mps_java_client</artifactId>
<version>release_number</version>
<dependency>
MWHttpClientA 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();
}
}
You configure the client-server connection using an object that implements the MWHttpClientConfig interface. This interface defines the following properties:
Interruptable - determines if MATLAB functions can be invoked asynchronouslyTimeOutMs - determines the amount of time, in milliseconds, the client waits for a response before timing outMaxConnections - determines the maximum number of connections the client opens to fulfil multiple requestsResponseSizeLimit - 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.
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:
Interruptable = falseTimeOutMs = 120000MaxConnections = -1 specifying that the client will use as many connections as allowed by the systemResponseSizeLimit = 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.
To change one or more connection properties, use the following steps:
MWHttpClientDefaultConfig.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());
...
You implement a custom connection configuration by extending MWHttpClientDefaultConfig. MWHttpClientDefaultConfig with one getter method for each configuration property:
public int getMaxConnectionsPerAddress() - returns the value for the maximum number of connections a client can use to handle simultaneous requests.public long getTimeOutMs() - returns the number of milliseconds the client will wait for a response before generating an error.public boolean isInterruptible() - returns a boolean specifying if the MATLAB function can be invoked asynchronously. If this method returns false, getMaxConnectionsPerAddress() must return -1.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;
}
}
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:
MWHttpClient class.createComponentProxy() methods.invoke() methods.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:
MWInvokable createComponentProxy(URL archiveURL) - creates a proxy that uses standard MATLAB data typesMWInvokable createComponentProxy(URL archiveURL, MWMarshalingRules marshalingRules) - creates a proxy that uses structuresTo 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);
A reflection-based proxy has three methods that can be used to invoke functions on a server:
Object[] invoke(final String functionName, final int nargout, final Class<T> targetType, final Object... inputs) - invoke a function that returns nargout values<T> T invoke(final String functionName, final Class<T> targetType, final Object... inputs) - invoke a functions that returns a single valueinvokeVoid(final String functionName, final Object... inputs) - invoke a function that returns no valuesAll of the methods map to the MATLAB function as follows:
nargout and targetType, represent the function's return valuesThe 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.
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();
}
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();
}
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:
MWDefaultMarshalingRules to use a list of the classes being marshaled.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).
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:
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.
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();
}
}
}
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.
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.
MATLAB Production Server Java client application has to handle following checked exceptions:
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 :
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.
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.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:
https:// URL.MATLAB Production Server's Java client API also provides:
HostnameVerifierUse 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.
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.
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:
getSSLContext() - returns the SSLContext objectgetHostnameVerifier() - returns the HostnameVerifier object to use if HTTPS hostname verification failsgetServerAuthorizer() - returns the MWSSLServerAuthorizer object to perform server authorization based on the server's certificateThe 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:
getSSLContext() returns the default SSLContext object created by the JRE.getHostnameVerifier() returns a HostnameVerifier implementation that always returns false. If the HTTPS hostname verification fails, this will not override the HTTPS layer's decision.getServerAuthorizer() returns a MWSSLServerAuthorizer implementation that authorizes all MATLAB Production Server instances.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.
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:
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.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.
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.
varargin and varargoutMATLAB 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"});
nullWhen 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.
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.
The table below shows the data marshaling rules applied when data is passed from Java to MATLAB.
| Values passed to Java method | Input type received by MATLAB | Dimension of data in MATLAB |
|---|---|---|
| java.lang.Byte, byte | int8 | {1,1} |
| byte[] data | {1, data.length} | |
| java.lang.Short, short | int16 | {1,1} |
| short[] data | {1, data.length} | |
| java.lang.Integer, int | int32 | {1,1} |
| int[] data | {1, data.length} | |
| java.lang.Long, long | int64 | {1,1} |
| long[] data | {1, data.length} | |
| java.lang.Float, float | single | {1,1} |
| float[] data | {1, data.length} | |
| java.lang.Double, double | double | {1,1} |
| double[] data | {1, data.length} | |
| java.lang.Boolean, boolean | logical | {1,1} |
| boolean[] data | {1, data.length} | |
| java.lang.Character, char | char | {1,1} |
| char[] data | {1, data.length} | |
| java.lang.String data | {1, data.length()} | |
| java.lang.String[] data | cell | { 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[] data | MATLAB 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 | ||
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 returns | Dimension of data in MATLAB | Java 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]... |
Following data types in MATLAB are not supported for marshaling by MATLAB Production Server Java client:
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 types | Compatible Java types supported by coercion |
|---|---|
| uint8 | short, int, long, float, double |
| int8 | short, int, long, float, double |
| uint16 | int, long, float, double |
| int16 | int, long, float, double |
| uint32 | long, float, double |
| int32 | long, float, double |
| uint64 | float, double |
| int64 | float, double |
| single | double |
| double | byte, 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.
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:
double[10] (number_of_dimensions = 1)double[1][10] (number_of_dimensions = 2)double[1][10][1] (number_of_dimensions = 3)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 :
When Java method's return type has more number of dimensions than MATLAB response, MATLAB's dimensions array will be padded with 1's to match the required number of output dimensions in Java.
Here's another example for more clarity. Consider following MATLAB function:
function a = foo a = ones(2,3);If following Java method was used to represent this MATLAB function,
double[][][][] foo() throws MATLABException, IOException;the dimensions of data received in Java will be {2,3,1,1}.
This is the case when Java method's return type has fewer number of dimensions than data in MATLAB. This case is possible only when extra dimensions for MATLAB array have value of 1 only. To compute number of dimensions in Java, excess 1's are removed first from the right in the dimensions array and then from the left.
For more clarity, consider following MATLAB function:
function a = foo a = ones(1,2,1,1,3,1);If following Java method was used to represent this MATLAB function,
double[][] foo() throws MATLABException, IOException;the dimensions of data received in Java will be {2,3}.
Here are some more examples of dimension narrowing:
| MATLAB Array Dimensions | Declared Output Java Type | Output Java Dimensions |
|---|---|---|
| 1x1 | double | 0 |
| 2x1 | double[] | 2 |
| 1x2 | double[] | 2 |
| 2x3x1 | double[][] | 2x3 |
| 1x3x4 | double[][] | 3x4 |
| 1x3x4x1x1 | double[][][] | 1x3x4 |
| 1x3x1x1x2x1x4x1 | double[][][][] | 3x2x1x4 |
nullAn empty array in MATLAB has at least one 0 as one of the dimensions. Following are a few examples of MATLAB expressions which create empty data:
empty_double = []; empty_double_array = ones(0,0); empty_char_array = ''; empty_cell_array = {}; empty_int32_array = int32( ones(1,2,0,3) ); empty_struct = struct([]);
Empty MATLAB data will be returned to Java as null for all the cases except when the target type in Java
is a scalar primitive. In Java, primitive scalar types cannot be assigned value of null and a Java exception
will be thrown by MATLAB Production Server Java client.
Also, empty MATLAB char array will be marshaled into Java as an empty string only if the target type in Java is java.lang.String.
If the target type is not java.lang.String, it will be marshaled as null.
null -> MATLAB
When a null value is passed from Java to MATLAB, it will always be marshaled into [] i.e a
0x0 double array in MATLAB. This will be independent of the declared input type used in Java.
In Java, following methods can accept null as input value:
void foo(String input); void foo(double[] input); void foo(double[][] input); void foo(Double input);In MATLAB, for all the above methods,
null will be received as:
[] i.e. 0x0 double
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();
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.
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();
}
}
}
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.
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
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;
}
}
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;
}
}
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
set methodsAn 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;
}
}
@ConstructorProperties annotationAn 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;
}
}
set methods and @ConstructorProperties annotation are provided, set methods get
precedence over @ConstructorProperties annotation.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.
You can record details such as HTTP request statuses, server URLs, and output data in your Java® client application using the logging capability available in the MATLAB® Production Server™ Java client library. To offer logging options, the Java client library, mps_client.jar, ships the SLF4J library, slf4j-api.jar. You can use the native logging support that SLF4J provides or use any SLF4J-supported logging framework such as Apache™ Log4j, Logback, or the java.util.logging package.
To use the SLF4J library that is embedded in mps_client.jar for logging, include mps_client.jar in your Java class path. For details on setting the Java class path, see Oracle® documentation Setting the class path for UNIX® and Setting the class path for Windows®.+9
A default log4j configuration file that outputs to standard output is provided at $MPS_INSTALL/client/java/log4j.properties
java -cp ./mps_client.jar:./Magic.jar -Dlog4j.configuration=file:/$MPS_INSTALL/client/java/log4j.properties Magic
If you use an SLF4J-supported logging framework or an SLF4J library that is different from the one that is included in mps_client.jar, they must be present in front of the mps_client.jar on the Java class path.
The following examples assume that you have bundled your client application in MyApplication.jar and the entry point of the application is in MainClass.
To use the Log4j, Log4j 2 or Logback logging frameworks, pass the respective configuration files to your Java application at startup. To do so, add the URL of the file location, file:/path/to/file/filename, to the respective JVM property.
For example, the following UNIX command adds the Log4j JAR file and configuration file to the class path.
java -cp ./path/to/log4j.jar:./mps_client.jar -Dlog4j.configurationFile=file:/path/to/file MainClass
To use Logback, add both the logback-classic and logback-core JAR files onto the class path. The following example UNIX command shows how to add them and the configuration file to the class path.
java -cp ./path/to/logback-classic.jar:./path/to/logback-core.jar:./mps_client.jar -Dlogback.configurationFile=/path/to/config.xml MainClass
If you encounter version mismatch issues between your engine and the embedded SLF4J library, load your own slf4j-api.jar of the appropriate version by adding it to the Java class path.
The following example UNIX command shows how to load your own SLF4J library:
java -cp ./path/to/your/slf4j-api.jar:./mps_client.jar MainClass
The following example UNIX command shows how to load your own SLF4J library and Logback JAR files:
java -cp ./path/to/slf4j-api.jar:./path/to/logback-classic.jar:./path/to/logback-core.jar:./mps_client.jar -Dlogback.configurationFile=/path/to/config.xml MainClass
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.
Refer to the respective 3rd party logging engine documentation for more information on how to configure the logging behavior.
Copyright 2010-2022 The MathWorks, Inc.