aboutsummaryrefslogtreecommitdiffstats
path: root/lib/jinterface
diff options
context:
space:
mode:
Diffstat (limited to 'lib/jinterface')
-rw-r--r--lib/jinterface/doc/src/jinterface_users_guide.xml8
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java46
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java46
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java5
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java5
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java46
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java48
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java207
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java26
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java29
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java30
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java96
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java13
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java17
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java118
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServerSocketTransport.java68
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServerTransport.java46
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSocketTransport.java89
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSocketTransportFactory.java56
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpTransport.java49
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpTransportFactory.java124
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/java_files8
-rw-r--r--lib/jinterface/test/jinterface_SUITE.erl32
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/CoreMatchBind.java584
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Makefile.src4
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/TransportFactoryTest.java90
-rw-r--r--lib/jinterface/test/jitu.erl2
27 files changed, 1739 insertions, 153 deletions
diff --git a/lib/jinterface/doc/src/jinterface_users_guide.xml b/lib/jinterface/doc/src/jinterface_users_guide.xml
index 5dfe5c0c6d..238f90ce38 100644
--- a/lib/jinterface/doc/src/jinterface_users_guide.xml
+++ b/lib/jinterface/doc/src/jinterface_users_guide.xml
@@ -223,6 +223,14 @@ OtpNode node = new OtpNode("gurka"); </code>
</section>
<section>
+ <title>Transport Factory</title>
+ <p>All necessary connections are made using methods of
+ <seealso marker="java/com/ericsson/otp/erlang/OtpTransportFactory">OtpTransportFactory</seealso>
+ interface. Default OtpTransportFactory implementation is based on standard Socket class.
+ User may provide custom transport factory as needed. See java doc for details.</p>
+ </section>
+
+ <section>
<title>Sending and Receiving Messages</title>
<p>Messages sent with this package must be instances of
<seealso marker="java/com/ericsson/otp/erlang/OtpErlangObject">OtpErlangObject</seealso>
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
index 1b0fe3e2e6..ab8fa06c1b 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
@@ -20,7 +20,7 @@
package com.ericsson.otp.erlang;
import java.io.IOException;
-import java.net.Socket;
+import java.io.OutputStream;
import java.util.Random;
/**
@@ -84,7 +84,7 @@ public abstract class AbstractConnection extends Thread {
private volatile boolean done = false;
protected boolean connected = false; // connection status
- protected Socket socket; // communication channel
+ protected OtpTransport socket; // communication channel
protected OtpPeer peer; // who are we connected to
protected OtpLocalNode localNode; // this nodes id
String name; // local name of this connection
@@ -126,7 +126,7 @@ public abstract class AbstractConnection extends Thread {
* Accept an incoming connection from a remote node. Used by
* {@link OtpSelf#accept() OtpSelf.accept()} to create a connection based on
* data received when handshaking with the peer node, when the remote node
- * is the connection intitiator.
+ * is the connection initiator.
*
* @exception java.io.IOException
* if it was not possible to connect to the peer.
@@ -134,20 +134,17 @@ public abstract class AbstractConnection extends Thread {
* @exception OtpAuthException
* if handshake resulted in an authentication error
*/
- protected AbstractConnection(final OtpLocalNode self, final Socket s)
+ protected AbstractConnection(final OtpLocalNode self, final OtpTransport s)
throws IOException, OtpAuthException {
localNode = self;
- peer = new OtpPeer();
+ peer = new OtpPeer(self.transportFactory);
socket = s;
- socket.setTcpNoDelay(true);
-
traceLevel = defaultLevel;
setDaemon(true);
if (traceLevel >= handshakeThreshold) {
- System.out.println("<- ACCEPT FROM " + s.getInetAddress() + ":"
- + s.getPort());
+ System.out.println("<- ACCEPT FROM " + s);
}
// get his info
@@ -189,6 +186,8 @@ public abstract class AbstractConnection extends Thread {
// now get a connection between the two...
port = OtpEpmd.lookupPort(peer);
+ if (port == 0)
+ throw new IOException("No remote node found - cannot connect");
// now find highest common dist value
if (peer.proto != self.proto || self.distHigh < peer.distLow
@@ -523,7 +522,9 @@ public abstract class AbstractConnection extends Thread {
// received tick? send tock!
if (len == 0) {
synchronized (this) {
- socket.getOutputStream().write(tock);
+ OutputStream out = socket.getOutputStream();
+ out.write(tock);
+ out.flush();
}
}
@@ -837,8 +838,11 @@ public abstract class AbstractConnection extends Thread {
}
}
- header.writeTo(socket.getOutputStream());
- payload.writeTo(socket.getOutputStream());
+ // group flush op in favour of possible ssh-tunneled stream
+ OutputStream out = socket.getOutputStream();
+ header.writeTo(out);
+ payload.writeTo(out);
+ out.flush();
} catch (final IOException e) {
close();
throw e;
@@ -859,7 +863,7 @@ public abstract class AbstractConnection extends Thread {
+ e);
}
}
- header.writeTo(socket.getOutputStream());
+ header.writeToAndFlush(socket.getOutputStream());
} catch (final IOException e) {
close();
throw e;
@@ -913,7 +917,8 @@ public abstract class AbstractConnection extends Thread {
}
/* this method now throws exception if we don't get full read */
- protected int readSock(final Socket s, final byte[] b) throws IOException {
+ protected int readSock(final OtpTransport s, final byte[] b)
+ throws IOException {
int got = 0;
final int len = b.length;
int i;
@@ -980,8 +985,7 @@ public abstract class AbstractConnection extends Thread {
protected void doConnect(final int port) throws IOException,
OtpAuthException {
try {
- socket = new Socket(peer.host(), port);
- socket.setTcpNoDelay(true);
+ socket = peer.createTransport(peer.host(), port);
if (traceLevel >= handshakeThreshold) {
System.out.println("-> MD5 CONNECT TO " + peer.host() + ":"
@@ -1077,7 +1081,7 @@ public abstract class AbstractConnection extends Thread {
obuf.write4BE(aflags);
obuf.write(str.getBytes());
- obuf.writeTo(socket.getOutputStream());
+ obuf.writeToAndFlush(socket.getOutputStream());
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendName" + " flags="
@@ -1098,7 +1102,7 @@ public abstract class AbstractConnection extends Thread {
obuf.write4BE(challenge);
obuf.write(str.getBytes());
- obuf.writeTo(socket.getOutputStream());
+ obuf.writeToAndFlush(socket.getOutputStream());
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendChallenge" + " flags="
@@ -1232,7 +1236,7 @@ public abstract class AbstractConnection extends Thread {
obuf.write1(ChallengeReply);
obuf.write4BE(challenge);
obuf.write(digest);
- obuf.writeTo(socket.getOutputStream());
+ obuf.writeToAndFlush(socket.getOutputStream());
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendChallengeReply"
@@ -1294,7 +1298,7 @@ public abstract class AbstractConnection extends Thread {
obuf.write1(ChallengeAck);
obuf.write(digest);
- obuf.writeTo(socket.getOutputStream());
+ obuf.writeToAndFlush(socket.getOutputStream());
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendChallengeAck"
@@ -1341,7 +1345,7 @@ public abstract class AbstractConnection extends Thread {
obuf.write1(ChallengeStatus);
obuf.write(status.getBytes());
- obuf.writeTo(socket.getOutputStream());
+ obuf.writeToAndFlush(socket.getOutputStream());
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendStatus" + " status="
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
index 6f07d8171e..0a33984b31 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -64,13 +64,14 @@ import java.net.UnknownHostException;
* instead.
* </p>
*/
-public class AbstractNode {
+public class AbstractNode implements OtpTransportFactory {
static String localHost = null;
String node;
String host;
String alive;
String cookie;
static String defaultCookie = null;
+ final OtpTransportFactory transportFactory;
// Node types
static final int NTYPE_R6 = 110; // 'n' post-r5, all nodes
@@ -146,21 +147,41 @@ public class AbstractNode {
}
}
- protected AbstractNode() {
+ protected AbstractNode(final OtpTransportFactory transportFactory) {
+ this.transportFactory = transportFactory;
}
/**
- * Create a node with the given name and the default cookie.
+ * Create a node with the given name and default cookie and transport
+ * factory.
*/
protected AbstractNode(final String node) {
- this(node, defaultCookie);
+ this(node, defaultCookie, new OtpSocketTransportFactory());
}
/**
- * Create a node with the given name and cookie.
+ * Create a node with the given name, transport factory and the default
+ * cookie.
+ */
+ protected AbstractNode(final String node,
+ final OtpTransportFactory transportFactory) {
+ this(node, defaultCookie, transportFactory);
+ }
+
+ /**
+ * Create a node with the given name, cookie and default transport factory.
*/
protected AbstractNode(final String name, final String cookie) {
+ this(name, cookie, new OtpSocketTransportFactory());
+ }
+
+ /**
+ * Create a node with the given name, cookie and transport factory.
+ */
+ protected AbstractNode(final String name, final String cookie,
+ final OtpTransportFactory transportFactory) {
this.cookie = cookie;
+ this.transportFactory = transportFactory;
final int i = name.indexOf('@', 0);
if (i < 0) {
@@ -268,4 +289,19 @@ public class AbstractNode {
}
return home;
}
+
+ public OtpTransport createTransport(final String addr, final int port)
+ throws IOException {
+ return transportFactory.createTransport(addr, port);
+ }
+
+ public OtpTransport createTransport(final InetAddress addr, final int port)
+ throws IOException {
+ return transportFactory.createTransport(addr, port);
+ }
+
+ public OtpServerTransport createServerTransport(final int port)
+ throws IOException {
+ return transportFactory.createServerTransport(port);
+ }
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
index 2c9b7766bc..af0926f939 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
@@ -19,7 +19,6 @@
package com.ericsson.otp.erlang;
import java.io.IOException;
-import java.net.Socket;
/**
* Maintains a connection between a Java process and a remote Erlang, Java or C
@@ -63,8 +62,8 @@ public class OtpConnection extends AbstractConnection {
* error
*/
// package scope
- OtpConnection(final OtpSelf self, final Socket s) throws IOException,
- OtpAuthException {
+ OtpConnection(final OtpSelf self, final OtpTransport s)
+ throws IOException, OtpAuthException {
super(self, s);
this.self = self;
queue = new GenericQueue();
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java
index 4d80f61d52..b0e3e81fca 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java
@@ -19,7 +19,6 @@
package com.ericsson.otp.erlang;
import java.io.IOException;
-import java.net.Socket;
/**
* <p>
@@ -78,8 +77,8 @@ public class OtpCookedConnection extends AbstractConnection {
* error
*/
// package scope
- OtpCookedConnection(final OtpNode self, final Socket s) throws IOException,
- OtpAuthException {
+ OtpCookedConnection(final OtpNode self, final OtpTransport s)
+ throws IOException, OtpAuthException {
super(self, s);
this.self = self;
links = new Links(25);
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
index 796babee1b..6c7c8fe951 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
@@ -21,13 +21,12 @@ package com.ericsson.otp.erlang;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
-import java.net.Socket;
/**
* Provides methods for registering, unregistering and looking up nodes with the
* Erlang portmapper daemon (Epmd). For each registered node, Epmd maintains
* information about the port on which incoming connections are accepted, as
- * well as which versions of the Erlang communication protocolt the node
+ * well as which versions of the Erlang communication protocol the node
* supports.
*
* <p>
@@ -136,7 +135,7 @@ public class OtpEpmd {
*/
public static boolean publishPort(final OtpLocalNode node)
throws IOException {
- Socket s = null;
+ OtpTransport s = null;
s = r4_publish(node);
@@ -156,16 +155,16 @@ public class OtpEpmd {
* This method does not report any failures.
*/
public static void unPublishPort(final OtpLocalNode node) {
- Socket s = null;
+ OtpTransport s = null;
try {
- s = new Socket((String) null, EpmdPort.get());
+ s = node.createTransport((String) null, EpmdPort.get());
@SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
obuf.write2BE(node.alive().length() + 1);
obuf.write1(stopReq);
obuf.writeN(node.alive().getBytes());
- obuf.writeTo(s.getOutputStream());
+ obuf.writeToAndFlush(s.getOutputStream());
// don't even wait for a response (is there one?)
if (traceLevel >= traceThreshold) {
System.out.println("-> UNPUBLISH " + node + " port="
@@ -187,12 +186,12 @@ public class OtpEpmd {
private static int r4_lookupPort(final AbstractNode node)
throws IOException {
int port = 0;
- Socket s = null;
+ OtpTransport s = null;
try {
@SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
- s = new Socket(node.host(), EpmdPort.get());
+ s = node.createTransport(node.host(), EpmdPort.get());
// build and send epmd request
// length[2], tag[1], alivename[n] (length = n+1)
@@ -201,7 +200,7 @@ public class OtpEpmd {
obuf.writeN(node.alive().getBytes());
// send request
- obuf.writeTo(s.getOutputStream());
+ obuf.writeToAndFlush(s.getOutputStream());
if (traceLevel >= traceThreshold) {
System.out.println("-> LOOKUP (r4) " + node);
@@ -242,7 +241,7 @@ public class OtpEpmd {
System.out.println("<- (no response)");
}
throw new IOException("Nameserver not responding on " + node.host()
- + " when looking up " + node.alive());
+ + " when looking up " + node.alive(), e);
} catch (final OtpErlangDecodeException e) {
if (traceLevel >= traceThreshold) {
System.out.println("<- (invalid response)");
@@ -276,14 +275,14 @@ public class OtpEpmd {
* fatal. If we manage to successfully communicate with an r4 epmd, we
* return either the socket, or null, depending on the result.
*/
- private static Socket r4_publish(final OtpLocalNode node)
+ private static OtpTransport r4_publish(final OtpLocalNode node)
throws IOException {
- Socket s = null;
+ OtpTransport s = null;
try {
@SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
- s = new Socket((String) null, EpmdPort.get());
+ s = node.createTransport((String) null, EpmdPort.get());
obuf.write2BE(node.alive().length() + 13);
@@ -301,7 +300,7 @@ public class OtpEpmd {
obuf.write2BE(0); // No extra
// send request
- obuf.writeTo(s.getOutputStream());
+ obuf.writeToAndFlush(s.getOutputStream());
if (traceLevel >= traceThreshold) {
System.out.println("-> PUBLISH (r4) " + node + " port="
@@ -356,23 +355,34 @@ public class OtpEpmd {
}
public static String[] lookupNames() throws IOException {
- return lookupNames(InetAddress.getByName(null));
+ return lookupNames(InetAddress.getByName(null),
+ new OtpSocketTransportFactory());
+ }
+
+ public static String[] lookupNames(
+ final OtpTransportFactory transportFactory) throws IOException {
+ return lookupNames(InetAddress.getByName(null), transportFactory);
}
public static String[] lookupNames(final InetAddress address)
throws IOException {
- Socket s = null;
+ return lookupNames(address, new OtpSocketTransportFactory());
+ }
+
+ public static String[] lookupNames(final InetAddress address,
+ final OtpTransportFactory transportFactory) throws IOException {
+ OtpTransport s = null;
try {
@SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
try {
- s = new Socket(address, EpmdPort.get());
+ s = transportFactory.createTransport(address, EpmdPort.get());
obuf.write2BE(1);
obuf.write1(names4req);
// send request
- obuf.writeTo(s.getOutputStream());
+ obuf.writeToAndFlush(s.getOutputStream());
if (traceLevel >= traceThreshold) {
System.out.println("-> NAMES (r4) ");
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java
index 990e50ddcd..268261ec10 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java
@@ -297,6 +297,54 @@ public class OtpErlangList extends OtpErlangObject implements
return getLastTail().equals(l.getLastTail());
}
+ @Override
+ public <T> boolean match(final OtpErlangObject term, final T bindings) {
+ if (!(term instanceof OtpErlangList)) {
+ return false;
+ }
+ final OtpErlangList that = (OtpErlangList) term;
+
+ final int thisArity = this.arity();
+ final int thatArity = that.arity();
+ final OtpErlangObject thisTail = this.getLastTail();
+ final OtpErlangObject thatTail = that.getLastTail();
+
+ if (thisTail == null) {
+ if (thisArity != thatArity || thatTail != null) {
+ return false;
+ }
+ } else {
+ if (thisArity > thatArity) {
+ return false;
+ }
+ }
+ for (int i = 0; i < thisArity; i++) {
+ if (!elementAt(i).match(that.elementAt(i), bindings)) {
+ return false;
+ }
+ }
+ if (thisTail == null) {
+ return true;
+ }
+ return thisTail.match(that.getNthTail(thisArity), bindings);
+ }
+
+ @Override
+ public <T> OtpErlangObject bind(final T binds) throws OtpErlangException {
+ final OtpErlangList list = (OtpErlangList) this.clone();
+
+ final int a = list.elems.length;
+ for (int i = 0; i < a; i++) {
+ list.elems[i] = list.elems[i].bind(binds);
+ }
+
+ if (list.lastTail != null) {
+ list.lastTail = list.lastTail.bind(binds);
+ }
+
+ return list;
+ }
+
public OtpErlangObject getLastTail() {
return lastTail;
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
index 7f2621923a..a8cd9d5392 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
@@ -18,6 +18,11 @@
*/
package com.ericsson.otp.erlang;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
/**
* Provides a Java representation of Erlang maps. Maps are created from one or
* more arbitrary Erlang terms.
@@ -31,10 +36,14 @@ public class OtpErlangMap extends OtpErlangObject {
// don't change this!
private static final long serialVersionUID = -6410770117696198497L;
- private static final OtpErlangObject[] NO_ELEMENTS = new OtpErlangObject[0];
+ private HashMap<OtpErlangObject, OtpErlangObject> map;
- private OtpErlangObject[] keys = NO_ELEMENTS;
- private OtpErlangObject[] values = NO_ELEMENTS;
+ /**
+ * Create an empty map.
+ */
+ public OtpErlangMap() {
+ map = new HashMap<OtpErlangObject, OtpErlangObject>();
+ }
/**
* Create a map from an array of keys and an array of values.
@@ -82,30 +91,20 @@ public class OtpErlangMap extends OtpErlangObject {
} else if (kcount != vcount) {
throw new java.lang.IllegalArgumentException(
"Map keys and values must have same arity");
- } else if (vcount < 1) {
- this.keys = NO_ELEMENTS;
- this.values = NO_ELEMENTS;
- } else {
- this.keys = new OtpErlangObject[vcount];
- for (int i = 0; i < vcount; i++) {
- if (keys[kstart + i] != null) {
- this.keys[i] = keys[kstart + i];
- } else {
- throw new java.lang.IllegalArgumentException(
- "Map key cannot be null (element" + (kstart + i)
- + ")");
- }
+ }
+ map = new HashMap<OtpErlangObject, OtpErlangObject>(vcount);
+ OtpErlangObject key, val;
+ for (int i = 0; i < vcount; i++) {
+ if ((key = keys[kstart + i]) == null) {
+ throw new java.lang.IllegalArgumentException(
+ "Map key cannot be null (element" + (kstart + i) + ")");
}
- this.values = new OtpErlangObject[vcount];
- for (int i = 0; i < vcount; i++) {
- if (values[vstart + i] != null) {
- this.values[i] = values[vstart + i];
- } else {
- throw new java.lang.IllegalArgumentException(
- "Map value cannot be null (element" + (vstart + i)
- + ")");
- }
+ if ((val = values[vstart + i]) == null) {
+ throw new java.lang.IllegalArgumentException(
+ "Map value cannot be null (element" + (vstart + i)
+ + ")");
}
+ put(key, val);
}
}
@@ -125,16 +124,15 @@ public class OtpErlangMap extends OtpErlangObject {
final int arity = buf.read_map_head();
if (arity > 0) {
- keys = new OtpErlangObject[arity];
- values = new OtpErlangObject[arity];
-
+ map = new HashMap<OtpErlangObject, OtpErlangObject>(arity);
for (int i = 0; i < arity; i++) {
- keys[i] = buf.read_any();
- values[i] = buf.read_any();
+ OtpErlangObject key, val;
+ key = buf.read_any();
+ val = buf.read_any();
+ put(key, val);
}
} else {
- keys = NO_ELEMENTS;
- values = NO_ELEMENTS;
+ map = new HashMap<OtpErlangObject, OtpErlangObject>();
}
}
@@ -144,7 +142,33 @@ public class OtpErlangMap extends OtpErlangObject {
* @return the number of elements contained in the map.
*/
public int arity() {
- return keys.length;
+ return map.size();
+ }
+
+ /**
+ * Put value corresponding to key into the map. For detailed behavior
+ * description see {@link Map#put(Object, Object)}.
+ *
+ * @param key
+ * key to associate value with
+ * @param value
+ * value to associate with key
+ * @return previous value associated with key or null
+ */
+ public OtpErlangObject put(final OtpErlangObject key,
+ final OtpErlangObject value) {
+ return map.put(key, value);
+ }
+
+ /**
+ * removes mapping for the key if present.
+ *
+ * @param key
+ * key for which mapping is to be remove
+ * @return value associated with key or null
+ */
+ public OtpErlangObject remove(final OtpErlangObject key) {
+ return map.remove(key);
}
/**
@@ -156,15 +180,7 @@ public class OtpErlangMap extends OtpErlangObject {
* @return the requested value, of null if key is not a valid key.
*/
public OtpErlangObject get(final OtpErlangObject key) {
- if (key == null) {
- return null;
- }
- for (int i = 0; i < keys.length; i++) {
- if (key.equals(keys[i])) {
- return values[i];
- }
- }
- return null;
+ return map.get(key);
}
/**
@@ -173,9 +189,7 @@ public class OtpErlangMap extends OtpErlangObject {
* @return an array containing all of the map's keys.
*/
public OtpErlangObject[] keys() {
- final OtpErlangObject[] res = new OtpErlangObject[arity()];
- System.arraycopy(keys, 0, res, 0, res.length);
- return res;
+ return map.keySet().toArray(new OtpErlangObject[arity()]);
}
/**
@@ -184,9 +198,16 @@ public class OtpErlangMap extends OtpErlangObject {
* @return an array containing all of the map's values.
*/
public OtpErlangObject[] values() {
- final OtpErlangObject[] res = new OtpErlangObject[arity()];
- System.arraycopy(values, 0, res, 0, res.length);
- return res;
+ return map.values().toArray(new OtpErlangObject[arity()]);
+ }
+
+ /**
+ * make Set view of the map key-value pairs
+ *
+ * @return a set containing key-value pairs
+ */
+ public Set<Entry<OtpErlangObject, OtpErlangObject>> entrySet() {
+ return map.entrySet();
}
/**
@@ -196,19 +217,20 @@ public class OtpErlangMap extends OtpErlangObject {
*/
@Override
public String toString() {
- int i;
final StringBuffer s = new StringBuffer();
- final int arity = values.length;
s.append("#{");
- for (i = 0; i < arity; i++) {
- if (i > 0) {
+ boolean first = true;
+ for (final Map.Entry<OtpErlangObject, OtpErlangObject> e : entrySet()) {
+ if (first) {
+ first = false;
+ } else {
s.append(",");
}
- s.append(keys[i].toString());
+ s.append(e.getKey().toString());
s.append(" => ");
- s.append(values[i].toString());
+ s.append(e.getValue().toString());
}
s.append("}");
@@ -224,13 +246,13 @@ public class OtpErlangMap extends OtpErlangObject {
*/
@Override
public void encode(final OtpOutputStream buf) {
- final int arity = values.length;
+ final int arity = arity();
buf.write_map_head(arity);
- for (int i = 0; i < arity; i++) {
- buf.write_any(keys[i]);
- buf.write_any(values[i]);
+ for (final Map.Entry<OtpErlangObject, OtpErlangObject> e : entrySet()) {
+ buf.write_any(e.getKey());
+ buf.write_any(e.getValue());
}
}
@@ -256,15 +278,46 @@ public class OtpErlangMap extends OtpErlangObject {
if (a != t.arity()) {
return false;
}
+ if (a == 0) {
+ return true;
+ }
- for (int i = 0; i < a; i++) {
- if (!keys[i].equals(t.keys[i])) {
- return false; // early exit
+ OtpErlangObject key, val;
+ for (final Map.Entry<OtpErlangObject, OtpErlangObject> e : entrySet()) {
+ key = e.getKey();
+ val = e.getValue();
+ final OtpErlangObject v = t.get(key);
+ if (v == null || !val.equals(v)) {
+ return false;
}
}
- for (int i = 0; i < a; i++) {
- if (!values[i].equals(t.values[i])) {
- return false; // early exit
+
+ return true;
+ }
+
+ @Override
+ public <T> boolean match(final OtpErlangObject term, final T binds) {
+ if (!(term instanceof OtpErlangMap)) {
+ return false;
+ }
+
+ final OtpErlangMap t = (OtpErlangMap) term;
+ final int a = arity();
+
+ if (a > t.arity()) {
+ return false;
+ }
+ if (a == 0) {
+ return true;
+ }
+
+ OtpErlangObject key, val;
+ for (final Map.Entry<OtpErlangObject, OtpErlangObject> e : entrySet()) {
+ key = e.getKey();
+ val = e.getValue();
+ final OtpErlangObject v = t.get(key);
+ if (v == null || !val.match(v, binds)) {
+ return false;
}
}
@@ -272,23 +325,31 @@ public class OtpErlangMap extends OtpErlangObject {
}
@Override
+ public <T> OtpErlangObject bind(final T binds) throws OtpErlangException {
+ final OtpErlangMap ret = new OtpErlangMap();
+
+ OtpErlangObject key, val;
+ for (final Map.Entry<OtpErlangObject, OtpErlangObject> e : entrySet()) {
+ key = e.getKey();
+ val = e.getValue();
+ ret.put(key, val.bind(binds));
+ }
+
+ return ret;
+ }
+
+ @Override
protected int doHashCode() {
final OtpErlangObject.Hash hash = new OtpErlangObject.Hash(9);
- final int a = arity();
- hash.combine(a);
- for (int i = 0; i < a; i++) {
- hash.combine(keys[i].hashCode());
- }
- for (int i = 0; i < a; i++) {
- hash.combine(values[i].hashCode());
- }
+ hash.combine(map.hashCode());
return hash.valueOf();
}
@Override
+ @SuppressWarnings("unchecked")
public Object clone() {
final OtpErlangMap newMap = (OtpErlangMap) super.clone();
- newMap.values = values.clone();
+ newMap.map = (HashMap<OtpErlangObject, OtpErlangObject>) map.clone();
return newMap;
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
index 7ab160bcdd..9339d3749b 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
@@ -80,6 +80,32 @@ public abstract class OtpErlangObject implements Serializable, Cloneable {
@Override
public abstract boolean equals(Object o);
+ /**
+ * Perform match operation against given term.
+ *
+ * @param term
+ * the object to match
+ * @param binds
+ * variable bindings
+ * @return true if match succeeded
+ */
+ public <T> boolean match(final OtpErlangObject term, final T binds) {
+ return equals(term);
+ }
+
+ /**
+ * Make new Erlang term replacing variables with the respective values from
+ * bindings argument(s).
+ *
+ * @param binds
+ * variable bindings
+ * @return new term
+ * @throws OtpErlangException
+ */
+ public <T> OtpErlangObject bind(final T binds) throws OtpErlangException {
+ return this;
+ }
+
@Override
public int hashCode() {
if (hashCodeValue == 0) {
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
index af2559e62e..ef0a453de1 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
@@ -236,6 +236,35 @@ public class OtpErlangTuple extends OtpErlangObject {
}
@Override
+ public <T> boolean match(final OtpErlangObject term, final T bindings) {
+ if (!(term instanceof OtpErlangTuple)) {
+ return false;
+ }
+ final OtpErlangTuple t = (OtpErlangTuple) term;
+ final int a = elems.length;
+ if (a != t.elems.length) {
+ return false;
+ }
+ for (int i = 0; i < a; i++) {
+ if (!elems[i].match(t.elems[i], bindings)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public <T> OtpErlangObject bind(final T binds) throws OtpErlangException {
+ final OtpErlangTuple tuple = (OtpErlangTuple) this.clone();
+ final int a = tuple.elems.length;
+ for (int i = 0; i < a; i++) {
+ final OtpErlangObject e = tuple.elems[i];
+ tuple.elems[i] = e.bind(binds);
+ }
+ return tuple;
+ }
+
+ @Override
protected int doHashCode() {
final OtpErlangObject.Hash hash = new OtpErlangObject.Hash(9);
final int a = arity();
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java
index b996ba6f6c..dd1d299297 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java
@@ -29,12 +29,7 @@ public class OtpLocalNode extends AbstractNode {
private int refId[];
protected int port;
- protected java.net.Socket epmd;
-
- protected OtpLocalNode() {
- super();
- init();
- }
+ protected OtpTransport epmd;
/**
* Create a node with the given name and the default cookie.
@@ -45,6 +40,16 @@ public class OtpLocalNode extends AbstractNode {
}
/**
+ * Create a node with the given name, transport factory and the default
+ * cookie.
+ */
+ protected OtpLocalNode(final String node,
+ final OtpTransportFactory transportFactory) {
+ super(node, transportFactory);
+ init();
+ }
+
+ /**
* Create a node with the given name and cookie.
*/
protected OtpLocalNode(final String node, final String cookie) {
@@ -52,6 +57,15 @@ public class OtpLocalNode extends AbstractNode {
init();
}
+ /**
+ * Create a node with the given name, cookie and transport factory.
+ */
+ protected OtpLocalNode(final String node, final String cookie,
+ final OtpTransportFactory transportFactory) {
+ super(node, cookie, transportFactory);
+ init();
+ }
+
private void init() {
serial = 0;
pidCount = 1;
@@ -77,7 +91,7 @@ public class OtpLocalNode extends AbstractNode {
* @param s
* The socket connecting this node to Epmd.
*/
- protected void setEpmd(final java.net.Socket s) {
+ protected void setEpmd(final OtpTransport s) {
epmd = s;
}
@@ -86,7 +100,7 @@ public class OtpLocalNode extends AbstractNode {
*
* @return The socket connecting this node to Epmd.
*/
- protected java.net.Socket getEpmd() {
+ protected OtpTransport getEpmd() {
return epmd;
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java
index d5edd135cf..7512d34c21 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java
@@ -20,8 +20,6 @@ package com.ericsson.otp.erlang;
import java.io.IOException;
import java.lang.ref.WeakReference;
-import java.net.ServerSocket;
-import java.net.Socket;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
@@ -97,7 +95,39 @@ public class OtpNode extends OtpLocalNode {
*
*/
public OtpNode(final String node) throws IOException {
- this(node, defaultCookie, 0);
+ super(node);
+
+ init(0);
+ }
+
+ /**
+ * <p>
+ * Create a node using the default cookie. The default cookie is found by
+ * reading the first line of the .erlang.cookie file in the user's home
+ * directory. The home directory is obtained from the System property
+ * "user.home".
+ * </p>
+ *
+ * <p>
+ * If the file does not exist, an empty string is used. This method makes no
+ * attempt to create the file.
+ * </p>
+ *
+ * @param node
+ * the name of this node.
+ *
+ * @param transportFactory
+ * the transport factory to use when creating connections.
+ *
+ * @exception IOException
+ * if communication could not be initialized.
+ *
+ */
+ public OtpNode(final String node,
+ final OtpTransportFactory transportFactory) throws IOException {
+ super(node, transportFactory);
+
+ init(0);
}
/**
@@ -128,6 +158,28 @@ public class OtpNode extends OtpLocalNode {
* the authorization cookie that will be used by this node when
* it communicates with other nodes.
*
+ * @param transportFactory
+ * the transport factory to use when creating connections.
+ *
+ * @exception IOException
+ * if communication could not be initialized.
+ *
+ */
+ public OtpNode(final String node, final String cookie,
+ final OtpTransportFactory transportFactory) throws IOException {
+ this(node, cookie, 0, transportFactory);
+ }
+
+ /**
+ * Create a node.
+ *
+ * @param node
+ * the name of this node.
+ *
+ * @param cookie
+ * the authorization cookie that will be used by this node when
+ * it communicates with other nodes.
+ *
* @param port
* the port number you wish to use for incoming connections.
* Specifying 0 lets the system choose an available port.
@@ -143,6 +195,34 @@ public class OtpNode extends OtpLocalNode {
init(port);
}
+ /**
+ * Create a node.
+ *
+ * @param node
+ * the name of this node.
+ *
+ * @param cookie
+ * the authorization cookie that will be used by this node when
+ * it communicates with other nodes.
+ *
+ * @param port
+ * the port number you wish to use for incoming connections.
+ * Specifying 0 lets the system choose an available port.
+ *
+ * @param transportFactory
+ * the transport factory to use when creating connections.
+ *
+ * @exception IOException
+ * if communication could not be initialized.
+ *
+ */
+ public OtpNode(final String node, final String cookie, final int port,
+ final OtpTransportFactory transportFactory) throws IOException {
+ super(node, cookie, transportFactory);
+
+ init(port);
+ }
+
private synchronized void init(final int aport) throws IOException {
if (!initDone) {
connections = new Hashtable<String, OtpCookedConnection>(17,
@@ -681,12 +761,12 @@ public class OtpNode extends OtpLocalNode {
* this thread simply listens for incoming connections
*/
public class Acceptor extends Thread {
- private final ServerSocket sock;
+ private final OtpServerTransport sock;
private final int acceptorPort;
private volatile boolean done = false;
Acceptor(final int port) throws IOException {
- sock = new ServerSocket(port);
+ sock = createServerTransport(port);
acceptorPort = sock.getLocalPort();
OtpNode.this.port = acceptorPort;
@@ -720,7 +800,7 @@ public class OtpNode extends OtpLocalNode {
localStatus(node, false, null);
}
- private void closeSock(final ServerSocket s) {
+ private void closeSock(final OtpServerTransport s) {
try {
if (s != null) {
s.close();
@@ -729,7 +809,7 @@ public class OtpNode extends OtpLocalNode {
}
}
- private void closeSock(final Socket s) {
+ private void closeSock(final OtpTransport s) {
try {
if (s != null) {
s.close();
@@ -744,7 +824,7 @@ public class OtpNode extends OtpLocalNode {
@Override
public void run() {
- Socket newsock = null;
+ OtpTransport newsock = null;
OtpCookedConnection conn = null;
localStatus(node, true, null);
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
index b8493b57ff..2ec583ff5c 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -21,6 +21,7 @@ package com.ericsson.otp.erlang;
// import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -202,6 +203,16 @@ public class OtpOutputStream extends ByteArrayOutputStream {
super.count += len;
}
+ @Override
+ public synchronized void writeTo(OutputStream out) throws IOException {
+ super.writeTo(out);
+ }
+
+ public synchronized void writeToAndFlush(OutputStream out) throws IOException {
+ super.writeTo(out);
+ out.flush();
+ }
+
/**
* Write the low byte of a value to the stream.
*
@@ -887,7 +898,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
if (oos.size() < 5) {
// fast path for small terms
try {
- oos.writeTo(this);
+ oos.writeToAndFlush(this);
// if the term is written as a compressed term, the output
// stream is closed, so we do this here, too
close();
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java
index 2c79c04247..cb09b40f47 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java
@@ -32,8 +32,8 @@ public class OtpPeer extends AbstractNode {
* common protocol version we both support
*/
- OtpPeer() {
- super();
+ OtpPeer(final OtpTransportFactory transportFactory) {
+ super(transportFactory);
}
/**
@@ -47,6 +47,19 @@ public class OtpPeer extends AbstractNode {
}
/**
+ * Create a peer node with custom transport factory.
+ *
+ * @param node
+ * the name of the node.
+ * @param transportFactory
+ * custom transport factory
+ */
+ public OtpPeer(final String node, final OtpTransportFactory
+ transportFactory) {
+ super(node, transportFactory);
+ }
+
+ /**
* Create a connection to a remote node.
*
* @param self
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java
index 166dac5701..5b9d13ad81 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java
@@ -19,8 +19,6 @@
package com.ericsson.otp.erlang;
import java.io.IOException;
-import java.net.ServerSocket;
-import java.net.Socket;
import java.net.UnknownHostException;
/**
@@ -48,7 +46,7 @@ import java.net.UnknownHostException;
*
*/
public class OtpSelf extends OtpLocalNode {
- private final ServerSocket sock;
+ private final OtpServerTransport sock;
private final OtpErlangPid pid;
/**
@@ -67,12 +65,43 @@ public class OtpSelf extends OtpLocalNode {
* @param node
* the name of this node.
*
+ * @exception IOException
+ * in case of server transport failure
+ *
*/
public OtpSelf(final String node) throws IOException {
this(node, defaultCookie, 0);
}
/**
+ * <p>
+ * Create a self node using the default cookie and custom transport factory.
+ * The default cookie is found by reading the first line of the
+ * .erlang.cookie file in the user's home directory. The home directory is
+ * obtained from the System property "user.home".
+ * </p>
+ *
+ * <p>
+ * If the file does not exist, an empty string is used. This method makes no
+ * attempt to create the file.
+ * </p>
+ *
+ * @param node
+ * the name of this node.
+ *
+ * @param transportFactory
+ * the transport factory to use when creating connections.
+ *
+ * @exception IOException
+ * in case of server transport failure
+ *
+ */
+ public OtpSelf(final String node,
+ final OtpTransportFactory transportFactory) throws IOException {
+ this(node, defaultCookie, 0, transportFactory);
+ }
+
+ /**
* Create a self node.
*
* @param node
@@ -81,16 +110,95 @@ public class OtpSelf extends OtpLocalNode {
* @param cookie
* the authorization cookie that will be used by this node when
* it communicates with other nodes.
+ *
+ * @exception IOException
+ * in case of server transport failure
*/
public OtpSelf(final String node, final String cookie) throws IOException {
this(node, cookie, 0);
}
+ /**
+ * Create a self node.
+ *
+ * @param node
+ * the name of this node.
+ *
+ * @param cookie
+ * the authorization cookie that will be used by this node when
+ * it communicates with other nodes.
+ *
+ * @param transportFactory
+ * the transport factory to use when creating connections.
+ *
+ * @exception IOException
+ * in case of server transport failure
+ */
+ public OtpSelf(final String node, final String cookie,
+ final OtpTransportFactory transportFactory) throws IOException {
+ this(node, cookie, 0, transportFactory);
+ }
+
+ /**
+ * Create a self node.
+ *
+ * @param node
+ * the name of this node.
+ *
+ * @param cookie
+ * the authorization cookie that will be used by this node when
+ * it communicates with other nodes.
+ *
+ * @param port
+ * the port number you wish to use for incoming connections.
+ * Specifying 0 lets the system choose an available port.
+ *
+ * @param transportFactory
+ * the transport factory to use when creating connections.
+ *
+ * @exception IOException
+ * in case of server transport failure
+ */
public OtpSelf(final String node, final String cookie, final int port)
throws IOException {
super(node, cookie);
- sock = new ServerSocket(port);
+ sock = createServerTransport(port);
+
+ if (port != 0) {
+ this.port = port;
+ } else {
+ this.port = sock.getLocalPort();
+ }
+
+ pid = createPid();
+ }
+
+ /**
+ * Create a self node.
+ *
+ * @param node
+ * the name of this node.
+ *
+ * @param cookie
+ * the authorization cookie that will be used by this node when
+ * it communicates with other nodes.
+ *
+ * @param port
+ * the port number you wish to use for incoming connections.
+ * Specifying 0 lets the system choose an available port.
+ *
+ * @param transportFactory
+ * the transport factory to use when creating connections.
+ *
+ * @exception IOException
+ * in case of server transport failure
+ */
+ public OtpSelf(final String node, final String cookie, final int port,
+ final OtpTransportFactory transportFactory) throws IOException {
+ super(node, cookie, transportFactory);
+
+ sock = createServerTransport(port);
if (port != 0) {
this.port = port;
@@ -179,7 +287,7 @@ public class OtpSelf extends OtpLocalNode {
* authorized to connect.
*/
public OtpConnection accept() throws IOException, OtpAuthException {
- Socket newsock = null;
+ OtpTransport newsock = null;
while (true) {
try {
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServerSocketTransport.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServerSocketTransport.java
new file mode 100644
index 0000000000..0e25b6bfb7
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServerSocketTransport.java
@@ -0,0 +1,68 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+package com.ericsson.otp.erlang;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * Default socket-based server transport
+ *
+ * @author Dmitriy Kargapolov
+ */
+public class OtpServerSocketTransport implements OtpServerTransport {
+
+ /**
+ * Underlying server socket
+ */
+ private final ServerSocket socket;
+
+ /**
+ * @see ServerSocket#ServerSocket(int)
+ */
+ public OtpServerSocketTransport(final int port) throws IOException {
+ socket = new ServerSocket(port);
+ }
+
+ /**
+ * @see ServerSocket#getLocalPort()
+ */
+ public int getLocalPort() {
+ return socket.getLocalPort();
+ }
+
+ /**
+ * @see ServerSocket#accept()
+ */
+ public OtpTransport accept() throws IOException {
+ final Socket sock = socket.accept();
+ sock.setTcpNoDelay(true);
+ return new OtpSocketTransport(sock);
+ }
+
+ /**
+ * @see ServerSocket#close()
+ */
+ public void close() throws IOException {
+ socket.close();
+ }
+
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServerTransport.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServerTransport.java
new file mode 100644
index 0000000000..4d31380bee
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServerTransport.java
@@ -0,0 +1,46 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+package com.ericsson.otp.erlang;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+/**
+ * Server-side connection-oriented transport interface.
+ *
+ * @author Dmitriy Kargapolov
+ */
+public interface OtpServerTransport {
+
+ /**
+ * @see ServerSocket#getLocalPort()
+ */
+ int getLocalPort();
+
+ /**
+ * @see ServerSocket#accept()
+ */
+ OtpTransport accept() throws IOException;
+
+ /**
+ * @see ServerSocket#close()
+ */
+ void close() throws IOException;
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSocketTransport.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSocketTransport.java
new file mode 100644
index 0000000000..f690ab59ed
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSocketTransport.java
@@ -0,0 +1,89 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+package com.ericsson.otp.erlang;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/**
+ * Default socket-based client transport
+ *
+ * @author Dmitriy Kargapolov
+ */
+public class OtpSocketTransport implements OtpTransport {
+
+ /**
+ * Underlying socket
+ */
+ private final Socket socket;
+
+ /**
+ * @see Socket#Socket(String, int)
+ */
+ public OtpSocketTransport(final String addr, final int port)
+ throws UnknownHostException, IOException {
+ socket = new Socket(addr, port);
+ socket.setTcpNoDelay(true);
+ }
+
+ /**
+ * @see Socket#Socket(InetAddress, int)
+ */
+ public OtpSocketTransport(final InetAddress addr, final int port)
+ throws UnknownHostException, IOException {
+ socket = new Socket(addr, port);
+ socket.setTcpNoDelay(true);
+ }
+
+ /**
+ * Socket wrapping constructor
+ *
+ * @param s
+ * socket to wrap
+ */
+ public OtpSocketTransport(final Socket s) {
+ socket = s;
+ }
+
+ /**
+ * @see Socket#getInputStream()
+ */
+ public InputStream getInputStream() throws IOException {
+ return socket.getInputStream();
+ }
+
+ /**
+ * @see Socket#getOutputStream()
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return socket.getOutputStream();
+ }
+
+ /**
+ * @see Socket#close()
+ */
+ public void close() throws IOException {
+ socket.close();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSocketTransportFactory.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSocketTransportFactory.java
new file mode 100644
index 0000000000..f6b5bfc86d
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSocketTransportFactory.java
@@ -0,0 +1,56 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+package com.ericsson.otp.erlang;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+/**
+ * Default socket-based transport factory
+ *
+ * @author Dmitriy Kargapolov
+ */
+public class OtpSocketTransportFactory implements OtpTransportFactory {
+
+ /**
+ * @see OtpTransportFactory#createTransport(String, int)
+ */
+ public OtpTransport createTransport(final String addr, final int port)
+ throws IOException {
+ return new OtpSocketTransport(addr, port);
+ }
+
+ /**
+ * @see OtpTransportFactory#createTransport(InetAddress, int)
+ */
+ public OtpTransport createTransport(final InetAddress addr, final int port)
+ throws IOException {
+ return new OtpSocketTransport(addr, port);
+ }
+
+ /**
+ * @see OtpTransportFactory#createServerTransport(int)
+ */
+ public OtpServerTransport createServerTransport(final int port)
+ throws IOException {
+ return new OtpServerSocketTransport(port);
+ }
+
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpTransport.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpTransport.java
new file mode 100644
index 0000000000..51c62d9ef0
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpTransport.java
@@ -0,0 +1,49 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+package com.ericsson.otp.erlang;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * Client-side connection-oriented transport interface.
+ *
+ * @author Dmitriy Kargapolov
+ */
+public interface OtpTransport {
+
+ /**
+ * @see Socket#getInputStream()
+ */
+ public abstract InputStream getInputStream() throws IOException;
+
+ /**
+ * @see Socket#getOutputStream()
+ */
+ public abstract OutputStream getOutputStream() throws IOException;
+
+ /**
+ * @see Socket#close()
+ */
+ public abstract void close() throws IOException;
+
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpTransportFactory.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpTransportFactory.java
new file mode 100644
index 0000000000..bd404daea5
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpTransportFactory.java
@@ -0,0 +1,124 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+package com.ericsson.otp.erlang;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+/**
+ * Factory class used to create client- and server-side transport instances. One
+ * static instance of class implementing this interface is created when program
+ * loaded. Default implementation used is {@link OtpSocketTransportFactory}.
+ * JInterface user can specify custom transport factory implementing this
+ * interface in the following ways:
+ * <dl>
+ * <dt>defining static class as internal to class holding main() method</dt>
+ * <dd>In the systems, where main class can be retrieved with
+ * <code>System.getProperty("sun.java.command")</code>, user can define static
+ * class <b>OtpErlangSystemTuner</b> internal to the main class, providing at
+ * least one static method with the name <b>getOtpTransportFactory</b>, with no
+ * parameters, returning object of class implementing
+ * <b>OtpTransportFactory</b>, for example:
+ *
+ * <pre>
+ *
+ * public class MyMainClass {
+ *
+ * public static class OtpErlangSystemTuner {
+ * ...
+ * public static OtpTransportFactory getOtpTransportFactory() {
+ * return new MyTransportFactory();
+ * }
+ * }
+ *
+ * public static class MyTransportFactory implements OtpTransportFactory {
+ * ...
+ * }
+ *
+ * public static void main(String[] args) {
+ * ...
+ * }
+ * }
+ *
+ *
+ * </pre>
+ *
+ * </dd>
+ *
+ * <dt>specifying factory class in the system properties</dt>
+ * <dd>User-defined transport factory class may be specified via system property
+ * <b>OtpTransportFactory</b>, for example:
+ *
+ * <pre>
+ *
+ * package com.my.company;
+ *
+ * public static class MyTransportFactory implements OtpTransportFactory {
+ * ...
+ * }
+ * </pre>
+ *
+ * In such case program may be run with
+ * -DOtpTransportFactory=com.my.company.MyTransportFactory, or other way of
+ * setting system property <i>before execution of static initializers</i> may be
+ * used.</dd>
+ * </dl>
+ *
+ * @author Dmitriy Kargapolov
+ */
+public interface OtpTransportFactory {
+
+ /**
+ * Create instance of {@link OtpTransport}
+ *
+ * @param addr
+ * host name or IP address string
+ * @param port
+ * port number
+ * @return new socket object
+ * @throws IOException
+ */
+ public abstract OtpTransport createTransport(String addr, int port)
+ throws IOException;
+
+ /**
+ * Create instance of {@link OtpTransport}
+ *
+ * @param addr
+ * peer address
+ * @param port
+ * port number
+ * @return new socket object
+ * @throws IOException
+ */
+ public abstract OtpTransport createTransport(InetAddress addr, int port)
+ throws IOException;
+
+ /**
+ * Create instance of {@link OtpServerTransport}
+ *
+ * @param port
+ * port number to listen on
+ * @return new socket object
+ * @throws IOException
+ */
+ public OtpServerTransport createServerTransport(int port)
+ throws IOException;
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
index 62fa7f990e..a0f19bc1aa 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files
@@ -53,7 +53,13 @@ COMM = \
OtpOutputStream \
OtpPeer \
OtpSelf \
- OtpServer
+ OtpServer \
+ OtpServerSocketTransport \
+ OtpServerTransport \
+ OtpSocketTransport \
+ OtpSocketTransportFactory \
+ OtpTransport \
+ OtpTransportFactory
ERL = \
OtpErlangAtom \
diff --git a/lib/jinterface/test/jinterface_SUITE.erl b/lib/jinterface/test/jinterface_SUITE.erl
index 00abc97ff5..73bab98559 100644
--- a/lib/jinterface/test/jinterface_SUITE.erl
+++ b/lib/jinterface/test/jinterface_SUITE.erl
@@ -22,7 +22,8 @@
init_per_suite/1, end_per_suite/1,
init_per_testcase/2, end_per_testcase/2]).
--export([nodename/1, register_and_whereis/1, get_names/1, boolean_atom/1,
+-export([transport_factory/1,
+ nodename/1, register_and_whereis/1, get_names/1, boolean_atom/1,
node_ping/1, mbox_ping/1,
java_erlang_send_receive/1,
java_internal_send_receive_same_node/1,
@@ -39,7 +40,8 @@
status_handler_localStatus/1, status_handler_remoteStatus/1,
status_handler_connAttempt/1,
maps/1,
- fun_equals/1
+ fun_equals/1,
+ core_match_bind/1
]).
-include_lib("common_test/include/ct.hrl").
@@ -103,12 +105,14 @@ end_per_group(_GroupName, Config) ->
fundamental() ->
[
+ transport_factory, % TransportFactoryTest.java
nodename, % Nodename.java
register_and_whereis, % RegisterAndWhereis.java
get_names, % GetNames.java
boolean_atom, % BooleanAtom.java
maps, % Maps.java
- fun_equals % FunEquals.java
+ fun_equals, % FunEquals.java
+ core_match_bind % CoreMatchBind.java
].
ping() ->
@@ -201,6 +205,16 @@ end_per_testcase(_Case,Config) ->
%%%-----------------------------------------------------------------
%%% TEST CASES
%%%-----------------------------------------------------------------
+transport_factory(doc) ->
+ ["TransportFactoryTest.java: Test custom OTP Transport Factory"];
+transport_factory(suite) ->
+ [];
+transport_factory(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "TransportFactoryTest").
+
+%%%-----------------------------------------------------------------
nodename(doc) ->
["Nodename.java: "
"Test OtpNode.node(), OtpNode.alive() and OtpNode.host()"];
@@ -705,6 +719,18 @@ fun_equals(Config) when is_list(Config) ->
[]).
%%%-----------------------------------------------------------------
+core_match_bind(doc) ->
+ ["CoreMatchBind.java: "
+ "Test OtpErlangObject.match() and bind()"];
+core_match_bind(suite) ->
+ [];
+core_match_bind(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "CoreMatchBind",
+ []).
+
+%%%-----------------------------------------------------------------
%%% INTERNAL FUNCTIONS
%%%-----------------------------------------------------------------
send_receive(TestCaseTag,Fun,Config) ->
diff --git a/lib/jinterface/test/jinterface_SUITE_data/CoreMatchBind.java b/lib/jinterface/test/jinterface_SUITE_data/CoreMatchBind.java
new file mode 100644
index 0000000000..a78a63093e
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/CoreMatchBind.java
@@ -0,0 +1,584 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2015. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.OtpErlangException;
+import com.ericsson.otp.erlang.OtpErlangInt;
+import com.ericsson.otp.erlang.OtpErlangList;
+import com.ericsson.otp.erlang.OtpErlangMap;
+import com.ericsson.otp.erlang.OtpErlangObject;
+import com.ericsson.otp.erlang.OtpErlangTuple;
+import com.ericsson.otp.erlang.OtpOutputStream;
+
+public class CoreMatchBind {
+
+ @SuppressWarnings("serial")
+ private static class DumbObject extends OtpErlangObject {
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ fail("unexpected encode() call");
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ fail("unexpected equals() call");
+ return false;
+ }
+
+ }
+
+ @SuppressWarnings("serial")
+ private static class BoundObject extends OtpErlangObject {
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ fail("unexpected encode() call");
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ fail("unexpected equals() call");
+ return false;
+ }
+
+ }
+
+ @SuppressWarnings("serial")
+ private static class TestObject extends OtpErlangObject {
+
+ private final Binder binder;
+ private DumbObject dumb;
+ private boolean flag;
+ private BoundObject obj;
+
+ public TestObject(final boolean flag, final Binder binder,
+ final DumbObject dumb) {
+ this.flag = flag;
+ this.binder = binder;
+ this.dumb = dumb;
+ }
+
+ public TestObject(final Binder binder, final BoundObject obj) {
+ this.binder = binder;
+ this.obj = obj;
+ }
+
+ public DumbObject getDumb() {
+ return dumb;
+ }
+
+ @Override
+ public String toString() {
+ return flag ? "T" : "F";
+ }
+
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ fail("unexpected encode() call");
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (obj == null) {
+ fail("unexpected equals() call");
+ }
+ return o == obj;
+ }
+
+ @Override
+ public <T> boolean match(final OtpErlangObject term, final T binds) {
+ if (binds != binder) {
+ fail("invalid binder");
+ }
+ if (term != dumb) {
+ fail("invalid object");
+ }
+ return flag;
+ }
+
+ @Override
+ public <T> OtpErlangObject bind(final T binds)
+ throws OtpErlangException {
+ if (binds != binder) {
+ fail("invalid binder");
+ }
+ return obj;
+ }
+
+ }
+
+ /*
+ * "always matched" object
+ */
+ @SuppressWarnings("serial")
+ private static class Any extends OtpErlangObject {
+
+ @Override
+ public String toString() {
+ return "any";
+ }
+
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ fail("unexpected encode() call");
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ fail("unexpected equals() call");
+ return false;
+ }
+
+ @Override
+ public <T> boolean match(final OtpErlangObject term, final T binds) {
+ return true;
+ }
+ }
+
+ private static class Binder {
+ // make object pair for match() testing
+ TestObject makeTest(final boolean flag) {
+ return new TestObject(flag, this, new DumbObject());
+ }
+
+ // make object pair for bind() testing
+ TestObject makeTest() {
+ return new TestObject(this, new BoundObject());
+ }
+ }
+
+ private static void isNotNull(final Object o) throws Exception {
+ if (o == null) {
+ throw new Exception("not null expected");
+ }
+ }
+
+ private static void fail(final String string) {
+ System.err.println(string);
+ new Throwable().printStackTrace(System.err);
+ System.exit(1);
+ }
+
+ private static void isT(final boolean b) throws Exception {
+ if (!b) {
+ throw new Exception("true expected");
+ }
+ }
+
+ private static void isF(final boolean b) throws Exception {
+ if (b) {
+ throw new Exception("false expected");
+ }
+ }
+
+ private static void equals(final OtpErlangObject a, final OtpErlangObject b)
+ throws Exception {
+ if (!a.equals(b)) {
+ throw new Exception(a + " != " + b);
+ }
+ }
+
+ /*
+ * scalar match test - match particular test object (producing given result)
+ * against particular dumb object passing particular bindings object; ensure
+ * all participants are used as expected in match behavior, check result.
+ */
+ private static void scalar_match_test() throws Exception {
+ final Binder bind = new Binder();
+
+ final TestObject t = bind.makeTest(true);
+ isT(t.match(t.getDumb(), bind));
+
+ final TestObject f = bind.makeTest(false);
+ isF(f.match(f.getDumb(), bind));
+ }
+
+ /*
+ * scalar bind test - ensure right object generated based on bindings
+ */
+ private static void scalar_bind_test() throws Exception {
+ final Binder bind = new Binder();
+ final TestObject t = bind.makeTest();
+ final OtpErlangObject o = t.bind(bind);
+ isNotNull(o);
+ equals(t, o);
+ }
+
+ /*
+ * used by tuple_arity_match_test()
+ */
+ private static OtpErlangObject mkTuplePattern(final int n) {
+ final Any a[] = new Any[n];
+ for (int i = 0; i < n; i++) {
+ a[i] = new Any();
+ }
+ return new OtpErlangTuple(a);
+ }
+
+ /*
+ * used by tuple_arity_match_test()
+ */
+ private static OtpErlangObject mkTupleObject(final int n) {
+ final DumbObject a[] = new DumbObject[n];
+ for (int i = 0; i < n; i++) {
+ a[i] = new DumbObject();
+ }
+ return new OtpErlangTuple(a);
+ }
+
+ /*
+ * ensure only tuples of the same arity can match
+ */
+ private static void tuple_arity_match_test(final int m, final int n)
+ throws Exception {
+ final Binder bind = new Binder();
+ for (int i = m; i < n; i++) {
+ for (int j = m; j < n; j++) {
+ final OtpErlangObject p = mkTuplePattern(i);
+ final OtpErlangObject o = mkTupleObject(j);
+ if (i == j) {
+ isT(p.match(o, bind));
+ } else {
+ isF(p.match(o, bind));
+ }
+ }
+ }
+ }
+
+ /*
+ * tuple match test - ensure elements of tuple are matched to corresponding
+ * elements of tested object and result is logical "and" over all elements.
+ */
+ private static void tuple_match_test(final int n) throws Exception {
+ final Binder bind = new Binder();
+ final int max = 1 << n;
+ final TestObject a[] = new TestObject[n];
+ final DumbObject d[] = new DumbObject[n];
+ for (int k = 0; k < max; k++) {
+ for (int m = 1, i = 0; m < max; m = m << 1, i++) {
+ d[i] = new DumbObject();
+ a[i] = new TestObject((k & m) != 0, bind, d[i]);
+ }
+ final OtpErlangObject tpl = new OtpErlangTuple(a);
+ final OtpErlangObject obj = new OtpErlangTuple(d);
+ if (k + 1 < max) {
+ isF(tpl.match(obj, bind));
+ } else {
+ isT(tpl.match(obj, bind));
+ }
+ }
+ }
+
+ /*
+ * tuple bind test - ensure result is a tuple where each element is a result
+ * of binding of corresponding pattern element using provided bindings.
+ */
+ private static void tuple_bind_test(final int n) throws Exception {
+ final Binder bind = new Binder();
+ final TestObject a[] = new TestObject[n];
+ final OtpErlangObject b[] = new OtpErlangObject[n];
+ for (int i = 0; i < n; i++) {
+ a[i] = bind.makeTest();
+ b[i] = a[i].obj;
+ }
+ final OtpErlangObject t = new OtpErlangTuple(a);
+ final OtpErlangObject o = t.bind(bind);
+ isNotNull(o);
+ equals(t, o);
+ }
+
+ private static OtpErlangObject mkListPattern(final int n, final boolean tail)
+ throws OtpErlangException {
+ final Any a[] = new Any[n];
+ for (int i = 0; i < n; i++) {
+ a[i] = new Any();
+ }
+ return tail ? new OtpErlangList(a, new Any()) : new OtpErlangList(a);
+ }
+
+ private static OtpErlangObject mkListObject(final int n, final boolean tail)
+ throws OtpErlangException {
+ final DumbObject a[] = new DumbObject[n];
+ for (int i = 0; i < n; i++) {
+ a[i] = new DumbObject();
+ }
+ return tail ? new OtpErlangList(a, new DumbObject())
+ : new OtpErlangList(a);
+ }
+
+ /*
+ * ensure only lists of the same arity and same tail presence can match
+ */
+ private static void list_arity_match_test(final int m, final int n)
+ throws Exception {
+ final Binder bind = new Binder();
+ for (int i = m; i < n; i++) {
+ for (int j = m; j < n; j++) {
+ for (int k = 0; k < 2; k++) {
+ if (i == 0 && k == 1) {
+ continue;
+ }
+ for (int l = 0; l < 2; l++) {
+ if (j == 0 && l == 1) {
+ continue;
+ }
+ final OtpErlangObject p = mkListPattern(i, k == 1);
+ final OtpErlangObject o = mkListObject(j, l == 1);
+ if (i == j && k == l || k == 1 && i <= j) {
+ isT(p.match(o, bind));
+ } else {
+ isF(p.match(o, bind));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * lists match test - ensure elements of lists are matched to corresponding
+ * elements of tested object and result is logical "and" over all elements,
+ * count tails as well
+ */
+ private static void list_match_test(final int n) throws Exception {
+ final Binder bind = new Binder();
+ final int max = 1 << n;
+ final TestObject a[] = new TestObject[n];
+ final DumbObject d[] = new DumbObject[n];
+ final DumbObject e[] = new DumbObject[n + 1];
+ for (int k = 0; k < max; k++) {
+ for (int m = 1, i = 0; m < max; m = m << 1, i++) {
+ d[i] = new DumbObject();
+ e[i] = d[i];
+ a[i] = new TestObject((k & m) != 0, bind, d[i]);
+ }
+ for (int i = n; i < n + 1; i++) {
+ e[i] = new DumbObject();
+ }
+ final OtpErlangObject lst = new OtpErlangList(a);
+ final OtpErlangObject obj = new OtpErlangList(d);
+ final OtpErlangObject ext = new OtpErlangList(e);
+ final OtpErlangObject eTl = new OtpErlangList(e, new DumbObject());
+
+ if (n > 0) {
+ final DumbObject dTail = new DumbObject();
+ final TestObject tTail = new TestObject(true, bind, dTail);
+ final TestObject fTail = new TestObject(false, bind, dTail);
+ final OtpErlangObject fTailLst = new OtpErlangList(a, fTail);
+ final OtpErlangObject tTailLst = new OtpErlangList(a, tTail);
+ final OtpErlangObject tailObj = new OtpErlangList(d, dTail);
+
+ // match lists with non-matching tails is always false
+ isF(fTailLst.match(tailObj, bind));
+
+ // match list with no tail to list with tail is always false
+ isF(lst.match(tailObj, bind));
+
+ // matching lists with matching tails
+ if (k + 1 < max) {
+ isF(tTailLst.match(tailObj, bind));
+ } else {
+ isT(tTailLst.match(tailObj, bind));
+ }
+
+ // matching shorter pattern with last tail to longer list
+ // with or with no extra tail; matching list pattern
+ // with last tail to same length list with no tail.
+ final Any aTail = new Any();
+ final OtpErlangObject shortLst = new OtpErlangList(a, aTail);
+ if (k + 1 < max) {
+ isF(shortLst.match(obj, bind)); // same arity
+ isF(shortLst.match(ext, bind)); // pattern arity is less
+ isF(shortLst.match(eTl, bind)); //
+ } else {
+ isT(shortLst.match(obj, bind)); // same arity
+ isT(shortLst.match(ext, bind)); // pattern arity is less
+ isT(shortLst.match(eTl, bind)); //
+ }
+ }
+
+ // matching lists with no tails
+ if (k + 1 < max) {
+ isF(lst.match(obj, bind));
+ } else {
+ isT(lst.match(obj, bind));
+ }
+
+ // extra-length object, no tail in "pattern"
+ isF(lst.match(ext, bind));
+ }
+ }
+
+ /*
+ * list bind test - ensure result is a list where each element is a result
+ * of binding of corresponding pattern element using provided bindings.
+ */
+ private static void list_bind_test(final int n) throws Exception {
+ final Binder bind = new Binder();
+ final TestObject a[] = new TestObject[n];
+ final OtpErlangObject b[] = new OtpErlangObject[n];
+ for (int i = 0; i < n; i++) {
+ a[i] = bind.makeTest();
+ b[i] = a[i].obj;
+ }
+ OtpErlangObject t = new OtpErlangList(a);
+ OtpErlangObject o = t.bind(bind);
+ isNotNull(o);
+ equals(t, o);
+ if (n > 0) {
+ // improper list case
+ t = new OtpErlangList(a, bind.makeTest());
+ o = t.bind(bind);
+ isNotNull(o);
+ equals(t, o);
+ }
+ }
+
+ /*
+ * map match test - object may have more keys than pattern
+ */
+ private static void map_match_test(final int m, final int n)
+ throws Exception {
+ final Binder bind = new Binder();
+
+ // pattern side - m elements
+ final OtpErlangObject k1[] = new OtpErlangObject[m];
+ final TestObject a[] = new TestObject[m];
+
+ // object side - n elements
+ final OtpErlangObject k2[] = new OtpErlangObject[n];
+ final DumbObject d[] = new DumbObject[n];
+
+ final int max = Math.max(m, n);
+ final int mskHi = 1 << max;
+ final int full = (1 << m) - 1;
+ for (int k = 0; k < mskHi; k++) {
+ for (int msk = 1, i = 0; msk < mskHi; msk = msk << 1, i++) {
+ if (i < n) {
+ k2[i] = new OtpErlangInt(i);
+ d[i] = new DumbObject();
+ }
+ if (i < m) {
+ k1[i] = new OtpErlangInt(i);
+ a[i] = new TestObject((k & msk) != 0, bind, i < n ? d[i]
+ : new DumbObject());
+ }
+ }
+ final OtpErlangObject map = new OtpErlangMap(k1, a); // m items
+ final OtpErlangObject obj = new OtpErlangMap(k2, d); // n items
+ if ((k & full) == full && m <= n) {
+ isT(map.match(obj, bind));
+ } else {
+ isF(map.match(obj, bind));
+ }
+ }
+ }
+
+ /*
+ * map bind test - ensure result is a map where each element is a result of
+ * binding of corresponding pattern element using provided bindings.
+ */
+ private static void map_bind_test(final int n) throws Exception {
+ final Binder bind = new Binder();
+ final TestObject a[] = new TestObject[n];
+ final OtpErlangObject b[] = new OtpErlangObject[n];
+ final OtpErlangObject k[] = new OtpErlangObject[n];
+ for (int i = 0; i < n; i++) {
+ a[i] = bind.makeTest();
+ b[i] = a[i].obj;
+ k[i] = new OtpErlangInt(i);
+ }
+ final OtpErlangObject t = new OtpErlangMap(k, a);
+ final OtpErlangObject o = t.bind(bind);
+ isNotNull(o);
+ equals(t, o);
+ }
+
+ public static void main(final String[] args) {
+ try {
+ scalar_match_test();
+ System.out.println("scalar_match_test() passed");
+
+ scalar_bind_test();
+ System.out.println("scalar_bind_test() passed");
+
+ for (int m = 0; m < 16; m++) {
+ for (int n = 0; n < 16; n++) {
+ tuple_arity_match_test(m, n);
+ }
+ }
+ System.out.println("tuple_arity_match_test() passed");
+
+ for (int n = 0; n < 16; n++) {
+ tuple_match_test(n);
+ }
+ System.out.println("tuple_match_test() passed");
+
+ for (int n = 0; n < 16; n++) {
+ tuple_bind_test(n);
+ }
+ System.out.println("tuple_bind_test() passed");
+
+ for (int m = 0; m < 16; m++) {
+ for (int n = 0; n < 16; n++) {
+ list_arity_match_test(m, n);
+ }
+ }
+ System.out.println("list_arity_match_test() passed");
+
+ for (int n = 0; n < 16; n++) {
+ list_match_test(n);
+ }
+ System.out.println("list_match_test() passed");
+
+ for (int n = 0; n < 16; n++) {
+ list_bind_test(n);
+ }
+ System.out.println("list_bind_test() passed");
+
+ for (int m = 0; m < 12; m++) {
+ for (int n = 0; n < 12; n++) {
+ map_match_test(m, n);
+ }
+ }
+ System.out.println("map_match_test() passed");
+
+ for (int n = 0; n < 16; n++) {
+ map_bind_test(n);
+ }
+ System.out.println("map_bind_test() passed");
+
+ } catch (final Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ System.out.println("ok");
+ }
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
index cd68f1ead5..a4a69000c6 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
+++ b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
@@ -38,6 +38,7 @@ JINTERFACE_CLASSPATH = @jinterface_classpath@
CLASSPATH = .@PS@$(JINTERFACE_CLASSPATH)@PS@
JAVA_FILES = \
+ TransportFactoryTest.java \
Nodename.java \
RegisterAndWhereis.java \
GetNames.java \
@@ -48,7 +49,8 @@ JAVA_FILES = \
MboxLinkUnlink.java \
NodeStatusHandler.java \
Maps.java \
- FunEquals.java
+ FunEquals.java \
+ CoreMatchBind.java
CLASS_FILES = $(JAVA_FILES:.java=.class)
diff --git a/lib/jinterface/test/jinterface_SUITE_data/TransportFactoryTest.java b/lib/jinterface/test/jinterface_SUITE_data/TransportFactoryTest.java
new file mode 100644
index 0000000000..367e28a512
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/TransportFactoryTest.java
@@ -0,0 +1,90 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2015. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import com.ericsson.otp.erlang.OtpSelf;
+import com.ericsson.otp.erlang.OtpServerTransport;
+import com.ericsson.otp.erlang.OtpSocketTransportFactory;
+import com.ericsson.otp.erlang.OtpTransport;
+import com.ericsson.otp.erlang.OtpTransportFactory;
+
+/**
+ * @author Dmitriy Kargapolov
+ */
+public class TransportFactoryTest {
+
+ /**
+ * example of custom transport factory wrapping default one
+ */
+ public static class TransportFactory implements OtpTransportFactory {
+
+ OtpSocketTransportFactory tf = new OtpSocketTransportFactory();
+
+ public OtpTransport createTransport(final String addr, final int port)
+ throws IOException {
+ clientOk = true;
+ System.out.println("creating transport to " + addr + ", " + port);
+ return tf.createTransport(addr, port);
+ }
+
+ public OtpTransport createTransport(final InetAddress addr,
+ final int port) throws IOException {
+ clientOk = true;
+ System.out.println("creating transport to " + addr + ", " + port);
+ return tf.createTransport(addr, port);
+ }
+
+ public OtpServerTransport createServerTransport(final int port)
+ throws IOException {
+ serverOk = true;
+ System.out.println("creating server transport to " + port);
+ return tf.createServerTransport(port);
+ }
+
+ }
+
+ static boolean serverOk = false;
+ static boolean clientOk = false;
+
+ public static void main(final String[] args) throws IOException {
+
+ // check server transport
+ final OtpSelf self = new OtpSelf("local", new TransportFactory());
+ if (!serverOk) {
+ fail("custom server transport was not created");
+ }
+ System.out.println("accepting connections on " + self.port());
+
+ // check client transport
+ try {
+ self.publishPort();
+ } catch (final Exception e) {
+ }
+ if (!clientOk) {
+ fail("custom client transport was not created");
+ }
+ }
+
+ private static void fail(final String string) {
+ System.err.println(string);
+ System.exit(1);
+ }
+}
diff --git a/lib/jinterface/test/jitu.erl b/lib/jinterface/test/jitu.erl
index b68dfd0351..8097237af6 100644
--- a/lib/jinterface/test/jitu.erl
+++ b/lib/jinterface/test/jitu.erl
@@ -117,7 +117,7 @@ classpath(Dir) ->
end,
es(Dir++PS++
filename:join([code:lib_dir(jinterface),"priv","OtpErlang.jar"])++PS++
- os:getenv("CLASSPATH", "") end,
+ os:getenv("CLASSPATH", ""),
Quote,
EscSpace).