org.n52.matlab.control.MatlabProxy Maven / Gradle / Ivy
The newest version!
package org.n52.matlab.control;
/*
* Copyright (c) 2013, Joshua Kaplan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
* - Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* - Neither the name of matlabcontrol nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Communicates with a running MATLAB session. This class cannot be instantiated, it may be created with a
* {@link MatlabProxyFactory}. Interaction with MATLAB occurs as if calling {@code eval} and {@code feval} in the
* MATLAB Command Window.
* Communicating with MATLAB
* Methods which interact with MATLAB provide Java objects to the MATLAB environment and retrieve data from the MATLAB
* environment as Java objects. The following description of how the conversion between MATLAB and Java types occurs is
* based on tests conducted on versions of MATLAB R2007b through R2010b. Unless otherwise noted the behavior was
* identical on all tested versions. However, this behavior is not officially documented by The MathWorks and may change
* for future versions of MATLAB. (matlabcontrol is not compatible with versions prior to MATLAB R2007b.)
*
* MATLAB to Java
*
Numeric Classes and Logical Class
* All MATLAB numeric types whether they are a singular value or an array/matrix (of any dimension) are always converted
* into a one-dimensional Java {@code double[]}. For complex numbers, only the real component is converted. MATLAB
* {@code logical}s whether a singular value or an array/matrix (of any dimension) are always converted into a
* one-dimensional Java {@code boolean[]}. MATLAB arrays are stored in a linear manner, which has been
* documented by The MathWorks. It is in
* this linear manner that MATLAB arrays are returned to Java. (Sparse matrices are stored differently and are not sent
* to Java in an easy to use manner.)
*
Character Class
* MATLAB {@code char} singular values are returned as a Java {@code String}. One-dimensional MATLAB {@code char}
* arrays are returned as a Java {@code String}. Two-dimensional MATLAB {@code char} arrays are returned as a Java
* {@code String[]} with each row of the MATLAB array becoming a Java {@code String}. MATLAB {@code char} arrays of
* more than two dimensions have an inconsistent conversion to a Java type, although all observed conversions are
* either a Java {@code String} or an array of {@code String}s.
*
Cell and Struct Arrays
* MATLAB {@code cell} arrays and {@code struct} arrays are converted to a Java {@code Object[]}, often with arrays
* inside of them.
*
Function Handles and Non-Built-In Classes
* MATLAB {@code function_handle}s and all non-built-in classes (such as the {@code Map} class or user defined classes)
* are converted to an instance of {@code com.mathworks.jmi.types.MLArrayRef} that is not {@code Serializable} which
* prevents it from being transferred to a Java application running outside MATLAB (more information on this can be
* found in the exception section below).
*
* Java to MATLAB
*
Primitives
* It is not possible to directly send a Java primitive to the MATLAB environment because all methods which interact
* with MATLAB take in either {@code Object} or {@code Object[]} which results in the Java primitives becoming their
* auto-boxed class equivalent. (For example {@code int} becomes {@code Integer}.) Java primitive arrays, such as
* {@code int[]} are {@code Object}s and can therefore be sent to the MATLAB environment. They are converted as follows:
*
*
*
* Java Type MATLAB Type
* boolean[]
logical
array
* char[]
not supported*
* byte[]
int8
array
* short[]
int16
array
* int[]
int32
array
* long[]
not supported*
* float[]
single
array
* double[]
double
array
*
*
*
* *In MATLAB R2009b and higher, MATLAB will throw an exception. In MATLAB R2008b and earlier, MATLAB will segfault. The
* behavior in MATLAB R2009a is not known.
*
{@code Number}s
* Subclasses of {@link Number}, which includes all of the auto-boxed versions of Java primitive numeric types, become
* MATLAB {@code double}s.
*
{@code Boolean}s
* {@link Boolean}s are converted to MATLAB {@code logical}s.
*
{@code Character}s
* {@link Character}s are converted to MATLAB {@code char}s.
*
{@code String}s
* {@link String}s are converted to MATLAB {@code char} arrays.
*
Object Arrays
* Arrays of non-primitive types are converted to MATLAB {@code cell} arrays. The contents of the array are converted
* according to these same rules. Note that Java's multidimensional arrays are not an exception to this rule. For
* instance a {@code double[][]} is an array of {@code double[]}s and so MATLAB will create a cell array of MATLAB
* {@code double} arrays.
*
Other Classes
* Classes not otherwise mentioned remain as their original Java type. Objects contained within that class or instances
* of that class are not automatically converted by MATLAB, although when fields or methods are accessed in the MATLAB
* environment they may be converted into MATLAB types as
* documented by The MathWorks.
*
* Behavior of transferred data
* How Java objects sent to MATLAB or retrieved from MATLAB behave depends on several factors:
*
* Running outside MATLAB
* References to Java objects are copies. (There is one exception to this rule. Objects that are {@link java.rmi.Remote}
* will act as if they are not copies. This is because matlabcontrol communicates with MATLAB's Java Virtual Machine
* using
* Remote Method Invocation.)
*
* Running inside MATLAB
* References to Java objects in MATLAB that are returned to Java, reference the same object. When passing a reference
* to a Java object to MATLAB, if the Java object is not converted to a MATLAB type then it will reference the same
* object in the MATLAB environment.
*
* Help transferring data
* The {@link matlabcontrol.extensions.MatlabProxyLogger} exists to record what is being returned from MATLAB.
* The {@link matlabcontrol.extensions.MatlabTypeConverter} can convert between complicated Java and MATLAB types.
* Currently only MATLAB numeric arrays are supported.
* Exceptions
* Proxy methods that are relayed to MATLAB can throw {@link MatlabInvocationException}s. They will be thrown if:
*
* - The proxy has been disconnected via {@link #disconnect()}.
* - Attempting to do anything that would normally cause an error in MATLAB such as calling a function improperly or
* referencing a variable that does not exist.
* - The underlying Java MATLAB Interface (JMI) API does not support the operation. For instance, attempting to send
* a {@code long[]} to MATLAB. Due to the undocumented nature of this interface, it is not entirely known what
* actions may result in an exception.
*
Running outside MATLAB
* - Communication between this Java Virtual Machine and the one that MATLAB is running in is disrupted, most
* commonly due to closing MATLAB.
* - The class of an object to be sent or returned is not {@link java.io.Serializable} or
* {@link java.rmi.Remote}.1 Java primitives and arrays behave as if they were {@code Serializable}.
* - The class of an object to be returned from MATLAB is not defined in your application and no
* {@link SecurityManager} has been installed.2
* - The class of an object to sent to MATLAB is not defined in MATLAB and the class is not on your application's
* classpath.3
*
* 1This is a requirement of Remote Method Invocation, which matlabcontrol uses when running outside MATLAB.
*
* 2 This is due to Remote Method Invocation prohibiting loading classes defined in remote Java Virtual
* Machines unless a {@code SecurityManager} has been set. {@link PermissiveSecurityManager} exists to provide an easy
* way to set a security manager without further restricting permissions. Please consult
* {@code PermissiveSecurityManager}'s documentation for more information.
*
* 3 MATLAB sessions started by a {@code MatlabProxyFactory} are able to load all classes defined in your
* application's class path as specified by the {@code java.class.path} property. Some frameworks load classes without
* placing them on the class path, in that case matlabcontrol will not know about them and cannot tell MATLAB how to
* load them.
* Thread Safety
* This proxy is unconditionally thread-safe. Methods which interact with MATLAB may be called concurrently; however
* they will be completed sequentially on MATLAB's main thread. Calls to MATLAB from a given thread will be executed in
* the order they were invoked. No guarantees are made about the relative ordering of calls made from different threads.
* This proxy may not be the only thing interacting with MATLAB's main thread. One proxy running outside MATLAB and any
* number of proxies running inside MATLAB may be simultaneously connected. If MATLAB is not hidden from user
* interaction then a user may also be making use of MATLAB's main thread. This means that two sequential calls to the
* proxy from the same thread that interact with MATLAB will execute in that order, but interactions with MATLAB may
* occur between the two calls. In typical use it is unlikely this behavior will pose a problem. However, for some uses
* cases it may be necessary to guarantee that several interactions with MATLAB occur without interruption.
* Uninterrupted access to MATLAB's main thread may be obtained by use of
* {@link #invokeAndWait(MatlabProxy.MatlabThreadCallable) invokeAndWait(...)}.
* Threads
* When running outside MATLAB, the proxy makes use of multiple internally managed threads. When the proxy
* becomes disconnected from MATLAB it notifies its disconnection listeners and then terminates all threads it was using
* internally. A proxy may disconnect from MATLAB without exiting MATLAB by calling {@link #disconnect()}.
*
* @see MatlabProxyFactory#getProxy()
* @see MatlabProxyFactory#requestProxy(matlabcontrol.MatlabProxyFactory.RequestCallback)
* @since 4.0.0
* @author Joshua Kaplan
*/
public abstract class MatlabProxy implements MatlabOperations
{
/**
* Unique identifier for this proxy.
*/
private final Identifier _id;
/**
* Whether the session of MATLAB this proxy is connected to is an existing session.
*/
private final boolean _existingSession;
/**
* Listeners for disconnection.
*/
private final CopyOnWriteArrayList _listeners;
/**
* This constructor is package private to prevent subclasses from outside of this package.
*/
MatlabProxy(Identifier id, boolean existingSession)
{
_id = id;
_existingSession = existingSession;
_listeners = new CopyOnWriteArrayList();
}
/**
* Returns the unique identifier for this proxy.
*
* @return identifier
*/
public Identifier getIdentifier()
{
return _id;
}
/**
* Whether this proxy is connected to a session of MATLAB that was running previous to the request to create this
* proxy.
*
* @return if existing session
*/
public boolean isExistingSession()
{
return _existingSession;
}
/**
* Returns a brief description of this proxy. The exact details of this representation are unspecified and are
* subject to change.
*
* @return
*/
@Override
public String toString()
{
return "[" + this.getClass().getName() +
" identifier=" + this.getIdentifier() + "," +
" connected=" + this.isConnected() + "," +
" insideMatlab=" + this.isRunningInsideMatlab() + "," +
" existingSession=" + this.isExistingSession() +
"]";
}
/**
* Adds a disconnection listener that will be notified when this proxy becomes disconnected from MATLAB.
*
* @param listener
*/
public void addDisconnectionListener(DisconnectionListener listener)
{
_listeners.add(listener);
}
/**
* Removes a disconnection listener. It will no longer be notified.
*
* @param listener
*/
public void removeDisconnectionListener(DisconnectionListener listener)
{
_listeners.remove(listener);
}
/**
* Notifies the disconnection listeners this proxy has become disconnected.
*/
void notifyDisconnectionListeners()
{
for(DisconnectionListener listener : _listeners)
{
listener.proxyDisconnected(this);
}
}
/**
* Whether this proxy is running inside of MATLAB.
*
* @return
*/
public abstract boolean isRunningInsideMatlab();
/**
* Whether this proxy is connected to MATLAB.
*
* The most likely reasons for this method to return {@code false} are if the proxy has been disconnected via
* {@link #disconnect()} or MATLAB has been closed (when running outside MATLAB).
*
* @return if connected
*
* @see #disconnect()
* @see #exit()
*/
public abstract boolean isConnected();
/**
* Disconnects the proxy from MATLAB. MATLAB will not exit. After disconnecting, any method sent to MATLAB will
* throw an exception. A proxy cannot be reconnected. Returns {@code true} if the proxy is now disconnected,
* {@code false} otherwise.
*
* @return if disconnected
*
* @see #exit()
* @see #isConnected()
*/
public abstract boolean disconnect();
/**
* Exits MATLAB. Attempting to exit MATLAB with either a {@code eval} or {@code feval} command will cause MATLAB to
* hang indefinitely.
*
* @throws MatlabInvocationException
*
* @see #disconnect()
* @see #isConnected()
*/
public abstract void exit() throws MatlabInvocationException;
/**
* Runs the {@code callable} on MATLAB's main thread and waits for it to return its result. This method allows for
* uninterrupted access to MATLAB's main thread between two or more interactions with MATLAB.
*
* If running outside MATLAB the {@code callable} must be {@link java.io.Serializable}; it may not be
* {@link java.rmi.Remote}.
*
* @param
* @param callable
* @return result of the callable
* @throws MatlabInvocationException
*/
public abstract T invokeAndWait(MatlabThreadCallable callable) throws MatlabInvocationException;
/**
* Uninterrupted block of computation performed in MATLAB.
*
* @see MatlabProxy#invokeAndWait(matlabcontrol.MatlabProxy.MatlabThreadCallable)
* @param type of the data returned by the callable
*/
public static interface MatlabThreadCallable
{
/**
* Performs the computation in MATLAB. The {@code proxy} provided will invoke its methods directly on MATLAB's
* main thread without delay. This {@code proxy} should be used to interact with MATLAB, not a
* {@code MatlabProxy} (or any class delegating to it).
*
* @param proxy
* @return result of the computation
* @throws MatlabInvocationException
*/
public T call(MatlabThreadProxy proxy) throws MatlabInvocationException;
}
/**
* Operates on MATLAB's main thread without interruption. Currently this interface has only the methods defined in
* {@link MatlabOperations}, but this may change in future releases.
*
* An implementation of this interface is provided to
* {@link MatlabThreadCallable#call(MatlabProxy.MatlabThreadProxy)} so that the callable can interact with
* MATLAB. Implementations of this interface behave identically to a {@link MatlabProxy} running inside of MATLAB
* except that they are not thread-safe. They must be used solely on the thread that calls
* {@link MatlabThreadCallable#call(MatlabProxy.MatlabThreadProxy) call(...)}.
*
* WARNING: This interface is not intended to be implemented by users of matlabcontrol. Methods may be added
* to this interface, and these additions will not be considered breaking binary compatibility.
*/
public static interface MatlabThreadProxy extends MatlabOperations
{
}
/**
* Listens for a proxy's disconnection from MATLAB.
*
* @see MatlabProxy#addDisconnectionListener(matlabcontrol.MatlabProxy.DisconnectionListener)
* @see MatlabProxy#removeDisconnectionListener(matlabcontrol.MatlabProxy.DisconnectionListener)
* @since 4.0.0
* @author Joshua Kaplan
*/
public static interface DisconnectionListener
{
/**
* Called when the proxy becomes disconnected from MATLAB. The proxy passed in will always be the proxy that
* the listener was added to. The proxy is provided so that if desired a single implementation of this
* interface may easily be used for multiple proxies.
*
* @param proxy disconnected proxy
*/
public void proxyDisconnected(MatlabProxy proxy);
}
/**
* Uniquely identifies a proxy.
*
* Implementations of this interface are unconditionally thread-safe.
*
* WARNING: This interface is not intended to be implemented by users of matlabcontrol. Methods may be added
* to this interface, and these additions will not be considered breaking binary compatibility.
*
* @since 4.0.0
*
* @author Joshua Kaplan
*/
public static interface Identifier
{
/**
* Returns {@code true} if {@code other} is equal to this identifier, {@code false} otherwise.
*
* @param other
* @return
*/
@Override
public boolean equals(Object other);
/**
* Returns a hash code which conforms to the {@code hashCode} contract defined in {@link Object#hashCode()}.
*
* @return
*/
@Override
public int hashCode();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy