diff options
Diffstat (limited to 'lib/jinterface')
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). |