From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001
From: Erlang/OTP
+ * This abstract class provides the neccesary methods to maintain the actual
+ * connection and encode the messages and headers in the proper format according
+ * to the Erlang distribution protocol. Subclasses can use these methods to
+ * provide a more or less transparent communication channel as desired.
+ *
+ * Note that no receive methods are provided. Subclasses must provide methods
+ * for message delivery, and may implement their own receive methods.
+ *
+ *
+ *
+ * If an exception occurs in any of the methods in this class, the connection
+ * will be closed and must be reopened in order to resume communication with the
+ * peer. This will be indicated to the subclass by passing the exception to its
+ * delivery() method.
+ *
+ * The System property OtpConnection.trace can be used to change the initial
+ * trace level setting for all connections. Normally the initial trace level is
+ * 0 and connections are not traced unless {@link #setTraceLevel
+ * setTraceLevel()} is used to change the setting for a particular connection.
+ * OtpConnection.trace can be used to turn on tracing by default for all
+ * connections.
+ *
+ * Set the trace level for this connection. Normally tracing is off by
+ * default unless System property OtpConnection.trace was set.
+ *
+ * The following levels are valid: 0 turns off tracing completely, 1 shows
+ * ordinary send and receive messages, 2 shows control messages such as link
+ * and unlink, 3 shows handshaking at connection setup, and 4 shows
+ * communication with Epmd. Each level includes the information shown by the
+ * lower ones.
+ *
+ * Represents an OTP node.
+ *
+ * About nodenames: Erlang nodenames consist of two components, an alivename and
+ * a hostname separated by '@'. Additionally, there are two nodename formats:
+ * short and long. Short names are of the form "alive@hostname", while long
+ * names are of the form "alive@host.fully.qualified.domainname". Erlang has
+ * special requirements regarding the use of the short and long formats, in
+ * particular they cannot be mixed freely in a network of communicating nodes,
+ * however Jinterface makes no distinction. See the Erlang documentation for
+ * more information about nodenames.
+ *
+ * The constructors for the AbstractNode classes will create names exactly as
+ * you provide them as long as the name contains '@'. If the string you provide
+ * contains no '@', it will be treated as an alivename and the name of the local
+ * host will be appended, resulting in a shortname. Nodenames longer than 255
+ * characters will be truncated without warning.
+ *
+ * Upon initialization, this class attempts to read the file .erlang.cookie in
+ * the user's home directory, and uses the trimmed first line of the file as the
+ * default cookie by those constructors lacking a cookie argument. If for any
+ * reason the file cannot be found or read, the default cookie will be set to
+ * the empty string (""). The location of a user's home directory is determined
+ * using the system property "user.home", which may not be automatically set on
+ * all platforms.
+ *
+ * Instances of this class cannot be created directly, use one of the subclasses
+ * instead.
+ *
+ * Once a connection is established between the local node and a remote node,
+ * the connection object can be used to send and receive messages between the
+ * nodes and make rpc calls (assuming that the remote node is a real Erlang
+ * node).
+ *
+ *
+ * The various receive methods are all blocking and will return only when a
+ * valid message has been received or an exception is raised.
+ *
+ *
+ * If an exception occurs in any of the methods in this class, the connection
+ * will be closed and must be explicitely reopened in order to resume
+ * communication with the peer.
+ *
+ *
+ * It is not possible to create an instance of this class directly.
+ * OtpConnection objects are returned by {@link OtpSelf#connect(OtpPeer)
+ * OtpSelf.connect()} and {@link OtpSelf#accept() OtpSelf.accept()}.
+ */
+public class OtpConnection extends AbstractConnection {
+ protected OtpSelf self;
+ protected GenericQueue queue; // messages get delivered here
+
+ /*
+ * 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.
+ *
+ * @exception java.io.IOException if it was not possible to connect to the
+ * peer.
+ *
+ * @exception OtpAuthException if handshake resulted in an authentication
+ * error
+ */
+ // package scope
+ OtpConnection(final OtpSelf self, final Socket s) throws IOException,
+ OtpAuthException {
+ super(self, s);
+ this.self = self;
+ queue = new GenericQueue();
+ start();
+ }
+
+ /*
+ * Intiate and open a connection to a remote node.
+ *
+ * @exception java.io.IOException if it was not possible to connect to the
+ * peer.
+ *
+ * @exception OtpAuthException if handshake resulted in an authentication
+ * error.
+ */
+ // package scope
+ OtpConnection(final OtpSelf self, final OtpPeer other) throws IOException,
+ OtpAuthException {
+ super(self, other);
+ this.self = self;
+ queue = new GenericQueue();
+ start();
+ }
+
+ @Override
+ public void deliver(final Exception e) {
+ queue.put(e);
+ }
+
+ @Override
+ public void deliver(final OtpMsg msg) {
+ queue.put(msg);
+ }
+
+ /**
+ * Get information about the node at the peer end of this connection.
+ *
+ * @return the {@link OtpPeer Node} representing the peer node.
+ */
+ public OtpPeer peer() {
+ return peer;
+ }
+
+ /**
+ * Get information about the node at the local end of this connection.
+ *
+ * @return the {@link OtpSelf Node} representing the local node.
+ */
+ public OtpSelf self() {
+ return self;
+ }
+
+ /**
+ * Return the number of messages currently waiting in the receive queue for
+ * this connection.
+ */
+ public int msgCount() {
+ return queue.getCount();
+ }
+
+ /**
+ * Receive a message from a remote process. This method blocks until a valid
+ * message is received or an exception is raised.
+ *
+ *
+ * If the remote node sends a message that cannot be decoded properly, the
+ * connection is closed and the method throws an exception.
+ *
+ * @return an object containing a single Erlang term.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ *
+ * @exception OtpErlangExit
+ * if an exit signal is received from a process on the
+ * peer node.
+ *
+ * @exception OtpAuthException
+ * if the remote node sends a message containing an
+ * invalid cookie.
+ */
+ public OtpErlangObject receive() throws IOException, OtpErlangExit,
+ OtpAuthException {
+ try {
+ return receiveMsg().getMsg();
+ } catch (final OtpErlangDecodeException e) {
+ close();
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ /**
+ * Receive a message from a remote process. This method blocks at most for
+ * the specified time, until a valid message is received or an exception is
+ * raised.
+ *
+ *
+ * If the remote node sends a message that cannot be decoded properly, the
+ * connection is closed and the method throws an exception.
+ *
+ * @param timeout
+ * the time in milliseconds that this operation will block.
+ * Specify 0 to poll the queue.
+ *
+ * @return an object containing a single Erlang term.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ *
+ * @exception OtpErlangExit
+ * if an exit signal is received from a process on the
+ * peer node.
+ *
+ * @exception OtpAuthException
+ * if the remote node sends a message containing an
+ * invalid cookie.
+ *
+ * @exception InterruptedException
+ * if no message if the method times out before a message
+ * becomes available.
+ */
+ public OtpErlangObject receive(final long timeout)
+ throws InterruptedException, IOException, OtpErlangExit,
+ OtpAuthException {
+ try {
+ return receiveMsg(timeout).getMsg();
+ } catch (final OtpErlangDecodeException e) {
+ close();
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ /**
+ * Receive a raw (still encoded) message from a remote process. This message
+ * blocks until a valid message is received or an exception is raised.
+ *
+ *
+ * If the remote node sends a message that cannot be decoded properly, the
+ * connection is closed and the method throws an exception.
+ *
+ * @return an object containing a raw (still encoded) Erlang term.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ *
+ * @exception OtpErlangExit
+ * if an exit signal is received from a process on the
+ * peer node, or if the connection is lost for any
+ * reason.
+ *
+ * @exception OtpAuthException
+ * if the remote node sends a message containing an
+ * invalid cookie.
+ */
+ public OtpInputStream receiveBuf() throws IOException, OtpErlangExit,
+ OtpAuthException {
+ return receiveMsg().getMsgBuf();
+ }
+
+ /**
+ * Receive a raw (still encoded) message from a remote process. This message
+ * blocks at most for the specified time until a valid message is received
+ * or an exception is raised.
+ *
+ *
+ * If the remote node sends a message that cannot be decoded properly, the
+ * connection is closed and the method throws an exception.
+ *
+ * @param timeout
+ * the time in milliseconds that this operation will block.
+ * Specify 0 to poll the queue.
+ *
+ * @return an object containing a raw (still encoded) Erlang term.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ *
+ * @exception OtpErlangExit
+ * if an exit signal is received from a process on the
+ * peer node, or if the connection is lost for any
+ * reason.
+ *
+ * @exception OtpAuthException
+ * if the remote node sends a message containing an
+ * invalid cookie.
+ *
+ * @exception InterruptedException
+ * if no message if the method times out before a message
+ * becomes available.
+ */
+ public OtpInputStream receiveBuf(final long timeout)
+ throws InterruptedException, IOException, OtpErlangExit,
+ OtpAuthException {
+ return receiveMsg(timeout).getMsgBuf();
+ }
+
+ /**
+ * Receive a messge complete with sender and recipient information.
+ *
+ * @return an {@link OtpMsg OtpMsg} containing the header information about
+ * the sender and recipient, as well as the actual message contents.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ *
+ * @exception OtpErlangExit
+ * if an exit signal is received from a process on the
+ * peer node, or if the connection is lost for any
+ * reason.
+ *
+ * @exception OtpAuthException
+ * if the remote node sends a message containing an
+ * invalid cookie.
+ */
+ public OtpMsg receiveMsg() throws IOException, OtpErlangExit,
+ OtpAuthException {
+ final Object o = queue.get();
+
+ if (o instanceof OtpMsg) {
+ return (OtpMsg) o;
+ } else if (o instanceof IOException) {
+ throw (IOException) o;
+ } else if (o instanceof OtpErlangExit) {
+ throw (OtpErlangExit) o;
+ } else if (o instanceof OtpAuthException) {
+ throw (OtpAuthException) o;
+ }
+
+ return null;
+ }
+
+ /**
+ * Receive a messge complete with sender and recipient information. This
+ * method blocks at most for the specified time.
+ *
+ * @param timeout
+ * the time in milliseconds that this operation will block.
+ * Specify 0 to poll the queue.
+ *
+ * @return an {@link OtpMsg OtpMsg} containing the header information about
+ * the sender and recipient, as well as the actual message contents.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ *
+ * @exception OtpErlangExit
+ * if an exit signal is received from a process on the
+ * peer node, or if the connection is lost for any
+ * reason.
+ *
+ * @exception OtpAuthException
+ * if the remote node sends a message containing an
+ * invalid cookie.
+ *
+ * @exception InterruptedException
+ * if no message if the method times out before a message
+ * becomes available.
+ */
+ public OtpMsg receiveMsg(final long timeout) throws InterruptedException,
+ IOException, OtpErlangExit, OtpAuthException {
+ final Object o = queue.get(timeout);
+
+ if (o instanceof OtpMsg) {
+ return (OtpMsg) o;
+ } else if (o instanceof IOException) {
+ throw (IOException) o;
+ } else if (o instanceof OtpErlangExit) {
+ throw (OtpErlangExit) o;
+ } else if (o instanceof OtpAuthException) {
+ throw (OtpAuthException) o;
+ }
+
+ return null;
+ }
+
+ /**
+ * Send a message to a process on a remote node.
+ *
+ * @param dest
+ * the Erlang PID of the remote process.
+ * @param msg
+ * the message to send.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void send(final OtpErlangPid dest, final OtpErlangObject msg)
+ throws IOException {
+ // encode and send the message
+ super.sendBuf(self.pid(), dest, new OtpOutputStream(msg));
+ }
+
+ /**
+ * Send a message to a named process on a remote node.
+ *
+ * @param dest
+ * the name of the remote process.
+ * @param msg
+ * the message to send.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void send(final String dest, final OtpErlangObject msg)
+ throws IOException {
+ // encode and send the message
+ super.sendBuf(self.pid(), dest, new OtpOutputStream(msg));
+ }
+
+ /**
+ * Send a pre-encoded message to a named process on a remote node.
+ *
+ * @param dest
+ * the name of the remote process.
+ * @param payload
+ * the encoded message to send.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void sendBuf(final String dest, final OtpOutputStream payload)
+ throws IOException {
+ super.sendBuf(self.pid(), dest, payload);
+ }
+
+ /**
+ * Send a pre-encoded message to a process on a remote node.
+ *
+ * @param dest
+ * the Erlang PID of the remote process.
+ * @param msg
+ * the encoded message to send.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void sendBuf(final OtpErlangPid dest, final OtpOutputStream payload)
+ throws IOException {
+ super.sendBuf(self.pid(), dest, payload);
+ }
+
+ /**
+ * Send an RPC request to the remote Erlang node. This convenience function
+ * creates the following message and sends it to 'rex' on the remote node:
+ *
+ *
+ * Note that this method has unpredicatble results if the remote node is not
+ * an Erlang node.
+ *
+ * Note that this method has unpredicatble results if the remote node is not
+ * an Erlang node.
+ *
+ * Maintains a connection between a Java process and a remote Erlang, Java or C
+ * node. The object maintains connection state and allows data to be sent to and
+ * received from the peer.
+ *
+ * Once a connection is established between the local node and a remote node,
+ * the connection object can be used to send and receive messages between the
+ * nodes.
+ *
+ * The various receive methods are all blocking and will return only when a
+ * valid message has been received or an exception is raised.
+ *
+ * If an exception occurs in any of the methods in this class, the connection
+ * will be closed and must be reopened in order to resume communication with the
+ * peer.
+ *
+ * The message delivery methods in this class deliver directly to
+ * {@link OtpMbox mailboxes} in the {@link OtpNode OtpNode} class.
+ *
+ * It is not possible to create an instance of this class directly.
+ * OtpCookedConnection objects are created as needed by the underlying mailbox
+ * mechanism.
+ *
+ * Nodes wishing to contact other nodes must first request information from Epmd
+ * before a connection can be set up, however this is done automatically by
+ * {@link OtpSelf#connect(OtpPeer) OtpSelf.connect()} when necessary.
+ *
+ *
+ * The methods {@link #publishPort(OtpLocalNode) publishPort()} and
+ * {@link #unPublishPort(OtpLocalNode) unPublishPort()} will fail if an Epmd
+ * process is not running on the localhost. Additionally
+ * {@link #lookupPort(AbstractNode) lookupPort()} will fail if there is no Epmd
+ * process running on the host where the specified node is running. See the
+ * Erlang documentation for information about starting Epmd.
+ *
+ *
+ * This class contains only static methods, there are no constructors.
+ */
+public class OtpEpmd {
+
+ private static class EpmdPort {
+ private static int epmdPort = 0;
+
+ public static int get() {
+ if (epmdPort == 0) {
+ String env;
+ try {
+ env = System.getenv("ERL_EPMD_PORT");
+ }
+ catch (java.lang.SecurityException e) {
+ env = null;
+ }
+ epmdPort = (env != null) ? Integer.parseInt(env) : 4369;
+ }
+ return epmdPort;
+ }
+ public static void set(int port) {
+ epmdPort = port;
+ }
+ }
+ // common values
+ private static final byte stopReq = (byte) 115;
+
+ // version specific value
+ private static final byte port3req = (byte) 112;
+ private static final byte publish3req = (byte) 97;
+ private static final byte publish3ok = (byte) 89;
+
+ private static final byte port4req = (byte) 122;
+ private static final byte port4resp = (byte) 119;
+ private static final byte publish4req = (byte) 120;
+ private static final byte publish4resp = (byte) 121;
+ private static final byte names4req = (byte) 110;
+
+ private static int traceLevel = 0;
+ private static final int traceThreshold = 4;
+
+ static {
+ // debug this connection?
+ final String trace = System.getProperties().getProperty(
+ "OtpConnection.trace");
+ try {
+ if (trace != null) {
+ traceLevel = Integer.valueOf(trace).intValue();
+ }
+ } catch (final NumberFormatException e) {
+ traceLevel = 0;
+ }
+ }
+
+ // only static methods: no public constructors
+ // hmm, idea: singleton constructor could spawn epmd process
+ private OtpEpmd() {
+ }
+
+
+ /**
+ * Set the port number to be used to contact the epmd process.
+ * Only needed when the default port is not desired and system environment
+ * variable ERL_EPMD_PORT can not be read (applet).
+ */
+ public static void useEpmdPort(int port) {
+ EpmdPort.set(port);
+ }
+
+ /**
+ * Determine what port a node listens for incoming connections on.
+ *
+ * @return the listen port for the specified node, or 0 if the node was not
+ * registered with Epmd.
+ *
+ * @exception java.io.IOException
+ * if there was no response from the name server.
+ */
+ public static int lookupPort(final AbstractNode node) throws IOException {
+ try {
+ return r4_lookupPort(node);
+ } catch (final IOException e) {
+ return r3_lookupPort(node);
+ }
+ }
+
+ /**
+ * Register with Epmd, so that other nodes are able to find and connect to
+ * it.
+ *
+ * @param node
+ * the server node that should be registered with Epmd.
+ *
+ * @return true if the operation was successful. False if the node was
+ * already registered.
+ *
+ * @exception java.io.IOException
+ * if there was no response from the name server.
+ */
+ public static boolean publishPort(final OtpLocalNode node)
+ throws IOException {
+ Socket s = null;
+
+ try {
+ s = r4_publish(node);
+ } catch (final IOException e) {
+ s = r3_publish(node);
+ }
+
+ node.setEpmd(s);
+
+ return s != null;
+ }
+
+ // Ask epmd to close his end of the connection.
+ // Caller should close his epmd socket as well.
+ // This method is pretty forgiving...
+ /**
+ * Unregister from Epmd. Other nodes wishing to connect will no longer be
+ * able to.
+ *
+ *
+ * This method does not report any failures.
+ */
+ public static void unPublishPort(final OtpLocalNode node) {
+ Socket s = null;
+
+ try {
+ s = new Socket((String) null, EpmdPort.get());
+ final OtpOutputStream obuf = new OtpOutputStream();
+ obuf.write2BE(node.alive().length() + 1);
+ obuf.write1(stopReq);
+ obuf.writeN(node.alive().getBytes());
+ obuf.writeTo(s.getOutputStream());
+ // don't even wait for a response (is there one?)
+ if (traceLevel >= traceThreshold) {
+ System.out.println("-> UNPUBLISH " + node + " port="
+ + node.port());
+ System.out.println("<- OK (assumed)");
+ }
+ } catch (final Exception e) {/* ignore all failures */
+ } finally {
+ try {
+ if (s != null) {
+ s.close();
+ }
+ } catch (final IOException e) { /* ignore close failure */
+ }
+ s = null;
+ }
+ }
+
+ private static int r3_lookupPort(final AbstractNode node)
+ throws IOException {
+ int port = 0;
+ Socket s = null;
+
+ try {
+ final OtpOutputStream obuf = new OtpOutputStream();
+ s = new Socket(node.host(), EpmdPort.get());
+
+ // build and send epmd request
+ // length[2], tag[1], alivename[n] (length = n+1)
+ obuf.write2BE(node.alive().length() + 1);
+ obuf.write1(port3req);
+ obuf.writeN(node.alive().getBytes());
+
+ // send request
+ obuf.writeTo(s.getOutputStream());
+
+ if (traceLevel >= traceThreshold) {
+ System.out.println("-> LOOKUP (r3) " + node);
+ }
+
+ // receive and decode reply
+ final byte[] tmpbuf = new byte[100];
+
+ s.getInputStream().read(tmpbuf);
+ final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
+
+ port = ibuf.read2BE();
+ } catch (final IOException e) {
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (no response)");
+ }
+ throw new IOException("Nameserver not responding on " + node.host()
+ + " when looking up " + node.alive());
+ } catch (final OtpErlangDecodeException e) {
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (invalid response)");
+ }
+ throw new IOException("Nameserver not responding on " + node.host()
+ + " when looking up " + node.alive());
+ } finally {
+ try {
+ if (s != null) {
+ s.close();
+ }
+ } catch (final IOException e) { /* ignore close errors */
+ }
+ s = null;
+ }
+
+ if (traceLevel >= traceThreshold) {
+ if (port == 0) {
+ System.out.println("<- NOT FOUND");
+ } else {
+ System.out.println("<- PORT " + port);
+ }
+ }
+ return port;
+ }
+
+ private static int r4_lookupPort(final AbstractNode node)
+ throws IOException {
+ int port = 0;
+ Socket s = null;
+
+ try {
+ final OtpOutputStream obuf = new OtpOutputStream();
+ s = new Socket(node.host(), EpmdPort.get());
+
+ // build and send epmd request
+ // length[2], tag[1], alivename[n] (length = n+1)
+ obuf.write2BE(node.alive().length() + 1);
+ obuf.write1(port4req);
+ obuf.writeN(node.alive().getBytes());
+
+ // send request
+ obuf.writeTo(s.getOutputStream());
+
+ if (traceLevel >= traceThreshold) {
+ System.out.println("-> LOOKUP (r4) " + node);
+ }
+
+ // receive and decode reply
+ // resptag[1], result[1], port[2], ntype[1], proto[1],
+ // disthigh[2], distlow[2], nlen[2], alivename[n],
+ // elen[2], edata[m]
+ final byte[] tmpbuf = new byte[100];
+
+ final int n = s.getInputStream().read(tmpbuf);
+
+ if (n < 0) {
+ // this was an r3 node => not a failure (yet)
+
+ s.close();
+ throw new IOException("Nameserver not responding on "
+ + node.host() + " when looking up " + node.alive());
+ }
+
+ final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
+
+ final int response = ibuf.read1();
+ if (response == port4resp) {
+ final int result = ibuf.read1();
+ if (result == 0) {
+ port = ibuf.read2BE();
+
+ node.ntype = ibuf.read1();
+ node.proto = ibuf.read1();
+ node.distHigh = ibuf.read2BE();
+ node.distLow = ibuf.read2BE();
+ // ignore rest of fields
+ }
+ }
+ } catch (final IOException e) {
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (no response)");
+ }
+ throw new IOException("Nameserver not responding on " + node.host()
+ + " when looking up " + node.alive());
+ } catch (final OtpErlangDecodeException e) {
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (invalid response)");
+ }
+ throw new IOException("Nameserver not responding on " + node.host()
+ + " when looking up " + node.alive());
+ } finally {
+ try {
+ if (s != null) {
+ s.close();
+ }
+ } catch (final IOException e) { /* ignore close errors */
+ }
+ s = null;
+ }
+
+ if (traceLevel >= traceThreshold) {
+ if (port == 0) {
+ System.out.println("<- NOT FOUND");
+ } else {
+ System.out.println("<- PORT " + port);
+ }
+ }
+ return port;
+ }
+
+ private static Socket r3_publish(final OtpLocalNode node)
+ throws IOException {
+ Socket s = null;
+
+ try {
+ final OtpOutputStream obuf = new OtpOutputStream();
+ s = new Socket((String) null, EpmdPort.get());
+
+ obuf.write2BE(node.alive().length() + 3);
+
+ obuf.write1(publish3req);
+ obuf.write2BE(node.port());
+ obuf.writeN(node.alive().getBytes());
+
+ // send request
+ obuf.writeTo(s.getOutputStream());
+ if (traceLevel >= traceThreshold) {
+ System.out.println("-> PUBLISH (r3) " + node + " port="
+ + node.port());
+ }
+
+ final byte[] tmpbuf = new byte[100];
+
+ final int n = s.getInputStream().read(tmpbuf);
+
+ if (n < 0) {
+ s.close();
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (no response)");
+ }
+ return null;
+ }
+
+ final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
+
+ if (ibuf.read1() == publish3ok) {
+ node.creation = ibuf.read2BE();
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- OK");
+ }
+ return s; // success - don't close socket
+ }
+ } catch (final IOException e) {
+ // epmd closed the connection = fail
+ if (s != null) {
+ s.close();
+ }
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (no response)");
+ }
+ throw new IOException("Nameserver not responding on " + node.host()
+ + " when publishing " + node.alive());
+ } catch (final OtpErlangDecodeException e) {
+ if (s != null) {
+ s.close();
+ }
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (invalid response)");
+ }
+ throw new IOException("Nameserver not responding on " + node.host()
+ + " when publishing " + node.alive());
+ }
+
+ if (s != null) {
+ s.close();
+ }
+ return null; // failure
+ }
+
+ /*
+ * this function will get an exception if it tries to talk to an r3 epmd, or
+ * if something else happens that it cannot forsee. In both cases we return
+ * an exception (and the caller should try again, using the r3 protocol). 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)
+ throws IOException {
+ Socket s = null;
+
+ try {
+ final OtpOutputStream obuf = new OtpOutputStream();
+ s = new Socket((String) null, EpmdPort.get());
+
+ obuf.write2BE(node.alive().length() + 13);
+
+ obuf.write1(publish4req);
+ obuf.write2BE(node.port());
+
+ obuf.write1(node.type());
+
+ obuf.write1(node.proto());
+ obuf.write2BE(node.distHigh());
+ obuf.write2BE(node.distLow());
+
+ obuf.write2BE(node.alive().length());
+ obuf.writeN(node.alive().getBytes());
+ obuf.write2BE(0); // No extra
+
+ // send request
+ obuf.writeTo(s.getOutputStream());
+
+ if (traceLevel >= traceThreshold) {
+ System.out.println("-> PUBLISH (r4) " + node + " port="
+ + node.port());
+ }
+
+ // get reply
+ final byte[] tmpbuf = new byte[100];
+ final int n = s.getInputStream().read(tmpbuf);
+
+ if (n < 0) {
+ // this was an r3 node => not a failure (yet)
+ if (s != null) {
+ s.close();
+ }
+ throw new IOException("Nameserver not responding on "
+ + node.host() + " when publishing " + node.alive());
+ }
+
+ final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
+
+ final int response = ibuf.read1();
+ if (response == publish4resp) {
+ final int result = ibuf.read1();
+ if (result == 0) {
+ node.creation = ibuf.read2BE();
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- OK");
+ }
+ return s; // success
+ }
+ }
+ } catch (final IOException e) {
+ // epmd closed the connection = fail
+ if (s != null) {
+ s.close();
+ }
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (no response)");
+ }
+ throw new IOException("Nameserver not responding on " + node.host()
+ + " when publishing " + node.alive());
+ } catch (final OtpErlangDecodeException e) {
+ if (s != null) {
+ s.close();
+ }
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (invalid response)");
+ }
+ throw new IOException("Nameserver not responding on " + node.host()
+ + " when publishing " + node.alive());
+ }
+
+ if (s != null) {
+ s.close();
+ }
+ return null;
+ }
+
+ public static String[] lookupNames() throws IOException {
+ return lookupNames(InetAddress.getLocalHost());
+ }
+
+ public static String[] lookupNames(final InetAddress address)
+ throws IOException {
+ Socket s = null;
+
+ try {
+ final OtpOutputStream obuf = new OtpOutputStream();
+ try {
+ s = new Socket(address, EpmdPort.get());
+
+ obuf.write2BE(1);
+ obuf.write1(names4req);
+ // send request
+ obuf.writeTo(s.getOutputStream());
+
+ if (traceLevel >= traceThreshold) {
+ System.out.println("-> NAMES (r4) ");
+ }
+
+ // get reply
+ final byte[] buffer = new byte[256];
+ final ByteArrayOutputStream out = new ByteArrayOutputStream(256);
+ while (true) {
+ final int bytesRead = s.getInputStream().read(buffer);
+ if (bytesRead == -1) {
+ break;
+ }
+ out.write(buffer, 0, bytesRead);
+ }
+ final byte[] tmpbuf = out.toByteArray();
+ final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
+ ibuf.read4BE(); // read port int
+ // final int port = ibuf.read4BE();
+ // check if port = epmdPort
+
+ final int n = tmpbuf.length;
+ final byte[] buf = new byte[n - 4];
+ System.arraycopy(tmpbuf, 4, buf, 0, n - 4);
+ final String all = OtpErlangString.newString(buf);
+ return all.split("\n");
+ } finally {
+ if (s != null) {
+ s.close();
+ }
+ }
+
+ } catch (final IOException e) {
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (no response)");
+ }
+ throw new IOException(
+ "Nameserver not responding when requesting names");
+ } catch (final OtpErlangDecodeException e) {
+ if (traceLevel >= traceThreshold) {
+ System.out.println("<- (invalid response)");
+ }
+ throw new IOException(
+ "Nameserver not responding when requesting names");
+ }
+ }
+
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
new file mode 100644
index 0000000000..4d53447164
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
@@ -0,0 +1,284 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Serializable;
+
+/**
+ * Provides a Java representation of Erlang atoms. Atoms can be created from
+ * strings whose length is not more than {@link #maxAtomLength maxAtomLength}
+ * characters.
+ */
+public class OtpErlangAtom extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = -3204386396807876641L;
+
+ /** The maximun allowed length of an atom, in characters */
+ public static final int maxAtomLength = 0xff; // one byte length
+
+ private final String atom;
+
+ /**
+ * Create an atom from the given string.
+ *
+ * @param atom
+ * the string to create the atom from.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if the string is null or contains more than
+ * {@link #maxAtomLength maxAtomLength} characters.
+ */
+ public OtpErlangAtom(final String atom) {
+ if (atom == null) {
+ throw new java.lang.IllegalArgumentException(
+ "null string value");
+ }
+
+ if (atom.length() > maxAtomLength) {
+ throw new java.lang.IllegalArgumentException("Atom may not exceed "
+ + maxAtomLength + " characters");
+ }
+ this.atom = atom;
+ }
+
+ /**
+ * Create an atom from a stream containing an atom encoded in Erlang
+ * external format.
+ *
+ * @param buf
+ * the stream containing the encoded atom.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang atom.
+ */
+ public OtpErlangAtom(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ atom = buf.read_atom();
+ }
+
+ /**
+ * Create an atom whose value is "true" or "false".
+ */
+ public OtpErlangAtom(final boolean t) {
+ atom = String.valueOf(t);
+ }
+
+ /**
+ * Get the actual string contained in this object.
+ *
+ * @return the raw string contained in this object, without regard to Erlang
+ * quoting rules.
+ *
+ * @see #toString
+ */
+ public String atomValue() {
+ return atom;
+ }
+
+ /**
+ * The boolean value of this atom.
+ *
+ * @return the value of this atom expressed as a boolean value. If the atom
+ * consists of the characters "true" (independent of case) the value
+ * will be true. For any other values, the value will be false.
+ *
+ */
+ public boolean booleanValue() {
+ return Boolean.valueOf(atomValue()).booleanValue();
+ }
+
+ /**
+ * Get the printname of the atom represented by this object. The difference
+ * between this method and {link #atomValue atomValue()} is that the
+ * printname is quoted and escaped where necessary, according to the Erlang
+ * rules for atom naming.
+ *
+ * @return the printname representation of this atom object.
+ *
+ * @see #atomValue
+ */
+ @Override
+ public String toString() {
+ if (atomNeedsQuoting(atom)) {
+ return "'" + escapeSpecialChars(atom) + "'";
+ } else {
+ return atom;
+ }
+ }
+
+ /**
+ * Determine if two atoms are equal.
+ *
+ * @param o
+ * the other object to compare to.
+ *
+ * @return true if the atoms are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(final Object o) {
+
+ if (!(o instanceof OtpErlangAtom)) {
+ return false;
+ }
+
+ final OtpErlangAtom atom = (OtpErlangAtom) o;
+ return this.atom.compareTo(atom.atom) == 0;
+ }
+
+ @Override
+ protected int doHashCode() {
+ return atom.hashCode();
+ }
+
+ /**
+ * Convert this atom to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded atom should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_atom(atom);
+ }
+
+ /* the following four predicates are helpers for the toString() method */
+ private boolean isErlangDigit(final char c) {
+ return c >= '0' && c <= '9';
+ }
+
+ private boolean isErlangUpper(final char c) {
+ return c >= 'A' && c <= 'Z' || c == '_';
+ }
+
+ private boolean isErlangLower(final char c) {
+ return c >= 'a' && c <= 'z';
+ }
+
+ private boolean isErlangLetter(final char c) {
+ return isErlangLower(c) || isErlangUpper(c);
+ }
+
+ // true if the atom should be displayed with quotation marks
+ private boolean atomNeedsQuoting(final String s) {
+ char c;
+
+ if (s.length() == 0) {
+ return true;
+ }
+ if (!isErlangLower(s.charAt(0))) {
+ return true;
+ }
+
+ final int len = s.length();
+ for (int i = 1; i < len; i++) {
+ c = s.charAt(i);
+
+ if (!isErlangLetter(c) && !isErlangDigit(c) && c != '@') {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Get the atom string, with special characters escaped. Note that this
+ * function currently does not consider any characters above 127 to be
+ * printable.
+ */
+ private String escapeSpecialChars(final String s) {
+ char c;
+ final StringBuffer so = new StringBuffer();
+
+ final int len = s.length();
+ for (int i = 0; i < len; i++) {
+ c = s.charAt(i);
+
+ /*
+ * note that some of these escape sequences are unique to Erlang,
+ * which is why the corresponding 'case' values use octal. The
+ * resulting string is, of course, in Erlang format.
+ */
+
+ switch (c) {
+ // some special escape sequences
+ case '\b':
+ so.append("\\b");
+ break;
+
+ case 0177:
+ so.append("\\d");
+ break;
+
+ case 033:
+ so.append("\\e");
+ break;
+
+ case '\f':
+ so.append("\\f");
+ break;
+
+ case '\n':
+ so.append("\\n");
+ break;
+
+ case '\r':
+ so.append("\\r");
+ break;
+
+ case '\t':
+ so.append("\\t");
+ break;
+
+ case 013:
+ so.append("\\v");
+ break;
+
+ case '\\':
+ so.append("\\\\");
+ break;
+
+ case '\'':
+ so.append("\\'");
+ break;
+
+ case '\"':
+ so.append("\\\"");
+ break;
+
+ default:
+ // some other character classes
+ if (c < 027) {
+ // control chars show as "\^@", "\^A" etc
+ so.append("\\^" + (char) ('A' - 1 + c));
+ } else if (c > 126) {
+ // 8-bit chars show as \345 \344 \366 etc
+ so.append("\\" + Integer.toOctalString(c));
+ } else {
+ // character is printable without modification!
+ so.append(c);
+ }
+ }
+ }
+ return new String(so);
+ }
+
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java
new file mode 100644
index 0000000000..a9eaad540e
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java
@@ -0,0 +1,88 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Serializable;
+
+/**
+ * Provides a Java representation of Erlang binaries. Anything that can be
+ * represented as a sequence of bytes can be made into an Erlang binary.
+ */
+public class OtpErlangBinary extends OtpErlangBitstr implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = -3781009633593609217L;
+
+ /**
+ * Create a binary from a byte array
+ *
+ * @param bin
+ * the array of bytes from which to create the binary.
+ */
+ public OtpErlangBinary(final byte[] bin) {
+ super(bin);
+ }
+
+ /**
+ * Create a binary from a stream containing a binary encoded in Erlang
+ * external format.
+ *
+ * @param buf
+ * the stream containing the encoded binary.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang binary.
+ */
+ public OtpErlangBinary(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ super(new byte[0]);
+ bin = buf.read_binary();
+ pad_bits = 0;
+ }
+
+ /**
+ * Create a binary from an arbitrary Java Object. The object must implement
+ * java.io.Serializable or java.io.Externalizable.
+ *
+ * @param o
+ * the object to serialize and create this binary from.
+ */
+ public OtpErlangBinary(final Object o) {
+ super(o);
+ }
+
+ /**
+ * Convert this binary to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded binary should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_binary(bin);
+ }
+
+ @Override
+ public Object clone() {
+ final OtpErlangBinary that = (OtpErlangBinary) super.clone();
+ return that;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java
new file mode 100644
index 0000000000..97897fe182
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java
@@ -0,0 +1,285 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2007-2009. 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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Provides a Java representation of Erlang bitstrs. An Erlang bitstr is an
+ * Erlang binary with a length not an integral number of bytes (8-bit). Anything
+ * can be represented as a sequence of bytes can be made into an Erlang bitstr.
+ */
+public class OtpErlangBitstr extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = -3781009633593609217L;
+
+ protected byte[] bin;
+ protected int pad_bits;
+
+ /**
+ * Create a bitstr from a byte array
+ *
+ * @param bin
+ * the array of bytes from which to create the bitstr.
+ */
+ public OtpErlangBitstr(final byte[] bin) {
+ this.bin = new byte[bin.length];
+ System.arraycopy(bin, 0, this.bin, 0, bin.length);
+ pad_bits = 0;
+ }
+
+ /**
+ * Create a bitstr with pad bits from a byte array.
+ *
+ * @param bin
+ * the array of bytes from which to create the bitstr.
+ * @param pad_bits
+ * the number of unused bits in the low end of the last byte.
+ */
+ public OtpErlangBitstr(final byte[] bin, final int pad_bits) {
+ this.bin = new byte[bin.length];
+ System.arraycopy(bin, 0, this.bin, 0, bin.length);
+ this.pad_bits = pad_bits;
+
+ check_bitstr(this.bin, this.pad_bits);
+ }
+
+ private void check_bitstr(final byte[] bin, final int pad_bits) {
+ if (pad_bits < 0 || 7 < pad_bits) {
+ throw new java.lang.IllegalArgumentException(
+ "Padding must be in range 0..7");
+ }
+ if (pad_bits != 0 && bin.length == 0) {
+ throw new java.lang.IllegalArgumentException(
+ "Padding on zero length bitstr");
+ }
+ if (bin.length != 0) {
+ // Make sure padding is zero
+ bin[bin.length - 1] &= ~((1 << pad_bits) - 1);
+ }
+ }
+
+ /**
+ * Create a bitstr from a stream containing a bitstr encoded in Erlang
+ * external format.
+ *
+ * @param buf
+ * the stream containing the encoded bitstr.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang bitstr.
+ */
+ public OtpErlangBitstr(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final int pbs[] = { 0 }; // This is ugly just to get a value-result
+ // parameter
+ bin = buf.read_bitstr(pbs);
+ pad_bits = pbs[0];
+
+ check_bitstr(bin, pad_bits);
+ }
+
+ /**
+ * Create a bitstr from an arbitrary Java Object. The object must implement
+ * java.io.Serializable or java.io.Externalizable.
+ *
+ * @param o
+ * the object to serialize and create this bitstr from.
+ */
+ public OtpErlangBitstr(final Object o) {
+ try {
+ bin = toByteArray(o);
+ pad_bits = 0;
+ } catch (final IOException e) {
+ throw new java.lang.IllegalArgumentException(
+ "Object must implement Serializable");
+ }
+ }
+
+ private static byte[] toByteArray(final Object o)
+ throws java.io.IOException {
+
+ if (o == null) {
+ return null;
+ }
+
+ /* need to synchronize use of the shared baos */
+ final java.io.ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(
+ baos);
+
+ oos.writeObject(o);
+ oos.flush();
+
+ return baos.toByteArray();
+ }
+
+ private static Object fromByteArray(final byte[] buf) {
+ if (buf == null) {
+ return null;
+ }
+
+ try {
+ final java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(
+ buf);
+ final java.io.ObjectInputStream ois = new java.io.ObjectInputStream(
+ bais);
+ return ois.readObject();
+ } catch (final java.lang.ClassNotFoundException e) {
+ } catch (final java.io.IOException e) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the byte array from a bitstr, padded with zero bits in the little end
+ * of the last byte.
+ *
+ * @return the byte array containing the bytes for this bitstr.
+ */
+ public byte[] binaryValue() {
+ return bin;
+ }
+
+ /**
+ * Get the size in whole bytes of the bitstr, rest bits in the last byte not
+ * counted.
+ *
+ * @return the number of bytes contained in the bintstr.
+ */
+ public int size() {
+ if (pad_bits == 0) {
+ return bin.length;
+ }
+ if (bin.length == 0) {
+ throw new java.lang.IllegalStateException("Impossible length");
+ }
+ return bin.length - 1;
+ }
+
+ /**
+ * Get the number of pad bits in the last byte of the bitstr. The pad bits
+ * are zero and in the little end.
+ *
+ * @return the number of pad bits in the bitstr.
+ */
+ public int pad_bits() {
+ return pad_bits;
+ }
+
+ /**
+ * Get the java Object from the bitstr. If the bitstr contains a serialized
+ * Java object, then this method will recreate the object.
+ *
+ *
+ * @return the java Object represented by this bitstr, or null if the bitstr
+ * does not represent a Java Object.
+ */
+ public Object getObject() {
+ if (pad_bits != 0) {
+ return null;
+ }
+ return fromByteArray(bin);
+ }
+
+ /**
+ * Get the string representation of this bitstr object. A bitstr is printed
+ * as #Bin<N>, where N is the number of bytes contained in the object
+ * or #bin<N-M> if there are M pad bits.
+ *
+ * @return the Erlang string representation of this bitstr.
+ */
+ @Override
+ public String toString() {
+ if (pad_bits == 0) {
+ return "#Bin<" + bin.length + ">";
+ }
+ if (bin.length == 0) {
+ throw new java.lang.IllegalStateException("Impossible length");
+ }
+ return "#Bin<" + bin.length + "-" + pad_bits + ">";
+ }
+
+ /**
+ * Convert this bitstr to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded bitstr should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_bitstr(bin, pad_bits);
+ }
+
+ /**
+ * Determine if two bitstrs are equal. Bitstrs are equal if they have the
+ * same byte length and tail length, and the array of bytes is identical.
+ *
+ * @param o
+ * the bitstr to compare to.
+ *
+ * @return true if the bitstrs contain the same bits, false otherwise.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangBitstr)) {
+ return false;
+ }
+
+ final OtpErlangBitstr that = (OtpErlangBitstr) o;
+ if (pad_bits != that.pad_bits) {
+ return false;
+ }
+
+ final int len = bin.length;
+ if (len != that.bin.length) {
+ return false;
+ }
+
+ for (int i = 0; i < len; i++) {
+ if (bin[i] != that.bin[i]) {
+ return false; // early exit
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ protected int doHashCode() {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(15);
+ hash.combine(bin);
+ hash.combine(pad_bits);
+ return hash.valueOf();
+ }
+
+ @Override
+ public Object clone() {
+ final OtpErlangBitstr that = (OtpErlangBitstr) super.clone();
+ that.bin = bin.clone();
+ that.pad_bits = pad_bits;
+ return that;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java
new file mode 100644
index 0000000000..b97b5b7d90
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java
@@ -0,0 +1,56 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Serializable;
+
+/**
+ * Provides a Java representation of Erlang booleans, which are special cases of
+ * atoms with values 'true' and 'false'.
+ */
+public class OtpErlangBoolean extends OtpErlangAtom implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 1087178844844988393L;
+
+ /**
+ * Create a boolean from the given value
+ *
+ * @param t
+ * the boolean value to represent as an atom.
+ */
+ public OtpErlangBoolean(final boolean t) {
+ super(t);
+ }
+
+ /**
+ * Create a boolean from a stream containing an atom encoded in Erlang
+ * external format. The value of the boolean will be true if the atom
+ * represented by the stream is "true" without regard to case. For other
+ * atom values, the boolean will have the value false.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang atom.
+ */
+ public OtpErlangBoolean(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ super(buf);
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java
new file mode 100644
index 0000000000..2d598c119e
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java
@@ -0,0 +1,61 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Serializable;
+
+/**
+ * Provides a Java representation of Erlang integral types.
+ */
+public class OtpErlangByte extends OtpErlangLong implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 5778019796466613446L;
+
+ /**
+ * Create an Erlang integer from the given value.
+ *
+ * @param b
+ * the byte value to use.
+ */
+ public OtpErlangByte(final byte b) {
+ super(b);
+ }
+
+ /**
+ * Create an Erlang integer from a stream containing an integer encoded in
+ * Erlang external format.
+ *
+ * @param buf
+ * the stream containing the encoded value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang integer.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as a byte.
+ */
+ public OtpErlangByte(final OtpInputStream buf)
+ throws OtpErlangRangeException, OtpErlangDecodeException {
+ super(buf);
+
+ final byte i = byteValue();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java
new file mode 100644
index 0000000000..b442bbcec1
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java
@@ -0,0 +1,61 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Serializable;
+
+/**
+ * Provides a Java representation of Erlang integral types.
+ */
+public class OtpErlangChar extends OtpErlangLong implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 3225337815669398204L;
+
+ /**
+ * Create an Erlang integer from the given value.
+ *
+ * @param c
+ * the char value to use.
+ */
+ public OtpErlangChar(final char c) {
+ super(c);
+ }
+
+ /**
+ * Create an Erlang integer from a stream containing an integer encoded in
+ * Erlang external format.
+ *
+ * @param buf
+ * the stream containing the encoded value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang integer.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as a char.
+ */
+ public OtpErlangChar(final OtpInputStream buf)
+ throws OtpErlangRangeException, OtpErlangDecodeException {
+ super(buf);
+
+ final char i = charValue();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java
new file mode 100644
index 0000000000..db55deaedf
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java
@@ -0,0 +1,35 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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;
+
+/**
+ * Exception raised when an attempt is made to create an Erlang term by decoding
+ * a sequence of bytes that does not represent the type of term that was
+ * requested.
+ *
+ * @see OtpInputStream
+ */
+public class OtpErlangDecodeException extends OtpErlangException {
+ /**
+ * Provides a detailed message.
+ */
+ public OtpErlangDecodeException(final String msg) {
+ super(msg);
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java
new file mode 100644
index 0000000000..793940e858
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java
@@ -0,0 +1,132 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Serializable;
+
+/**
+ * Provides a Java representation of Erlang floats and doubles. Erlang defines
+ * only one floating point numeric type, however this class and its subclass
+ * {@link OtpErlangFloat} are used to provide representations corresponding to
+ * the Java types Double and Float.
+ */
+public class OtpErlangDouble extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 132947104811974021L;
+
+ private final double d;
+
+ /**
+ * Create an Erlang float from the given double value.
+ */
+ public OtpErlangDouble(final double d) {
+ this.d = d;
+ }
+
+ /**
+ * Create an Erlang float from a stream containing a double encoded in
+ * Erlang external format.
+ *
+ * @param buf
+ * the stream containing the encoded value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang float.
+ */
+ public OtpErlangDouble(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ d = buf.read_double();
+ }
+
+ /**
+ * Get the value, as a double.
+ *
+ * @return the value of this object, as a double.
+ */
+ public double doubleValue() {
+ return d;
+ }
+
+ /**
+ * Get the value, as a float.
+ *
+ * @return the value of this object, as a float.
+ *
+ * @exception OtpErlangRangeException
+ * if the value cannot be represented as a float.
+ */
+ public float floatValue() throws OtpErlangRangeException {
+ final float f = (float) d;
+
+ if (f != d) {
+ throw new OtpErlangRangeException("Value too large for float: " + d);
+ }
+
+ return f;
+ }
+
+ /**
+ * Get the string representation of this double.
+ *
+ * @return the string representation of this double.
+ */
+ @Override
+ public String toString() {
+ return "" + d;
+ }
+
+ /**
+ * Convert this double to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded value should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_double(d);
+ }
+
+ /**
+ * Determine if two floats are equal. Floats are equal if they contain the
+ * same value.
+ *
+ * @param o
+ * the float to compare to.
+ *
+ * @return true if the floats have the same value.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangDouble)) {
+ return false;
+ }
+
+ final OtpErlangDouble d = (OtpErlangDouble) o;
+ return this.d == d.d;
+ }
+
+ @Override
+ protected int doHashCode() {
+ Double v = new Double(d);
+ return v.hashCode();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangException.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangException.java
new file mode 100644
index 0000000000..5b111a56a8
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangException.java
@@ -0,0 +1,40 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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;
+
+/**
+ * Base class for the other OTP erlang exception classes.
+ */
+public class OtpErlangException extends OtpException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Provides no message.
+ */
+ public OtpErlangException() {
+ super();
+ }
+
+ /**
+ * Provides a detailed message.
+ */
+ public OtpErlangException(final String msg) {
+ super(msg);
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangExit.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangExit.java
new file mode 100644
index 0000000000..6b9015c0e5
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangExit.java
@@ -0,0 +1,112 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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;
+
+/**
+ * Exception raised when a communication channel is broken. This can be caused
+ * for a number of reasons, for example:
+ *
+ *
+ * Equivalent to
+ * Equivalent to
+ * The arity of the list is the number of elements it contains.
+ */
+public class OtpErlangList extends OtpErlangObject implements
+ Iterable
+ * The arity of the tuple is the number of elements it contains. Elements are
+ * indexed from 0 to (arity-1) and can be retrieved individually by using the
+ * appropriate index.
+ */
+public class OtpErlangTuple extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 9163498658004915935L;
+
+ private static final OtpErlangObject[] NO_ELEMENTS = new OtpErlangObject[0];
+
+ private OtpErlangObject[] elems = NO_ELEMENTS;
+
+ /**
+ * Create a unary tuple containing the given element.
+ *
+ * @param elem
+ * the element to create the tuple from.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if the element is null.
+ */
+ public OtpErlangTuple(final OtpErlangObject elem) {
+ if (elem == null) {
+ throw new java.lang.IllegalArgumentException(
+ "Tuple element cannot be null");
+ } else {
+ elems = new OtpErlangObject[] { elem };
+ }
+ }
+
+ /**
+ * Create a tuple from an array of terms.
+ *
+ * @param elems
+ * the array of terms to create the tuple from.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if the array is empty (null) or contains null
+ * elements.
+ */
+ public OtpErlangTuple(final OtpErlangObject[] elems) {
+ this(elems, 0, elems.length);
+ }
+
+ /**
+ * Create a tuple from an array of terms.
+ *
+ * @param elems
+ * the array of terms to create the tuple from.
+ * @param start
+ * the offset of the first term to insert.
+ * @param count
+ * the number of terms to insert.
+ *
+ * @exception java.lang.IllegalArgumentException
+ * if the array is empty (null) or contains null
+ * elements.
+ */
+ public OtpErlangTuple(OtpErlangObject[] elems, final int start,
+ final int count) {
+ if (elems == null) {
+ throw new java.lang.IllegalArgumentException(
+ "Tuple content can't be null");
+ } else if (count < 1) {
+ elems = NO_ELEMENTS;
+ } else {
+ this.elems = new OtpErlangObject[count];
+ for (int i = 0; i < count; i++) {
+ if (elems[start + i] != null) {
+ this.elems[i] = elems[start + i];
+ } else {
+ throw new java.lang.IllegalArgumentException(
+ "Tuple element cannot be null (element"
+ + (start + i) + ")");
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a tuple from a stream containing an tuple encoded in Erlang
+ * external format.
+ *
+ * @param buf
+ * the stream containing the encoded tuple.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang tuple.
+ */
+ public OtpErlangTuple(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final int arity = buf.read_tuple_head();
+
+ if (arity > 0) {
+ elems = new OtpErlangObject[arity];
+
+ for (int i = 0; i < arity; i++) {
+ elems[i] = buf.read_any();
+ }
+ } else {
+ elems = NO_ELEMENTS;
+ }
+ }
+
+ /**
+ * Get the arity of the tuple.
+ *
+ * @return the number of elements contained in the tuple.
+ */
+ public int arity() {
+ return elems.length;
+ }
+
+ /**
+ * Get the specified element from the tuple.
+ *
+ * @param i
+ * the index of the requested element. Tuple elements are
+ * numbered as array elements, starting at 0.
+ *
+ * @return the requested element, of null if i is not a valid element index.
+ */
+ public OtpErlangObject elementAt(final int i) {
+ if (i >= arity() || i < 0) {
+ return null;
+ }
+ return elems[i];
+ }
+
+ /**
+ * Get all the elements from the tuple as an array.
+ *
+ * @return an array containing all of the tuple's elements.
+ */
+ public OtpErlangObject[] elements() {
+ final OtpErlangObject[] res = new OtpErlangObject[arity()];
+ System.arraycopy(elems, 0, res, 0, res.length);
+ return res;
+ }
+
+ /**
+ * Get the string representation of the tuple.
+ *
+ * @return the string representation of the tuple.
+ */
+ @Override
+ public String toString() {
+ int i;
+ final StringBuffer s = new StringBuffer();
+ final int arity = elems.length;
+
+ s.append("{");
+
+ for (i = 0; i < arity; i++) {
+ if (i > 0) {
+ s.append(",");
+ }
+ s.append(elems[i].toString());
+ }
+
+ s.append("}");
+
+ return s.toString();
+ }
+
+ /**
+ * Convert this tuple to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded tuple should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ final int arity = elems.length;
+
+ buf.write_tuple_head(arity);
+
+ for (int i = 0; i < arity; i++) {
+ buf.write_any(elems[i]);
+ }
+ }
+
+ /**
+ * Determine if two tuples are equal. Tuples are equal if they have the same
+ * arity and all of the elements are equal.
+ *
+ * @param o
+ * the tuple to compare to.
+ *
+ * @return true if the tuples have the same arity and all the elements are
+ * equal.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangTuple)) {
+ return false;
+ }
+
+ final OtpErlangTuple t = (OtpErlangTuple) o;
+ final int a = arity();
+
+ if (a != t.arity()) {
+ return false;
+ }
+
+ for (int i = 0; i < a; i++) {
+ if (!elems[i].equals(t.elems[i])) {
+ return false; // early exit
+ }
+ }
+
+ return true;
+ }
+
+ protected int doHashCode() {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(9);
+ final int a = arity();
+ hash.combine(a);
+ for (int i = 0; i < a; i++) {
+ hash.combine(elems[i].hashCode());
+ }
+ return hash.valueOf();
+ }
+
+ @Override
+ public Object clone() {
+ final OtpErlangTuple newTuple = (OtpErlangTuple) super.clone();
+ newTuple.elems = elems.clone();
+ return newTuple;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java
new file mode 100644
index 0000000000..f01354d821
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java
@@ -0,0 +1,67 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Serializable;
+
+/**
+ * Provides a Java representation of Erlang integral types.
+ */
+public class OtpErlangUInt extends OtpErlangLong implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = -1450956122937471885L;
+
+ /**
+ * Create an Erlang integer from the given value.
+ *
+ * @param i
+ * the non-negative int value to use.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is negative.
+ */
+ public OtpErlangUInt(final int i) throws OtpErlangRangeException {
+ super(i);
+
+ final int j = uIntValue();
+ }
+
+ /**
+ * Create an Erlang integer from a stream containing an integer encoded in
+ * Erlang external format.
+ *
+ * @param buf
+ * the stream containing the encoded value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang integer.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as an int,
+ * or the value is negative.
+ */
+ public OtpErlangUInt(final OtpInputStream buf)
+ throws OtpErlangRangeException, OtpErlangDecodeException {
+ super(buf);
+
+ final int j = uIntValue();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java
new file mode 100644
index 0000000000..6b6bc7a56b
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java
@@ -0,0 +1,67 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Serializable;
+
+/**
+ * Provides a Java representation of Erlang integral types.
+ */
+public class OtpErlangUShort extends OtpErlangLong implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 300370950578307246L;
+
+ /**
+ * Create an Erlang integer from the given value.
+ *
+ * @param s
+ * the non-negative short value to use.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is negative.
+ */
+ public OtpErlangUShort(final short s) throws OtpErlangRangeException {
+ super(s);
+
+ final short j = uShortValue();
+ }
+
+ /**
+ * Create an Erlang integer from a stream containing an integer encoded in
+ * Erlang external format.
+ *
+ * @param buf
+ * the stream containing the encoded value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang integer.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as a
+ * short, or the value is negative.
+ */
+ public OtpErlangUShort(final OtpInputStream buf)
+ throws OtpErlangRangeException, OtpErlangDecodeException {
+ super(buf);
+
+ final short j = uShortValue();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java
new file mode 100644
index 0000000000..33d25b6021
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java
@@ -0,0 +1,38 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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;
+
+/**
+ * Base class for the other OTP exception classes.
+ */
+public abstract class OtpException extends Exception {
+ /**
+ * Provides no message.
+ */
+ public OtpException() {
+ super();
+ }
+
+ /**
+ * Provides a detailed message.
+ */
+ public OtpException(final String msg) {
+ super(msg);
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
new file mode 100644
index 0000000000..e70b9a786b
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java
@@ -0,0 +1,105 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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;
+
+/**
+ * Provides a collection of constants used when encoding and decoding Erlang
+ * terms.
+ */
+public class OtpExternal {
+ // no constructor
+ private OtpExternal() {
+ }
+
+ /** The tag used for small integers */
+ public static final int smallIntTag = 97;
+
+ /** The tag used for integers */
+ public static final int intTag = 98;
+
+ /** The tag used for floating point numbers */
+ public static final int floatTag = 99;
+ public static final int newFloatTag = 70;
+
+ /** The tag used for atoms */
+ public static final int atomTag = 100;
+
+ /** The tag used for old stype references */
+ public static final int refTag = 101;
+
+ /** The tag used for ports */
+ public static final int portTag = 102;
+
+ /** The tag used for PIDs */
+ public static final int pidTag = 103;
+
+ /** The tag used for small tuples */
+ public static final int smallTupleTag = 104;
+
+ /** The tag used for large tuples */
+ public static final int largeTupleTag = 105;
+
+ /** The tag used for empty lists */
+ public static final int nilTag = 106;
+
+ /** The tag used for strings and lists of small integers */
+ public static final int stringTag = 107;
+
+ /** The tag used for non-empty lists */
+ public static final int listTag = 108;
+
+ /** The tag used for binaries */
+ public static final int binTag = 109;
+
+ /** The tag used for bitstrs */
+ public static final int bitBinTag = 77;
+
+ /** The tag used for small bignums */
+ public static final int smallBigTag = 110;
+
+ /** The tag used for large bignums */
+ public static final int largeBigTag = 111;
+
+ /** The tag used for old new Funs */
+ public static final int newFunTag = 112;
+
+ /** The tag used for external Funs (M:F/A) */
+ public static final int externalFunTag = 113;
+
+ /** The tag used for new style references */
+ public static final int newRefTag = 114;
+
+ /** The tag used for old Funs */
+ public static final int funTag = 117;
+
+ /** The tag used for compressed terms */
+ public static final int compressedTag = 80;
+
+ /** The version number used to mark serialized Erlang terms */
+ public static final int versionTag = 131;
+
+ /** The largest value that can be encoded as an integer */
+ public static final int erlMax = (1 << 27) - 1;
+
+ /** The smallest value that can be encoded as an integer */
+ public static final int erlMin = -(1 << 27);
+
+ /** The longest allowed Erlang atom */
+ public static final int maxAtomLength = 255;
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
new file mode 100644
index 0000000000..b9b43481ee
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -0,0 +1,1203 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+
+/**
+ * Provides a stream for decoding Erlang terms from external format.
+ *
+ *
+ * Note that this class is not synchronized, if you need synchronization you
+ * must provide it yourself.
+ */
+public class OtpInputStream extends ByteArrayInputStream {
+
+ public static int DECODE_INT_LISTS_AS_STRINGS = 1;
+
+ private final int flags;
+
+ /**
+ * @param buf
+ */
+ public OtpInputStream(final byte[] buf) {
+ this(buf, 0);
+ }
+
+ /**
+ * Create a stream from a buffer containing encoded Erlang terms.
+ *
+ * @param flags
+ */
+ public OtpInputStream(final byte[] buf, final int flags) {
+ super(buf);
+ this.flags = flags;
+ }
+
+ /**
+ * Create a stream from a buffer containing encoded Erlang terms at the
+ * given offset and length.
+ *
+ * @param flags
+ */
+ public OtpInputStream(final byte[] buf, final int offset, final int length,
+ final int flags) {
+ super(buf, offset, length);
+ this.flags = flags;
+ }
+
+ /**
+ * Get the current position in the stream.
+ *
+ * @return the current position in the stream.
+ */
+ public int getPos() {
+ return super.pos;
+ }
+
+ /**
+ * Set the current position in the stream.
+ *
+ * @param pos
+ * the position to move to in the stream. If pos indicates a
+ * position beyond the end of the stream, the position is move to
+ * the end of the stream instead. If pos is negative, the
+ * position is moved to the beginning of the stream instead.
+ *
+ * @return the previous position in the stream.
+ */
+ public int setPos(int pos) {
+ final int oldpos = super.pos;
+
+ if (pos > super.count) {
+ pos = super.count;
+ } else if (pos < 0) {
+ pos = 0;
+ }
+
+ super.pos = pos;
+
+ return oldpos;
+ }
+
+ /**
+ * Read an array of bytes from the stream. The method reads at most
+ * buf.length bytes from the input stream.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int readN(final byte[] buf) throws OtpErlangDecodeException {
+ return this.readN(buf, 0, buf.length);
+ }
+
+ /**
+ * Read an array of bytes from the stream. The method reads at most len
+ * bytes from the input stream into offset off of the buffer.
+ *
+ * @return the number of bytes read.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int readN(final byte[] buf, final int off, final int len)
+ throws OtpErlangDecodeException {
+ if (len == 0 && available() == 0) {
+ return 0;
+ }
+ final int i = super.read(buf, off, len);
+ if (i < 0) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ return i;
+ }
+
+ /**
+ * Alias for peek1()
+ */
+ public int peek() throws OtpErlangDecodeException {
+ return peek1();
+ }
+
+ /**
+ * Look ahead one position in the stream without consuming the byte found
+ * there.
+ *
+ * @return the next byte in the stream, as an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int peek1() throws OtpErlangDecodeException {
+ int i;
+ try {
+ i = super.buf[super.pos];
+ if (i < 0) {
+ i += 256;
+ }
+
+ return i;
+ } catch (final Exception e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ }
+
+ public int peek1skip_version() throws OtpErlangDecodeException {
+ int i = peek1();
+ if (i == OtpExternal.versionTag) {
+ read1();
+ i = peek1();
+ }
+ return i;
+ }
+
+ /**
+ * Read a one byte integer from the stream.
+ *
+ * @return the byte read, as an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read1() throws OtpErlangDecodeException {
+ int i;
+ i = super.read();
+
+ if (i < 0) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+
+ return i;
+ }
+
+ public int read1skip_version() throws OtpErlangDecodeException {
+ int tag = read1();
+ if (tag == OtpExternal.versionTag) {
+ tag = read1();
+ }
+ return tag;
+ }
+
+ /**
+ * Read a two byte big endian integer from the stream.
+ *
+ * @return the bytes read, converted from big endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read2BE() throws OtpErlangDecodeException {
+ final byte[] b = new byte[2];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ return (b[0] << 8 & 0xff00) + (b[1] & 0xff);
+ }
+
+ /**
+ * Read a four byte big endian integer from the stream.
+ *
+ * @return the bytes read, converted from big endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read4BE() throws OtpErlangDecodeException {
+ final byte[] b = new byte[4];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ return (b[0] << 24 & 0xff000000) + (b[1] << 16 & 0xff0000)
+ + (b[2] << 8 & 0xff00) + (b[3] & 0xff);
+ }
+
+ /**
+ * Read a two byte little endian integer from the stream.
+ *
+ * @return the bytes read, converted from little endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read2LE() throws OtpErlangDecodeException {
+ final byte[] b = new byte[2];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ return (b[1] << 8 & 0xff00) + (b[0] & 0xff);
+ }
+
+ /**
+ * Read a four byte little endian integer from the stream.
+ *
+ * @return the bytes read, converted from little endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public int read4LE() throws OtpErlangDecodeException {
+ final byte[] b = new byte[4];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ return (b[3] << 24 & 0xff000000) + (b[2] << 16 & 0xff0000)
+ + (b[1] << 8 & 0xff00) + (b[0] & 0xff);
+ }
+
+ /**
+ * Read a little endian integer from the stream.
+ *
+ * @param n
+ * the number of bytes to read
+ *
+ * @return the bytes read, converted from little endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public long readLE(int n) throws OtpErlangDecodeException {
+ final byte[] b = new byte[n];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ long v = 0;
+ while (n-- > 0) {
+ v = v << 8 | (long) b[n] & 0xff;
+ }
+ return v;
+ }
+
+ /**
+ * Read a bigendian integer from the stream.
+ *
+ * @param n
+ * the number of bytes to read
+ *
+ * @return the bytes read, converted from big endian to an integer.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public long readBE(final int n) throws OtpErlangDecodeException {
+ final byte[] b = new byte[n];
+ try {
+ super.read(b);
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+ ;
+ long v = 0;
+ for (int i = 0; i < n; i++) {
+ v = v << 8 | (long) b[i] & 0xff;
+ }
+ return v;
+ }
+
+ /**
+ * Read an Erlang atom from the stream and interpret the value as a boolean.
+ *
+ * @return true if the atom at the current position in the stream contains
+ * the value 'true' (ignoring case), false otherwise.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an atom.
+ */
+ public boolean read_boolean() throws OtpErlangDecodeException {
+ return Boolean.valueOf(read_atom()).booleanValue();
+ }
+
+ /**
+ * Read an Erlang atom from the stream.
+ *
+ * @return a String containing the value of the atom.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an atom.
+ */
+ public String read_atom() throws OtpErlangDecodeException {
+ int tag;
+ int len;
+ byte[] strbuf;
+ String atom;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.atomTag) {
+ throw new OtpErlangDecodeException(
+ "wrong tag encountered, expected " + OtpExternal.atomTag
+ + ", got " + tag);
+ }
+
+ len = read2BE();
+
+ strbuf = new byte[len];
+ this.readN(strbuf);
+ atom = OtpErlangString.newString(strbuf);
+
+ if (atom.length() > OtpExternal.maxAtomLength) {
+ atom = atom.substring(0, OtpExternal.maxAtomLength);
+ }
+
+ return atom;
+ }
+
+ /**
+ * Read an Erlang binary from the stream.
+ *
+ * @return a byte array containing the value of the binary.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a binary.
+ */
+ public byte[] read_binary() throws OtpErlangDecodeException {
+ int tag;
+ int len;
+ byte[] bin;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.binTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.binTag
+ + ", got " + tag);
+ }
+
+ len = read4BE();
+
+ bin = new byte[len];
+ this.readN(bin);
+
+ return bin;
+ }
+
+ /**
+ * Read an Erlang bitstr from the stream.
+ *
+ * @param pad_bits
+ * an int array whose first element will be set to the number of
+ * pad bits in the last byte.
+ *
+ * @return a byte array containing the value of the bitstr.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a bitstr.
+ */
+ public byte[] read_bitstr(final int pad_bits[])
+ throws OtpErlangDecodeException {
+ int tag;
+ int len;
+ byte[] bin;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.bitBinTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.bitBinTag
+ + ", got " + tag);
+ }
+
+ len = read4BE();
+ bin = new byte[len];
+ final int tail_bits = read1();
+ if (tail_bits < 0 || 7 < tail_bits) {
+ throw new OtpErlangDecodeException(
+ "Wrong tail bit count in bitstr: " + tail_bits);
+ }
+ if (len == 0 && tail_bits != 0) {
+ throw new OtpErlangDecodeException(
+ "Length 0 on bitstr with tail bit count: " + tail_bits);
+ }
+ this.readN(bin);
+
+ pad_bits[0] = 8 - tail_bits;
+ return bin;
+ }
+
+ /**
+ * Read an Erlang float from the stream.
+ *
+ * @return the float value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a float.
+ */
+ public float read_float() throws OtpErlangDecodeException {
+ final double d = read_double();
+ return (float) d;
+ }
+
+ /**
+ * Read an Erlang float from the stream.
+ *
+ * @return the float value, as a double.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a float.
+ */
+ public double read_double() throws OtpErlangDecodeException {
+ int tag;
+
+ // parse the stream
+ tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.newFloatTag: {
+ return Double.longBitsToDouble(readBE(8));
+ }
+ case OtpExternal.floatTag: {
+ BigDecimal val;
+ int epos;
+ int exp;
+ final byte[] strbuf = new byte[31];
+ String str;
+
+ // get the string
+ this.readN(strbuf);
+ str = OtpErlangString.newString(strbuf);
+
+ // find the exponent prefix 'e' in the string
+ epos = str.indexOf('e', 0);
+
+ if (epos < 0) {
+ throw new OtpErlangDecodeException("Invalid float format: '"
+ + str + "'");
+ }
+
+ // remove the sign from the exponent, if positive
+ String estr = str.substring(epos + 1).trim();
+
+ if (estr.substring(0, 1).equals("+")) {
+ estr = estr.substring(1);
+ }
+
+ // now put the mantissa and exponent together
+ exp = Integer.valueOf(estr).intValue();
+ val = new BigDecimal(str.substring(0, epos)).movePointRight(exp);
+
+ return val.doubleValue();
+ }
+ default:
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected "
+ + OtpExternal.newFloatTag + ", got " + tag);
+ }
+ }
+
+ /**
+ * Read one byte from the stream.
+ *
+ * @return the byte read.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next byte cannot be read.
+ */
+ public byte read_byte() throws OtpErlangDecodeException {
+ final long l = this.read_long(false);
+ final byte i = (byte) l;
+
+ if (l != i) {
+ throw new OtpErlangDecodeException("Value does not fit in byte: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read a character from the stream.
+ *
+ * @return the character value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an integer that can
+ * be represented as a char.
+ */
+ public char read_char() throws OtpErlangDecodeException {
+ final long l = this.read_long(true);
+ final char i = (char) l;
+
+ if (l != (i & 0xffffL)) {
+ throw new OtpErlangDecodeException("Value does not fit in char: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read an unsigned integer from the stream.
+ *
+ * @return the integer value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * positive integer.
+ */
+ public int read_uint() throws OtpErlangDecodeException {
+ final long l = this.read_long(true);
+ final int i = (int) l;
+
+ if (l != (i & 0xFFFFffffL)) {
+ throw new OtpErlangDecodeException("Value does not fit in uint: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read an integer from the stream.
+ *
+ * @return the integer value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as
+ * an integer.
+ */
+ public int read_int() throws OtpErlangDecodeException {
+ final long l = this.read_long(false);
+ final int i = (int) l;
+
+ if (l != i) {
+ throw new OtpErlangDecodeException("Value does not fit in int: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read an unsigned short from the stream.
+ *
+ * @return the short value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * positive short.
+ */
+ public short read_ushort() throws OtpErlangDecodeException {
+ final long l = this.read_long(true);
+ final short i = (short) l;
+
+ if (l != (i & 0xffffL)) {
+ throw new OtpErlangDecodeException("Value does not fit in ushort: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read a short from the stream.
+ *
+ * @return the short value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * short.
+ */
+ public short read_short() throws OtpErlangDecodeException {
+ final long l = this.read_long(false);
+ final short i = (short) l;
+
+ if (l != i) {
+ throw new OtpErlangDecodeException("Value does not fit in short: "
+ + l);
+ }
+
+ return i;
+ }
+
+ /**
+ * Read an unsigned long from the stream.
+ *
+ * @return the long value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * positive long.
+ */
+ public long read_ulong() throws OtpErlangDecodeException {
+ return this.read_long(true);
+ }
+
+ /**
+ * Read a long from the stream.
+ *
+ * @return the long value.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream can not be represented as a
+ * long.
+ */
+ public long read_long() throws OtpErlangDecodeException {
+ return this.read_long(false);
+ }
+
+ public long read_long(final boolean unsigned)
+ throws OtpErlangDecodeException {
+ final byte[] b = read_integer_byte_array();
+ return OtpInputStream.byte_array_to_long(b, unsigned);
+ }
+
+ /**
+ * Read an integer from the stream.
+ *
+ * @return the value as a big endian 2's complement byte array.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an integer.
+ */
+ public byte[] read_integer_byte_array() throws OtpErlangDecodeException {
+ int tag;
+ byte[] nb;
+
+ tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.smallIntTag:
+ nb = new byte[2];
+ nb[0] = 0;
+ nb[1] = (byte) read1();
+ break;
+
+ case OtpExternal.intTag:
+ nb = new byte[4];
+ if (this.readN(nb) != 4) { // Big endian
+ throw new OtpErlangDecodeException(
+ "Cannot read from intput stream");
+ }
+ break;
+
+ case OtpExternal.smallBigTag:
+ case OtpExternal.largeBigTag:
+ int arity;
+ int sign;
+ if (tag == OtpExternal.smallBigTag) {
+ arity = read1();
+ sign = read1();
+ } else {
+ arity = read4BE();
+ sign = read1();
+ if (arity + 1 < 0) {
+ throw new OtpErlangDecodeException(
+ "Value of largeBig does not fit in BigInteger, arity "
+ + arity + " sign " + sign);
+ }
+ }
+ nb = new byte[arity + 1];
+ // Value is read as little endian. The big end is augumented
+ // with one zero byte to make the value 2's complement positive.
+ if (this.readN(nb, 0, arity) != arity) {
+ throw new OtpErlangDecodeException(
+ "Cannot read from intput stream");
+ }
+ // Reverse the array to make it big endian.
+ for (int i = 0, j = nb.length; i < j--; i++) {
+ // Swap [i] with [j]
+ final byte b = nb[i];
+ nb[i] = nb[j];
+ nb[j] = b;
+ }
+ if (sign != 0) {
+ // 2's complement negate the big endian value in the array
+ int c = 1; // Carry
+ for (int j = nb.length; j-- > 0;) {
+ c = (~nb[j] & 0xFF) + c;
+ nb[j] = (byte) c;
+ c >>= 8;
+ }
+ }
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid integer tag: " + tag);
+ }
+
+ return nb;
+ }
+
+ public static long byte_array_to_long(final byte[] b, final boolean unsigned)
+ throws OtpErlangDecodeException {
+ long v;
+ switch (b.length) {
+ case 0:
+ v = 0;
+ break;
+ case 2:
+ v = ((b[0] & 0xFF) << 8) + (b[1] & 0xFF);
+ v = (short) v; // Sign extend
+ if (v < 0 && unsigned) {
+ throw new OtpErlangDecodeException("Value not unsigned: " + v);
+ }
+ break;
+ case 4:
+ v = ((b[0] & 0xFF) << 24) + ((b[1] & 0xFF) << 16)
+ + ((b[2] & 0xFF) << 8) + (b[3] & 0xFF);
+ v = (int) v; // Sign extend
+ if (v < 0 && unsigned) {
+ throw new OtpErlangDecodeException("Value not unsigned: " + v);
+ }
+ break;
+ default:
+ int i = 0;
+ final byte c = b[i];
+ // Skip non-essential leading bytes
+ if (unsigned) {
+ if (c < 0) {
+ throw new OtpErlangDecodeException("Value not unsigned: "
+ + b);
+ }
+ while (b[i] == 0) {
+ i++; // Skip leading zero sign bytes
+ }
+ } else {
+ if (c == 0 || c == -1) { // Leading sign byte
+ i = 1;
+ // Skip all leading sign bytes
+ while (i < b.length && b[i] == c) {
+ i++;
+ }
+ if (i < b.length) {
+ // Check first non-sign byte to see if its sign
+ // matches the whole number's sign. If not one more
+ // byte is needed to represent the value.
+ if (((c ^ b[i]) & 0x80) != 0) {
+ i--;
+ }
+ }
+ }
+ }
+ if (b.length - i > 8) {
+ // More than 64 bits of value
+ throw new OtpErlangDecodeException(
+ "Value does not fit in long: " + b);
+ }
+ // Convert the necessary bytes
+ for (v = c < 0 ? -1 : 0; i < b.length; i++) {
+ v = v << 8 | b[i] & 0xFF;
+ }
+ }
+ return v;
+ }
+
+ /**
+ * Read a list header from the stream.
+ *
+ * @return the arity of the list.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a list.
+ */
+ public int read_list_head() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.nilTag:
+ arity = 0;
+ break;
+
+ case OtpExternal.stringTag:
+ arity = read2BE();
+ break;
+
+ case OtpExternal.listTag:
+ arity = read4BE();
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid list tag: " + tag);
+ }
+
+ return arity;
+ }
+
+ /**
+ * Read a tuple header from the stream.
+ *
+ * @return the arity of the tuple.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a tuple.
+ */
+ public int read_tuple_head() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ // decode the tuple header and get arity
+ switch (tag) {
+ case OtpExternal.smallTupleTag:
+ arity = read1();
+ break;
+
+ case OtpExternal.largeTupleTag:
+ arity = read4BE();
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid tuple tag: " + tag);
+ }
+
+ return arity;
+ }
+
+ /**
+ * Read an empty list from the stream.
+ *
+ * @return zero (the arity of the list).
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an empty list.
+ */
+ public int read_nil() throws OtpErlangDecodeException {
+ int arity = 0;
+ final int tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.nilTag:
+ arity = 0;
+ break;
+
+ default:
+ throw new OtpErlangDecodeException("Not valid nil tag: " + tag);
+ }
+
+ return arity;
+ }
+
+ /**
+ * Read an Erlang PID from the stream.
+ *
+ * @return the value of the PID.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an Erlang PID.
+ */
+ public OtpErlangPid read_pid() throws OtpErlangDecodeException {
+ String node;
+ int id;
+ int serial;
+ int creation;
+ int tag;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.pidTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.pidTag
+ + ", got " + tag);
+ }
+
+ node = read_atom();
+ id = read4BE() & 0x7fff; // 15 bits
+ serial = read4BE() & 0x1fff; // 13 bits
+ creation = read1() & 0x03; // 2 bits
+
+ return new OtpErlangPid(node, id, serial, creation);
+ }
+
+ /**
+ * Read an Erlang port from the stream.
+ *
+ * @return the value of the port.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an Erlang port.
+ */
+ public OtpErlangPort read_port() throws OtpErlangDecodeException {
+ String node;
+ int id;
+ int creation;
+ int tag;
+
+ tag = read1skip_version();
+
+ if (tag != OtpExternal.portTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.portTag
+ + ", got " + tag);
+ }
+
+ node = read_atom();
+ id = read4BE() & 0xfffffff; // 28 bits
+ creation = read1() & 0x03; // 2 bits
+
+ return new OtpErlangPort(node, id, creation);
+ }
+
+ /**
+ * Read an Erlang reference from the stream.
+ *
+ * @return the value of the reference
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not an Erlang reference.
+ */
+ public OtpErlangRef read_ref() throws OtpErlangDecodeException {
+ String node;
+ int id;
+ int creation;
+ int tag;
+
+ tag = read1skip_version();
+
+ switch (tag) {
+ case OtpExternal.refTag:
+ node = read_atom();
+ id = read4BE() & 0x3ffff; // 18 bits
+ creation = read1() & 0x03; // 2 bits
+ return new OtpErlangRef(node, id, creation);
+
+ case OtpExternal.newRefTag:
+ final int arity = read2BE();
+ node = read_atom();
+ creation = read1() & 0x03; // 2 bits
+
+ final int[] ids = new int[arity];
+ for (int i = 0; i < arity; i++) {
+ ids[i] = read4BE();
+ }
+ ids[0] &= 0x3ffff; // first id gets truncated to 18 bits
+ return new OtpErlangRef(node, ids, creation);
+
+ default:
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected ref, got " + tag);
+ }
+ }
+
+ public OtpErlangFun read_fun() throws OtpErlangDecodeException {
+ final int tag = read1skip_version();
+ if (tag == OtpExternal.funTag) {
+ final int nFreeVars = read4BE();
+ final OtpErlangPid pid = read_pid();
+ final String module = read_atom();
+ final long index = read_long();
+ final long uniq = read_long();
+ final OtpErlangObject[] freeVars = new OtpErlangObject[nFreeVars];
+ for (int i = 0; i < nFreeVars; ++i) {
+ freeVars[i] = read_any();
+ }
+ return new OtpErlangFun(pid, module, index, uniq, freeVars);
+ } else if (tag == OtpExternal.newFunTag) {
+ final int n = read4BE();
+ final int arity = read1();
+ final byte[] md5 = new byte[16];
+ readN(md5);
+ final int index = read4BE();
+ final int nFreeVars = read4BE();
+ final String module = read_atom();
+ final long oldIndex = read_long();
+ final long uniq = read_long();
+ final OtpErlangPid pid = read_pid();
+ final OtpErlangObject[] freeVars = new OtpErlangObject[nFreeVars];
+ for (int i = 0; i < nFreeVars; ++i) {
+ freeVars[i] = read_any();
+ }
+ return new OtpErlangFun(pid, module, arity, md5, index, oldIndex,
+ uniq, freeVars);
+ } else {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected fun, got " + tag);
+ }
+ }
+
+ public OtpErlangExternalFun read_external_fun()
+ throws OtpErlangDecodeException {
+ final int tag = read1skip_version();
+ if (tag != OtpExternal.externalFunTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected external fun, got " + tag);
+ }
+ final String module = read_atom();
+ final String function = read_atom();
+ final int arity = (int) read_long();
+ return new OtpErlangExternalFun(module, function, arity);
+ }
+
+ /**
+ * Read a string from the stream.
+ *
+ * @return the value of the string.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a string.
+ */
+ public String read_string() throws OtpErlangDecodeException {
+ int tag;
+ int len;
+ byte[] strbuf;
+ int[] intbuf;
+ tag = read1skip_version();
+ switch (tag) {
+ case OtpExternal.stringTag:
+ len = read2BE();
+ strbuf = new byte[len];
+ this.readN(strbuf);
+ return OtpErlangString.newString(strbuf);
+ case OtpExternal.nilTag:
+ return "";
+ case OtpExternal.listTag: // List when unicode +
+ len = read4BE();
+ intbuf = new int[len];
+ for (int i = 0; i < len; i++) {
+ intbuf[i] = read_int();
+ if (! OtpErlangString.isValidCodePoint(intbuf[i])) {
+ throw new OtpErlangDecodeException
+ ("Invalid CodePoint: " + intbuf[i]);
+ }
+ }
+ read_nil();
+ return new String(intbuf, 0, intbuf.length);
+ default:
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected " + OtpExternal.stringTag
+ + " or " + OtpExternal.listTag + ", got " + tag);
+ }
+ }
+
+ /**
+ * Read a compressed term from the stream
+ *
+ * @return the resulting uncompressed term.
+ *
+ * @exception OtpErlangDecodeException
+ * if the next term in the stream is not a compressed term.
+ */
+ public OtpErlangObject read_compressed() throws OtpErlangDecodeException {
+ final int tag = read1skip_version();
+
+ if (tag != OtpExternal.compressedTag) {
+ throw new OtpErlangDecodeException(
+ "Wrong tag encountered, expected "
+ + OtpExternal.compressedTag + ", got " + tag);
+ }
+
+ final int size = read4BE();
+ final byte[] buf = new byte[size];
+ final java.util.zip.InflaterInputStream is =
+ new java.util.zip.InflaterInputStream(this);
+ try {
+ final int dsize = is.read(buf, 0, size);
+ if (dsize != size) {
+ throw new OtpErlangDecodeException("Decompression gave "
+ + dsize + " bytes, not " + size);
+ }
+ } catch (final IOException e) {
+ throw new OtpErlangDecodeException("Cannot read from input stream");
+ }
+
+ final OtpInputStream ois = new OtpInputStream(buf, flags);
+ return ois.read_any();
+ }
+
+ /**
+ * Read an arbitrary Erlang term from the stream.
+ *
+ * @return the Erlang term.
+ *
+ * @exception OtpErlangDecodeException
+ * if the stream does not contain a known Erlang type at the
+ * next position.
+ */
+ public OtpErlangObject read_any() throws OtpErlangDecodeException {
+ // calls one of the above functions, depending on o
+ final int tag = peek1skip_version();
+
+ switch (tag) {
+ case OtpExternal.smallIntTag:
+ case OtpExternal.intTag:
+ case OtpExternal.smallBigTag:
+ case OtpExternal.largeBigTag:
+ return new OtpErlangLong(this);
+
+ case OtpExternal.atomTag:
+ return new OtpErlangAtom(this);
+
+ case OtpExternal.floatTag:
+ case OtpExternal.newFloatTag:
+ return new OtpErlangDouble(this);
+
+ case OtpExternal.refTag:
+ case OtpExternal.newRefTag:
+ return new OtpErlangRef(this);
+
+ case OtpExternal.portTag:
+ return new OtpErlangPort(this);
+
+ case OtpExternal.pidTag:
+ return new OtpErlangPid(this);
+
+ case OtpExternal.stringTag:
+ return new OtpErlangString(this);
+
+ case OtpExternal.listTag:
+ case OtpExternal.nilTag:
+ if ((flags & DECODE_INT_LISTS_AS_STRINGS) != 0) {
+ final int savePos = getPos();
+ try {
+ return new OtpErlangString(this);
+ } catch (final OtpErlangDecodeException e) {
+ }
+ setPos(savePos);
+ }
+ return new OtpErlangList(this);
+
+ case OtpExternal.smallTupleTag:
+ case OtpExternal.largeTupleTag:
+ return new OtpErlangTuple(this);
+
+ case OtpExternal.binTag:
+ return new OtpErlangBinary(this);
+
+ case OtpExternal.bitBinTag:
+ return new OtpErlangBitstr(this);
+
+ case OtpExternal.compressedTag:
+ return read_compressed();
+
+ case OtpExternal.newFunTag:
+ case OtpExternal.funTag:
+ return new OtpErlangFun(this);
+
+ default:
+ throw new OtpErlangDecodeException("Uknown data type: " + tag);
+ }
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java
new file mode 100644
index 0000000000..fbd0eb4073
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java
@@ -0,0 +1,161 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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;
+
+/**
+ * This class represents local node types. It is used to group the node types
+ * {@link OtpNode OtpNode} and {@link OtpSelf OtpSelf}.
+ */
+public class OtpLocalNode extends AbstractNode {
+ private int serial = 0;
+ private int pidCount = 1;
+ private int portCount = 1;
+ private int refId[];
+
+ protected int port;
+ protected java.net.Socket epmd;
+
+ protected OtpLocalNode() {
+ super();
+ init();
+ }
+
+ /**
+ * Create a node with the given name and the default cookie.
+ */
+ protected OtpLocalNode(final String node) {
+ super(node);
+ init();
+ }
+
+ /**
+ * Create a node with the given name and cookie.
+ */
+ protected OtpLocalNode(final String node, final String cookie) {
+ super(node, cookie);
+ init();
+ }
+
+ private void init() {
+ serial = 0;
+ pidCount = 1;
+ portCount = 1;
+ refId = new int[3];
+ refId[0] = 1;
+ refId[1] = 0;
+ refId[2] = 0;
+ }
+
+ /**
+ * Get the port number used by this node.
+ *
+ * @return the port number this server node is accepting connections on.
+ */
+ public int port() {
+ return port;
+ }
+
+ /**
+ * Set the Epmd socket after publishing this nodes listen port to Epmd.
+ *
+ * @param s
+ * The socket connecting this node to Epmd.
+ */
+ protected void setEpmd(final java.net.Socket s) {
+ epmd = s;
+ }
+
+ /**
+ * Get the Epmd socket.
+ *
+ * @return The socket connecting this node to Epmd.
+ */
+ protected java.net.Socket getEpmd() {
+ return epmd;
+ }
+
+ /**
+ * Create an Erlang {@link OtpErlangPid pid}. Erlang pids are based upon
+ * some node specific information; this method creates a pid using the
+ * information in this node. Each call to this method produces a unique pid.
+ *
+ * @return an Erlang pid.
+ */
+ public synchronized OtpErlangPid createPid() {
+ final OtpErlangPid p = new OtpErlangPid(node, pidCount, serial,
+ creation);
+
+ pidCount++;
+ if (pidCount > 0x7fff) {
+ pidCount = 0;
+
+ serial++;
+ if (serial > 0x1fff) { /* 13 bits */
+ serial = 0;
+ }
+ }
+
+ return p;
+ }
+
+ /**
+ * Create an Erlang {@link OtpErlangPort port}. Erlang ports are based upon
+ * some node specific information; this method creates a port using the
+ * information in this node. Each call to this method produces a unique
+ * port. It may not be meaningful to create a port in a non-Erlang
+ * environment, but this method is provided for completeness.
+ *
+ * @return an Erlang port.
+ */
+ public synchronized OtpErlangPort createPort() {
+ final OtpErlangPort p = new OtpErlangPort(node, portCount, creation);
+
+ portCount++;
+ if (portCount > 0xfffffff) { /* 28 bits */
+ portCount = 0;
+ }
+
+ return p;
+ }
+
+ /**
+ * Create an Erlang {@link OtpErlangRef reference}. Erlang references are
+ * based upon some node specific information; this method creates a
+ * reference using the information in this node. Each call to this method
+ * produces a unique reference.
+ *
+ * @return an Erlang reference.
+ */
+ public synchronized OtpErlangRef createRef() {
+ final OtpErlangRef r = new OtpErlangRef(node, refId, creation);
+
+ // increment ref ids (3 ints: 18 + 32 + 32 bits)
+ refId[0]++;
+ if (refId[0] > 0x3ffff) {
+ refId[0] = 0;
+
+ refId[1]++;
+ if (refId[1] == 0) {
+ refId[2]++;
+ }
+ }
+
+ return r;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java
new file mode 100644
index 0000000000..903a446258
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java
@@ -0,0 +1,354 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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;
+
+// package scope
+class OtpMD5 {
+
+ /*
+ * * MD5 constants
+ */
+ static final long S11 = 7;
+ static final long S12 = 12;
+ static final long S13 = 17;
+ static final long S14 = 22;
+ static final long S21 = 5;
+ static final long S22 = 9;
+ static final long S23 = 14;
+ static final long S24 = 20;
+ static final long S31 = 4;
+ static final long S32 = 11;
+ static final long S33 = 16;
+ static final long S34 = 23;
+ static final long S41 = 6;
+ static final long S42 = 10;
+ static final long S43 = 15;
+ static final long S44 = 21;
+
+ /*
+ * Has to be this large to avoid sign problems
+ */
+
+ private final long state[] = { 0x67452301L, 0xefcdab89L, 0x98badcfeL,
+ 0x10325476L };
+ private final long count[] = { 0L, 0L };
+ private final int buffer[];
+
+ public OtpMD5() {
+ buffer = new int[64];
+ int i;
+ for (i = 0; i < 64; ++i) {
+ buffer[i] = 0;
+ }
+ }
+
+ private int[] to_bytes(final String s) {
+ final char tmp[] = s.toCharArray();
+ final int ret[] = new int[tmp.length];
+ int i;
+
+ for (i = 0; i < tmp.length; ++i) {
+ ret[i] = tmp[i] & 0xFF;
+ }
+ return ret;
+ }
+
+ private int[] clean_bytes(final int bytes[]) {
+ final int ret[] = new int[bytes.length];
+ int i;
+
+ for (i = 0; i < bytes.length; ++i) {
+ ret[i] = bytes[i] & 0xFF;
+ }
+ return ret;
+ }
+
+ /*
+ * * A couple of operations where 32 bit over/under-flow is expected
+ */
+
+ private long shl(final long what, final int steps) {
+ return what << steps & 0xFFFFFFFFL;
+ }
+
+ private long shr(final long what, final int steps) {
+ return what >>> steps;
+ }
+
+ private long plus(final long a, final long b) {
+ return a + b & 0xFFFFFFFFL;
+ }
+
+ private long not(long x) {
+ return ~x & 0xFFFFFFFFL;
+ }
+
+ private void to_buffer(int to_start, final int[] from, int from_start,
+ int num) {
+ while (num-- > 0) {
+ buffer[to_start++] = from[from_start++];
+ }
+ }
+
+ private void do_update(final int bytes[]) {
+ int index = (int) (count[0] >>> 3 & 0x3F);
+ final long inlen = bytes.length;
+ final long addcount = shl(inlen, 3);
+ final long partlen = 64 - index;
+ int i;
+
+ count[0] = plus(count[0], addcount);
+
+ if (count[0] < addcount) {
+ ++count[1];
+ }
+
+ count[1] = plus(count[1], shr(inlen, 29));
+
+ /* dumpstate(); */
+
+ if (inlen >= partlen) {
+ to_buffer(index, bytes, 0, (int) partlen);
+ transform(buffer, 0);
+
+ for (i = (int) partlen; i + 63 < inlen; i += 64) {
+ transform(bytes, i);
+ }
+
+ index = 0;
+ } else {
+ i = 0;
+ }
+
+ /* dumpstate(); */
+
+ to_buffer(index, bytes, i, (int) inlen - i);
+
+ /* dumpstate(); */
+
+ }
+
+ private void dumpstate() {
+ System.out.println("state = {" + state[0] + ", " + state[1] + ", "
+ + state[2] + ", " + state[3] + "}");
+ System.out.println("count = {" + count[0] + ", " + count[1] + "}");
+ System.out.print("buffer = {");
+ int i;
+ for (i = 0; i < 64; ++i) {
+ if (i > 0) {
+ System.out.print(", ");
+ }
+ System.out.print(buffer[i]);
+ }
+ System.out.println("}");
+ }
+
+ /*
+ * * The transformation functions
+ */
+
+ private long F(final long x, final long y, final long z) {
+ return x & y | not(x) & z;
+ }
+
+ private long G(final long x, final long y, final long z) {
+ return x & z | y & not(z);
+ }
+
+ private long H(final long x, final long y, final long z) {
+ return x ^ y ^ z;
+ }
+
+ private long I(final long x, final long y, final long z) {
+ return y ^ (x | not(z));
+ }
+
+ private long ROTATE_LEFT(final long x, final long n) {
+ return shl(x, (int) n) | shr(x, (int) (32 - n));
+ }
+
+ private long FF(long a, final long b, final long c, final long d,
+ final long x, final long s, final long ac) {
+ a = plus(a, plus(plus(F(b, c, d), x), ac));
+ a = ROTATE_LEFT(a, s);
+ return plus(a, b);
+ }
+
+ private long GG(long a, final long b, final long c, final long d,
+ final long x, final long s, final long ac) {
+ a = plus(a, plus(plus(G(b, c, d), x), ac));
+ a = ROTATE_LEFT(a, s);
+ return plus(a, b);
+ }
+
+ private long HH(long a, final long b, final long c, final long d,
+ final long x, final long s, final long ac) {
+ a = plus(a, plus(plus(H(b, c, d), x), ac));
+ a = ROTATE_LEFT(a, s);
+ return plus(a, b);
+ }
+
+ private long II(long a, final long b, final long c, final long d,
+ final long x, final long s, final long ac) {
+ a = plus(a, plus(plus(I(b, c, d), x), ac));
+ a = ROTATE_LEFT(a, s);
+ return plus(a, b);
+ }
+
+ private void decode(final long output[], final int input[],
+ final int in_from, final int len) {
+ int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[i] = input[j + in_from] | shl(input[j + in_from + 1], 8)
+ | shl(input[j + in_from + 2], 16)
+ | shl(input[j + in_from + 3], 24);
+ }
+ }
+
+ private void transform(final int block[], final int from) {
+ long a = state[0];
+ long b = state[1];
+ long c = state[2];
+ long d = state[3];
+ final long x[] = new long[16];
+
+ decode(x, block, from, 64);
+
+ a = FF(a, b, c, d, x[0], S11, 0xd76aa478L); /* 1 */
+ d = FF(d, a, b, c, x[1], S12, 0xe8c7b756L); /* 2 */
+ c = FF(c, d, a, b, x[2], S13, 0x242070dbL); /* 3 */
+ b = FF(b, c, d, a, x[3], S14, 0xc1bdceeeL); /* 4 */
+ a = FF(a, b, c, d, x[4], S11, 0xf57c0fafL); /* 5 */
+ d = FF(d, a, b, c, x[5], S12, 0x4787c62aL); /* 6 */
+ c = FF(c, d, a, b, x[6], S13, 0xa8304613L); /* 7 */
+ b = FF(b, c, d, a, x[7], S14, 0xfd469501L); /* 8 */
+ a = FF(a, b, c, d, x[8], S11, 0x698098d8L); /* 9 */
+ d = FF(d, a, b, c, x[9], S12, 0x8b44f7afL); /* 10 */
+ c = FF(c, d, a, b, x[10], S13, 0xffff5bb1L); /* 11 */
+ b = FF(b, c, d, a, x[11], S14, 0x895cd7beL); /* 12 */
+ a = FF(a, b, c, d, x[12], S11, 0x6b901122L); /* 13 */
+ d = FF(d, a, b, c, x[13], S12, 0xfd987193L); /* 14 */
+ c = FF(c, d, a, b, x[14], S13, 0xa679438eL); /* 15 */
+ b = FF(b, c, d, a, x[15], S14, 0x49b40821L); /* 16 */
+
+ /* Round 2 */
+ a = GG(a, b, c, d, x[1], S21, 0xf61e2562L); /* 17 */
+ d = GG(d, a, b, c, x[6], S22, 0xc040b340L); /* 18 */
+ c = GG(c, d, a, b, x[11], S23, 0x265e5a51L); /* 19 */
+ b = GG(b, c, d, a, x[0], S24, 0xe9b6c7aaL); /* 20 */
+ a = GG(a, b, c, d, x[5], S21, 0xd62f105dL); /* 21 */
+ d = GG(d, a, b, c, x[10], S22, 0x2441453L); /* 22 */
+ c = GG(c, d, a, b, x[15], S23, 0xd8a1e681L); /* 23 */
+ b = GG(b, c, d, a, x[4], S24, 0xe7d3fbc8L); /* 24 */
+ a = GG(a, b, c, d, x[9], S21, 0x21e1cde6L); /* 25 */
+ d = GG(d, a, b, c, x[14], S22, 0xc33707d6L); /* 26 */
+ c = GG(c, d, a, b, x[3], S23, 0xf4d50d87L); /* 27 */
+ b = GG(b, c, d, a, x[8], S24, 0x455a14edL); /* 28 */
+ a = GG(a, b, c, d, x[13], S21, 0xa9e3e905L); /* 29 */
+ d = GG(d, a, b, c, x[2], S22, 0xfcefa3f8L); /* 30 */
+ c = GG(c, d, a, b, x[7], S23, 0x676f02d9L); /* 31 */
+ b = GG(b, c, d, a, x[12], S24, 0x8d2a4c8aL); /* 32 */
+
+ /* Round 3 */
+ a = HH(a, b, c, d, x[5], S31, 0xfffa3942L); /* 33 */
+ d = HH(d, a, b, c, x[8], S32, 0x8771f681L); /* 34 */
+ c = HH(c, d, a, b, x[11], S33, 0x6d9d6122L); /* 35 */
+ b = HH(b, c, d, a, x[14], S34, 0xfde5380cL); /* 36 */
+ a = HH(a, b, c, d, x[1], S31, 0xa4beea44L); /* 37 */
+ d = HH(d, a, b, c, x[4], S32, 0x4bdecfa9L); /* 38 */
+ c = HH(c, d, a, b, x[7], S33, 0xf6bb4b60L); /* 39 */
+ b = HH(b, c, d, a, x[10], S34, 0xbebfbc70L); /* 40 */
+ a = HH(a, b, c, d, x[13], S31, 0x289b7ec6L); /* 41 */
+ d = HH(d, a, b, c, x[0], S32, 0xeaa127faL); /* 42 */
+ c = HH(c, d, a, b, x[3], S33, 0xd4ef3085L); /* 43 */
+ b = HH(b, c, d, a, x[6], S34, 0x4881d05L); /* 44 */
+ a = HH(a, b, c, d, x[9], S31, 0xd9d4d039L); /* 45 */
+ d = HH(d, a, b, c, x[12], S32, 0xe6db99e5L); /* 46 */
+ c = HH(c, d, a, b, x[15], S33, 0x1fa27cf8L); /* 47 */
+ b = HH(b, c, d, a, x[2], S34, 0xc4ac5665L); /* 48 */
+
+ /* Round 4 */
+ a = II(a, b, c, d, x[0], S41, 0xf4292244L); /* 49 */
+ d = II(d, a, b, c, x[7], S42, 0x432aff97L); /* 50 */
+ c = II(c, d, a, b, x[14], S43, 0xab9423a7L); /* 51 */
+ b = II(b, c, d, a, x[5], S44, 0xfc93a039L); /* 52 */
+ a = II(a, b, c, d, x[12], S41, 0x655b59c3L); /* 53 */
+ d = II(d, a, b, c, x[3], S42, 0x8f0ccc92L); /* 54 */
+ c = II(c, d, a, b, x[10], S43, 0xffeff47dL); /* 55 */
+ b = II(b, c, d, a, x[1], S44, 0x85845dd1L); /* 56 */
+ a = II(a, b, c, d, x[8], S41, 0x6fa87e4fL); /* 57 */
+ d = II(d, a, b, c, x[15], S42, 0xfe2ce6e0L); /* 58 */
+ c = II(c, d, a, b, x[6], S43, 0xa3014314L); /* 59 */
+ b = II(b, c, d, a, x[13], S44, 0x4e0811a1L); /* 60 */
+ a = II(a, b, c, d, x[4], S41, 0xf7537e82L); /* 61 */
+ d = II(d, a, b, c, x[11], S42, 0xbd3af235L); /* 62 */
+ c = II(c, d, a, b, x[2], S43, 0x2ad7d2bbL); /* 63 */
+ b = II(b, c, d, a, x[9], S44, 0xeb86d391L); /* 64 */
+
+ state[0] = plus(state[0], a);
+ state[1] = plus(state[1], b);
+ state[2] = plus(state[2], c);
+ state[3] = plus(state[3], d);
+ }
+
+ public void update(final int bytes[]) {
+ do_update(clean_bytes(bytes));
+ }
+
+ public void update(final String s) {
+ do_update(to_bytes(s));
+ }
+
+ private int[] encode(final long[] input, final int len) {
+ final int output[] = new int[len];
+ int i, j;
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (int) (input[i] & 0xff);
+ output[j + 1] = (int) (input[i] >>> 8 & 0xff);
+ output[j + 2] = (int) (input[i] >>> 16 & 0xff);
+ output[j + 3] = (int) (input[i] >>> 24 & 0xff);
+ }
+ return output;
+ }
+
+ public int[] final_bytes() {
+ final int bits[] = encode(count, 8);
+ int index, padlen;
+ int padding[], i;
+ int[] digest;
+
+ index = (int) (count[0] >>> 3 & 0x3f);
+ padlen = index < 56 ? 56 - index : 120 - index;
+ /* padlen > 0 */
+ padding = new int[padlen];
+ padding[0] = 0x80;
+ for (i = 1; i < padlen; ++i) {
+ padding[i] = 0;
+ }
+
+ do_update(padding);
+
+ do_update(bits);
+
+ digest = encode(state, 16);
+
+ return digest;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
new file mode 100644
index 0000000000..4146bd3ced
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
@@ -0,0 +1,722 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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;
+
+/**
+ *
+ * Provides a simple mechanism for exchanging messages with Erlang processes or
+ * other instances of this class.
+ *
+ * Each mailbox is associated with a unique {@link OtpErlangPid pid} that
+ * contains information necessary for delivery of messages. When sending
+ * messages to named processes or mailboxes, the sender pid is made available to
+ * the recipient of the message. When sending messages to other mailboxes, the
+ * recipient can only respond if the sender includes the pid as part of the
+ * message contents. The sender can determine his own pid by calling
+ * {@link #self self()}.
+ *
+ * Mailboxes can be named, either at creation or later. Messages can be sent to
+ * named mailboxes and named Erlang processes without knowing the
+ * {@link OtpErlangPid pid} that identifies the mailbox. This is neccessary in
+ * order to set up initial communication between parts of an application. Each
+ * mailbox can have at most one name.
+ *
+ * Since this class was intended for communication with Erlang, all of the send
+ * methods take {@link OtpErlangObject OtpErlangObject} arguments. However this
+ * class can also be used to transmit arbitrary Java objects (as long as they
+ * implement one of java.io.Serializable or java.io.Externalizable) by
+ * encapsulating the object in a {@link OtpErlangBinary OtpErlangBinary}.
+ *
+ * Messages to remote nodes are externalized for transmission, and as a result
+ * the recipient receives a copy of the original Java object. To ensure
+ * consistent behaviour when messages are sent between local mailboxes, such
+ * messages are cloned before delivery.
+ *
+ * Additionally, mailboxes can be linked in much the same way as Erlang
+ * processes. If a link is active when a mailbox is {@link #close closed}, any
+ * linked Erlang processes or OtpMboxes will be sent an exit signal. As well,
+ * exit signals will be (eventually) sent if a mailbox goes out of scope and its
+ * {@link #finalize finalize()} method called. However due to the nature of
+ * finalization (i.e. Java makes no guarantees about when {@link #finalize
+ * finalize()} will be called) it is recommended that you always explicitly
+ * close mailboxes if you are using links instead of relying on finalization to
+ * notify other parties in a timely manner.
+ *
+ * Get the identifying {@link OtpErlangPid pid} associated with this
+ * mailbox.
+ *
+ * The {@link OtpErlangPid pid} associated with this mailbox uniquely
+ * identifies the mailbox and can be used to address the mailbox. You can
+ * send the {@link OtpErlangPid pid} to a remote communicating part so that
+ * he can know where to send his response.
+ *
+ * Register or remove a name for this mailbox. Registering a name for a
+ * mailbox enables others to send messages without knowing the
+ * {@link OtpErlangPid pid} of the mailbox. A mailbox can have at most one
+ * name; if the mailbox already had a name, calling this method will
+ * supercede that name.
+ *
+ * After this operation, the mailbox will no longer be able to receive
+ * messages. Any delivered but as yet unretrieved messages can still be
+ * retrieved however.
+ *
+ * If there are links from this mailbox to other {@link OtpErlangPid pids},
+ * they will be broken when this method is called and exit signals will be
+ * sent.
+ *
+ * { self, { call, Mod, Fun, Args, user } }
+ *
+ *
+ *
+ * { self, { call, Mod, Fun, Args, user } }
+ *
+ *
+ *
+ * { rex, Term }
+ *
+ *
+ * @return the second element of the tuple if the received message is a
+ * two-tuple, otherwise null. No further error checking is
+ * performed.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ *
+ * @exception OtpErlangExit
+ * if an exit signal is received from a process on the
+ * peer node.
+ *
+ * @exception OtpAuthException
+ * if the remote node sends a message containing an
+ * invalid cookie.
+ */
+ public OtpErlangObject receiveRPC() throws IOException, OtpErlangExit,
+ OtpAuthException {
+
+ final OtpErlangObject msg = receive();
+
+ if (msg instanceof OtpErlangTuple) {
+ final OtpErlangTuple t = (OtpErlangTuple) msg;
+ if (t.arity() == 2) {
+ return t.elementAt(1); // obs: second element
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Create a link between the local node and the specified process on the
+ * remote node. If the link is still active when the remote process
+ * terminates, an exit signal will be sent to this connection. Use
+ * {@link #unlink unlink()} to remove the link.
+ *
+ * @param dest
+ * the Erlang PID of the remote process.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void link(final OtpErlangPid dest) throws IOException {
+ super.sendLink(self.pid(), dest);
+ }
+
+ /**
+ * Remove a link between the local node and the specified process on the
+ * remote node. This method deactivates links created with
+ * {@link #link link()}.
+ *
+ * @param dest
+ * the Erlang PID of the remote process.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void unlink(final OtpErlangPid dest) throws IOException {
+ super.sendUnlink(self.pid(), dest);
+ }
+
+ /**
+ * Send an exit signal to a remote process.
+ *
+ * @param dest
+ * the Erlang PID of the remote process.
+ * @param reason
+ * an Erlang term describing the exit reason.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void exit(final OtpErlangPid dest, final OtpErlangObject reason)
+ throws IOException {
+ super.sendExit2(self.pid(), dest, reason);
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java
new file mode 100644
index 0000000000..5abf6e33f7
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java
@@ -0,0 +1,244 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2009. 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.Socket;
+
+/**
+ *
+ *
+ *
+ * @see OtpConnection
+ */
+
+public class OtpErlangExit extends OtpErlangException {
+ private static final long serialVersionUID = 1L;
+
+ OtpErlangObject reason = null;
+ OtpErlangPid pid = null;
+
+ /**
+ * Create an OtpErlangExit exception with the given reason.
+ *
+ * @param reason
+ * the reason this exit signal has been sent.
+ */
+ public OtpErlangExit(final OtpErlangObject reason) {
+ super(reason.toString());
+ this.reason = reason;
+ }
+
+ /**
+ * OtpErlangExit(new
+ * OtpErlangAtom(reason)
.
+ * OtpErlangExit(new OtpErlangAtom(reason),
+ * pid)
.
+ * exit(new OtpErlangAtom(reason))
.
+ *
+ * Send an exit signal to a remote {@link OtpErlangPid pid}. This method + * does not cause any links to be broken, except indirectly if the remote + * {@link OtpErlangPid pid} exits as a result of this exit signal. + *
+ * + * @param to + * the {@link OtpErlangPid pid} to which the exit signal + * should be sent. + * + * @param reason + * an Erlang term indicating the reason for the exit. + */ + // it's called exit, but it sends exit2 + public void exit(final OtpErlangPid to, final OtpErlangObject reason) { + exit(2, to, reason); + } + + /** + *
+ * Equivalent to exit(to, new
+ * OtpErlangAtom(reason))
.
+ *
+ * Link to a remote mailbox or Erlang process. Links are idempotent, calling + * this method multiple times will not result in more than one link being + * created. + *
+ * + *+ * If the remote process subsequently exits or the mailbox is closed, a + * subsequent attempt to retrieve a message through this mailbox will cause + * an {@link OtpErlangExit OtpErlangExit} exception to be raised. Similarly, + * if the sending mailbox is closed, the linked mailbox or process will + * receive an exit signal. + *
+ * + *+ * If the remote process cannot be reached in order to set the link, the + * exception is raised immediately. + *
+ * + * @param to + * the {@link OtpErlangPid pid} representing the object to + * link to. + * + * @exception OtpErlangExit + * if the {@link OtpErlangPid pid} referred to does not + * exist or could not be reached. + * + */ + public void link(final OtpErlangPid to) throws OtpErlangExit { + try { + final String node = to.node(); + if (node.equals(home.node())) { + if (!home.deliver(new OtpMsg(OtpMsg.linkTag, self, to))) { + throw new OtpErlangExit("noproc", to); + } + } else { + final OtpCookedConnection conn = home.getConnection(node); + if (conn != null) { + conn.link(self, to); + } else { + throw new OtpErlangExit("noproc", to); + } + } + } catch (final OtpErlangExit e) { + throw e; + } catch (final Exception e) { + } + + links.addLink(self, to); + } + + /** + *+ * Remove a link to a remote mailbox or Erlang process. This method removes + * a link created with {@link #link link()}. Links are idempotent; calling + * this method once will remove all links between this mailbox and the + * remote {@link OtpErlangPid pid}. + *
+ * + * @param to + * the {@link OtpErlangPid pid} representing the object to + * unlink from. + * + */ + public void unlink(final OtpErlangPid to) { + links.removeLink(self, to); + + try { + final String node = to.node(); + if (node.equals(home.node())) { + home.deliver(new OtpMsg(OtpMsg.unlinkTag, self, to)); + } else { + final OtpCookedConnection conn = home.getConnection(node); + if (conn != null) { + conn.unlink(self, to); + } + } + } catch (final Exception e) { + } + } + + /** + *+ * Create a connection to a remote node. + *
+ * + *+ * Strictly speaking, this method is not necessary simply to set up a + * connection, since connections are created automatically first time a + * message is sent to a {@link OtpErlangPid pid} on the remote node. + *
+ * + *+ * This method makes it possible to wait for a node to come up, however, or + * check that a node is still alive. + *
+ * + *+ * This method calls a method with the same name in {@link OtpNode#ping + * Otpnode} but is provided here for convenience. + *
+ * + * @param node + * the name of the node to ping. + * + * @param timeout + * the time, in milliseconds, before reporting failure. + */ + public boolean ping(final String node, final long timeout) { + return home.ping(node, timeout); + } + + /** + *+ * Get a list of all known registered names on the same {@link OtpNode node} + * as this mailbox. + *
+ * + *+ * This method calls a method with the same name in {@link OtpNode#getNames + * Otpnode} but is provided here for convenience. + *
+ * + * @return an array of Strings containing all registered names on this + * {@link OtpNode node}. + */ + public String[] getNames() { + return home.getNames(); + } + + /** + * Determine the {@link OtpErlangPid pid} corresponding to a registered name + * on this {@link OtpNode node}. + * + *+ * This method calls a method with the same name in {@link OtpNode#whereis + * Otpnode} but is provided here for convenience. + *
+ * + * @return the {@link OtpErlangPid pid} corresponding to the registered + * name, or null if the name is not known on this node. + */ + public OtpErlangPid whereis(final String name) { + return home.whereis(name); + } + + /** + * Close this mailbox. + * + *+ * After this operation, the mailbox will no longer be able to receive + * messages. Any delivered but as yet unretrieved messages can still be + * retrieved however. + *
+ * + *+ * If there are links from this mailbox to other {@link OtpErlangPid pids}, + * they will be broken when this method is called and exit signals with + * reason 'normal' will be sent. + *
+ * + *+ * This is equivalent to {@link #exit(String) exit("normal")}. + *
+ */ + public void close() { + home.closeMbox(this); + } + + @Override + protected void finalize() { + close(); + queue.flush(); + } + + /** + * Determine if two mailboxes are equal. + * + * @return true if both Objects are mailboxes with the same identifying + * {@link OtpErlangPid pids}. + */ + @Override + public boolean equals(final Object o) { + if (!(o instanceof OtpMbox)) { + return false; + } + + final OtpMbox m = (OtpMbox) o; + return m.self.equals(self); + } + + /* + * called by OtpNode to deliver message to this mailbox. + * + * About exit and exit2: both cause exception to be raised upon receive(). + * However exit (not 2) causes any link to be removed as well, while exit2 + * leaves any links intact. + */ + void deliver(final OtpMsg m) { + switch (m.type()) { + case OtpMsg.linkTag: + links.addLink(self, m.getSenderPid()); + break; + + case OtpMsg.unlinkTag: + links.removeLink(self, m.getSenderPid()); + break; + + case OtpMsg.exitTag: + links.removeLink(self, m.getSenderPid()); + queue.put(m); + break; + + case OtpMsg.exit2Tag: + default: + queue.put(m); + break; + } + } + + // used to break all known links to this mbox + void breakLinks(final OtpErlangObject reason) { + final Link[] l = links.clearLinks(); + + if (l != null) { + final int len = l.length; + + for (int i = 0; i < len; i++) { + exit(1, l[i].remote(), reason); + } + } + } +} diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java new file mode 100644 index 0000000000..80d8a5ccae --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java @@ -0,0 +1,291 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2000-2009. 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; + +/** + *+ * Provides a carrier for Erlang messages. + *
+ * + *+ * Instances of this class are created to package header and payload information + * in received Erlang messages so that the recipient can obtain both parts with + * a single call to {@link OtpMbox#receiveMsg receiveMsg()}. + *
+ * + *
+ * The header information that is available is as follows:
+ * Message are sent using the Erlang external format (see separate + * documentation). When a message is received and delivered to the recipient + * {@link OtpMbox mailbox}, the body of the message is still in this external + * representation until {@link #getMsg getMsg()} is called, at which point the + * message is decoded. A copy of the decoded message is stored in the OtpMsg so + * that subsequent calls to {@link #getMsg getMsg()} do not require that the + * message be decoded a second time. + *
+ */ +public class OtpMsg { + public static final int linkTag = 1; + public static final int sendTag = 2; + public static final int exitTag = 3; + public static final int unlinkTag = 4; + /* public static final int nodeLinkTag = 5; */ + public static final int regSendTag = 6; + /* public static final int groupLeaderTag = 7; */ + public static final int exit2Tag = 8; + + protected int tag; // what type of message is this (send, link, exit etc) + protected OtpInputStream paybuf; + protected OtpErlangObject payload; + + protected OtpErlangPid from; + protected OtpErlangPid to; + protected String toName; + + // send has receiver pid but no sender information + OtpMsg(final OtpErlangPid to, final OtpInputStream paybuf) { + tag = sendTag; + from = null; + this.to = to; + toName = null; + this.paybuf = paybuf; + payload = null; + } + + // send has receiver pid but no sender information + OtpMsg(final OtpErlangPid to, final OtpErlangObject payload) { + tag = sendTag; + from = null; + this.to = to; + toName = null; + paybuf = null; + this.payload = payload; + } + + // send_reg has sender pid and receiver name + OtpMsg(final OtpErlangPid from, final String toName, + final OtpInputStream paybuf) { + tag = regSendTag; + this.from = from; + this.toName = toName; + to = null; + this.paybuf = paybuf; + payload = null; + } + + // send_reg has sender pid and receiver name + OtpMsg(final OtpErlangPid from, final String toName, + final OtpErlangObject payload) { + tag = regSendTag; + this.from = from; + this.toName = toName; + to = null; + paybuf = null; + this.payload = payload; + } + + // exit (etc) has from, to, reason + OtpMsg(final int tag, final OtpErlangPid from, final OtpErlangPid to, + final OtpErlangObject reason) { + this.tag = tag; + this.from = from; + this.to = to; + paybuf = null; + payload = reason; + } + + // special case when reason is an atom (i.e. most of the time) + OtpMsg(final int tag, final OtpErlangPid from, final OtpErlangPid to, + final String reason) { + this.tag = tag; + this.from = from; + this.to = to; + paybuf = null; + payload = new OtpErlangAtom(reason); + } + + // other message types (link, unlink) + OtpMsg(int tag, final OtpErlangPid from, final OtpErlangPid to) { + // convert TT-tags to equiv non-TT versions + if (tag > 10) { + tag -= 10; + } + + this.tag = tag; + this.from = from; + this.to = to; + } + + /** + * Get the payload from this message without deserializing it. + * + * @return the serialized Erlang term contained in this message. + * + */ + OtpInputStream getMsgBuf() { + return paybuf; + } + + /** + *+ * Get the type marker from this message. The type marker identifies the + * type of message. Valid values are the ``tag'' constants defined in this + * class. + *
+ * + *+ * The tab identifies not only the type of message but also the content of + * the OtpMsg object, since different messages have different components, as + * follows: + *
+ * + *+ * Deserialize and return a new copy of the message contained in this + * OtpMsg. + *
+ * + *+ * The first time this method is called the actual payload is deserialized + * and the Erlang term is created. Calling this method subsequent times will + * not cuase the message to be deserialized additional times, instead the + * same Erlang term object will be returned. + *
+ * + * @return an Erlang term. + * + * @exception OtpErlangDecodeException + * if the byte stream could not be deserialized. + * + */ + public OtpErlangObject getMsg() throws OtpErlangDecodeException { + if (payload == null) { + payload = paybuf.read_any(); + } + return payload; + } + + /** + *+ * Get the name of the recipient for this message. + *
+ * + *+ * Messages are sent to Pids or names. If this message was sent to a name + * then the name is returned by this method. + *
+ * + * @return the name of the recipient, or null if the recipient was in fact a + * Pid. + */ + public String getRecipientName() { + return toName; + } + + /** + *+ * Get the Pid of the recipient for this message, if it is a sendTag + * message. + *
+ * + *+ * Messages are sent to Pids or names. If this message was sent to a Pid + * then the Pid is returned by this method. The recipient Pid is also + * available for link, unlink and exit messages. + *
+ * + * @return the Pid of the recipient, or null if the recipient was in fact a + * name. + */ + public OtpErlangPid getRecipientPid() { + return to; + } + + /** + *+ * Get the name of the recipient for this message, if it is a regSendTag + * message. + *
+ * + *+ * Messages are sent to Pids or names. If this message was sent to a name + * then the name is returned by this method. + *
+ * + * @return the Pid of the recipient, or null if the recipient was in fact a + * name. + */ + public Object getRecipient() { + if (toName != null) { + return toName; + } + return to; + } + + /** + *+ * Get the Pid of the sender of this message. + *
+ * + *+ * For messages sent to names, the Pid of the sender is included with the + * message. The sender Pid is also available for link, unlink and exit + * messages. It is not available for sendTag messages sent to Pids. + *
+ * + * @return the Pid of the sender, or null if it was not available. + */ + public OtpErlangPid getSenderPid() { + return from; + } +} diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java new file mode 100644 index 0000000000..d499fae3fb --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java @@ -0,0 +1,807 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2000-2009. 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.lang.ref.WeakReference; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; + +/** + *+ * Represents a local OTP node. This class is used when you do not wish to + * manage connections yourself - outgoing connections are established as needed, + * and incoming connections accepted automatically. This class supports the use + * of a mailbox API for communication, while management of the underlying + * communication mechanism is automatic and hidden from the application + * programmer. + *
+ * + *+ * Once an instance of this class has been created, obtain one or more mailboxes + * in order to send or receive messages. The first message sent to a given node + * will cause a connection to be set up to that node. Any messages received will + * be delivered to the appropriate mailboxes. + *
+ * + *+ * To shut down the node, call {@link #close close()}. This will prevent the + * node from accepting additional connections and it will cause all existing + * connections to be closed. Any unread messages in existing mailboxes can still + * be read, however no new messages will be delivered to the mailboxes. + *
+ * + *+ * Note that the use of this class requires that Epmd (Erlang Port Mapper + * Daemon) is running on each cooperating host. This class does not start Epmd + * automatically as Erlang does, you must start it manually or through some + * other means. See the Erlang documentation for more information about this. + *
+ */ +public class OtpNode extends OtpLocalNode { + private boolean initDone = false; + + // thread to manage incoming connections + private Acceptor acceptor = null; + + // keep track of all connections + Hashtable+ * 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". + *
+ * + *+ * If the file does not exist, an empty string is used. This method makes no + * attempt to create the file. + *
+ * + * @param node + * the name of this node. + * + * @exception IOException + * if communication could not be initialized. + * + */ + public OtpNode(final String node) throws IOException { + this(node, defaultCookie, 0); + } + + /** + * 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. + * + * @exception IOException + * if communication could not be initialized. + * + */ + public OtpNode(final String node, final String cookie) throws IOException { + this(node, cookie, 0); + } + + /** + * 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. + * + * @exception IOException + * if communication could not be initialized. + * + */ + public OtpNode(final String node, final String cookie, final int port) + throws IOException { + super(node, cookie); + + init(port); + } + + private synchronized void init(final int port) throws IOException { + if (!initDone) { + connections = new Hashtable+ * After this operation, the mailbox will no longer be able to + * receive messages. Any delivered but as yet unretrieved + * messages can still be retrieved however. + *
+ * + *+ * If there are links from the mailbox to other + * {@link OtpErlangPid pids}, they will be broken when this + * method is called and exit signals with reason 'normal' will be + * sent. + *
+ * + */ + public void closeMbox(final OtpMbox mbox) { + closeMbox(mbox, new OtpErlangAtom("normal")); + } + + /** + * Close the specified mailbox with the given reason. + * + * @param mbox + * the mailbox to close. + * @param reason + * an Erlang term describing the reason for the termination. + * + *+ * After this operation, the mailbox will no longer be able to + * receive messages. Any delivered but as yet unretrieved + * messages can still be retrieved however. + *
+ * + *+ * If there are links from the mailbox to other + * {@link OtpErlangPid pids}, they will be broken when this + * method is called and exit signals with the given reason will + * be sent. + *
+ * + */ + public void closeMbox(final OtpMbox mbox, final OtpErlangObject reason) { + if (mbox != null) { + mboxes.remove(mbox); + mbox.name = null; + mbox.breakLinks(reason); + } + } + + /** + * Create an named mailbox that can be used to send and receive messages + * with other, similar mailboxes and with Erlang processes. Messages can be + * sent to this mailbox by using its registered name or the associated + * {@link OtpMbox#self pid}. + * + * @param name + * a name to register for this mailbox. The name must be unique + * within this OtpNode. + * + * @return a mailbox, or null if the name was already in use. + * + */ + public OtpMbox createMbox(final String name) { + return mboxes.create(name); + } + + /** + *+ * Register or remove a name for the given mailbox. Registering a name for a + * mailbox enables others to send messages without knowing the + * {@link OtpErlangPid pid} of the mailbox. A mailbox can have at most one + * name; if the mailbox already had a name, calling this method will + * supercede that name. + *
+ * + * @param name + * the name to register for the mailbox. Specify null to + * unregister the existing name from this mailbox. + * + * @param mbox + * the mailbox to associate with the name. + * + * @return true if the name was available, or false otherwise. + */ + public boolean registerName(final String name, final OtpMbox mbox) { + return mboxes.register(name, mbox); + } + + /** + * Get a list of all known registered names on this node. + * + * @return an array of Strings, containins all known registered names on + * this node. + */ + + public String[] getNames() { + return mboxes.names(); + } + + /** + * Determine the {@link OtpErlangPid pid} corresponding to a registered name + * on this node. + * + * @return the {@link OtpErlangPid pid} corresponding to the registered + * name, or null if the name is not known on this node. + */ + public OtpErlangPid whereis(final String name) { + final OtpMbox m = mboxes.get(name); + if (m != null) { + return m.self(); + } + return null; + } + + /** + * Register interest in certain system events. The {@link OtpNodeStatus + * OtpNodeStatus} handler object contains callback methods, that will be + * called when certain events occur. + * + * @param handler + * the callback object to register. To clear the handler, specify + * null as the handler to use. + * + */ + public synchronized void registerStatusHandler(final OtpNodeStatus handler) { + this.handler = handler; + } + + /** + *+ * Determine if another node is alive. This method has the side effect of + * setting up a connection to the remote node (if possible). Only a single + * outgoing message is sent; the timeout is how long to wait for a response. + *
+ * + *+ * Only a single attempt is made to connect to the remote node, so for + * example it is not possible to specify an extremely long timeout and + * expect to be notified when the node eventually comes up. If you wish to + * wait for a remote node to be started, the following construction may be + * useful: + *
+ * + *+ * // ping every 2 seconds until positive response + * while (!me.ping(him, 2000)) + * ; + *+ * + * @param node + * the name of the node to ping. + * + * @param timeout + * the time, in milliseconds, to wait for response before + * returning false. + * + * @return true if the node was alive and the correct ping response was + * returned. false if the correct response was not returned on time. + */ + /* + * internal info about the message formats... + * + * the request: -> REG_SEND {6,#Pid
+ * Provides a callback mechanism for receiving status change information about + * other nodes in the system. Register an instance of this class (or a subclass) + * with your {@link OtpNode OtpNode} when you wish to be notified about such + * status changes and other similar events. + *
+ * + *+ * This class provides default handers that ignore all events. Applications are + * expected to extend this class in order to act on events that are deemed + * interesting. + *
+ * + *+ * Note that this class is likely to change in the near future + *
+ */ +public class OtpNodeStatus { + public OtpNodeStatus() { + } + + /** + * Notify about remote node status changes. + * + * @param node + * the node whose status change is being indicated by this + * call. + * + * @param up + * true if the node has come up, false if it has gone down. + * + * @param info + * additional info that may be available, for example an + * exception that was raised causing the event in question + * (may be null). + * + */ + public void remoteStatus(final String node, final boolean up, + final Object info) { + } + + /** + * Notify about local node exceptions. + * + * @param node + * the node whose status change is being indicated by this + * call. + * + * @param up + * true if the node has come up, false if it has gone down. + * + * @param info + * additional info that may be available, for example an + * exception that was raised causing the event in question + * (may be null). + */ + public void localStatus(final String node, final boolean up, + final Object info) { + } + + /** + * Notify about failed connection attempts. + * + * @param node + * The name of the remote node + * + * @param incoming + * The direction of the connection attempt, i.e. true for + * incoming, false for outgoing. + * + * @param info + * additional info that may be available, for example an + * exception that was raised causing the event in question + * (may be null). + */ + public void connAttempt(final String node, final boolean incoming, + final Object info) { + } +} diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java new file mode 100644 index 0000000000..181350100f --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java @@ -0,0 +1,816 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2000-2009. 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.OutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; + +/** + * Provides a stream for encoding Erlang terms to external format, for + * transmission or storage. + * + *+ * Note that this class is not synchronized, if you need synchronization you + * must provide it yourself. + * + */ +public class OtpOutputStream extends ByteArrayOutputStream { + /** The default initial size of the stream. * */ + public static final int defaultInitialSize = 2048; + + /** The default increment used when growing the stream. * */ + public static final int defaultIncrement = 2048; + + // static formats, used to encode floats and doubles + private static final DecimalFormat eform = new DecimalFormat("e+00;e-00"); + private static final BigDecimal ten = new BigDecimal(10.0); + private static final BigDecimal one = new BigDecimal(1.0); + + /** + * Create a stream with the default initial size (2048 bytes). + */ + public OtpOutputStream() { + this(defaultInitialSize); + } + + /** + * Create a stream with the specified initial size. + */ + public OtpOutputStream(final int size) { + super(size); + } + + /** + * Create a stream containing the encoded version of the given Erlang term. + */ + public OtpOutputStream(final OtpErlangObject o) { + this(); + write_any(o); + } + + // package scope + /* + * Get the contents of the output stream as an input stream instead. This is + * used internally in {@link OtpCconnection} for tracing outgoing packages. + * + * @param offset where in the output stream to read data from when creating + * the input stream. The offset is necessary because header contents start 5 + * bytes into the header buffer, whereas payload contents start at the + * beginning + * + * @return an input stream containing the same raw data. + */ + OtpInputStream getOtpInputStream(final int offset) { + return new OtpInputStream(super.buf, offset, super.count - offset, 0); + } + + /** + * Get the current position in the stream. + * + * @return the current position in the stream. + */ + public int getPos() { + return super.count; + } + + /** + * Write one byte to the stream. + * + * @param b + * the byte to write. + * + */ + public void write(final byte b) { + if (super.count >= super.buf.length) { + // System.err.println("Expanding buffer from " + this.buf.length + // + " to " + (this.buf.length+defaultIncrement)); + final byte[] tmp = new byte[super.buf.length + defaultIncrement]; + System.arraycopy(super.buf, 0, tmp, 0, super.count); + super.buf = tmp; + } + super.buf[super.count++] = b; + } + + /** + * Write an array of bytes to the stream. + * + * @param buf + * the array of bytes to write. + * + */ + + @Override + public void write(final byte[] buf) { + if (super.count + buf.length > super.buf.length) { + // System.err.println("Expanding buffer from " + super.buf.length + // + " to " + (buf.length + super.buf.lengt + defaultIncrement)); + final byte[] tmp = new byte[super.buf.length + buf.length + + defaultIncrement]; + System.arraycopy(super.buf, 0, tmp, 0, super.count); + super.buf = tmp; + } + System.arraycopy(buf, 0, super.buf, super.count, buf.length); + super.count += buf.length; + } + + /** + * Write the low byte of a value to the stream. + * + * @param n + * the value to use. + * + */ + public void write1(final long n) { + write((byte) (n & 0xff)); + } + + /** + * Write an array of bytes to the stream. + * + * @param buf + * the array of bytes to write. + * + */ + public void writeN(final byte[] bytes) { + write(bytes); + } + + /** + * Get the current capacity of the stream. As bytes are added the capacity + * of the stream is increased automatically, however this method returns the + * current size. + * + * @return the size of the internal buffer used by the stream. + */ + public int length() { + return super.buf.length; + } + + /** + * Get the number of bytes in the stream. + * + * @return the number of bytes in the stream. + * + * @deprecated As of Jinterface 1.4, replaced by super.size(). + * @see #size() + */ + + @Deprecated + public int count() { + return count; + } + + /** + * Write the low two bytes of a value to the stream in big endian order. + * + * @param n + * the value to use. + */ + public void write2BE(final long n) { + write((byte) ((n & 0xff00) >> 8)); + write((byte) (n & 0xff)); + } + + /** + * Write the low four bytes of a value to the stream in big endian order. + * + * @param n + * the value to use. + */ + public void write4BE(final long n) { + write((byte) ((n & 0xff000000) >> 24)); + write((byte) ((n & 0xff0000) >> 16)); + write((byte) ((n & 0xff00) >> 8)); + write((byte) (n & 0xff)); + } + + /** + * Write the low eight (all) bytes of a value to the stream in big endian + * order. + * + * @param n + * the value to use. + */ + public void write8BE(final long n) { + write((byte) (n >> 56 & 0xff)); + write((byte) (n >> 48 & 0xff)); + write((byte) (n >> 40 & 0xff)); + write((byte) (n >> 32 & 0xff)); + write((byte) (n >> 24 & 0xff)); + write((byte) (n >> 16 & 0xff)); + write((byte) (n >> 8 & 0xff)); + write((byte) (n & 0xff)); + } + + /** + * Write any number of bytes in little endian format. + * + * @param n + * the value to use. + * @param b + * the number of bytes to write from the little end. + */ + public void writeLE(long n, final int b) { + for (int i = 0; i < b; i++) { + write((byte) (n & 0xff)); + n >>= 8; + } + } + + /** + * Write the low two bytes of a value to the stream in little endian order. + * + * @param n + * the value to use. + */ + public void write2LE(final long n) { + write((byte) (n & 0xff)); + write((byte) ((n & 0xff00) >> 8)); + } + + /** + * Write the low four bytes of a value to the stream in little endian order. + * + * @param n + * the value to use. + */ + public void write4LE(final long n) { + write((byte) (n & 0xff)); + write((byte) ((n & 0xff00) >> 8)); + write((byte) ((n & 0xff0000) >> 16)); + write((byte) ((n & 0xff000000) >> 24)); + } + + /** + * Write the low eight bytes of a value to the stream in little endian + * order. + * + * @param n + * the value to use. + */ + public void write8LE(final long n) { + write((byte) (n & 0xff)); + write((byte) (n >> 8 & 0xff)); + write((byte) (n >> 16 & 0xff)); + write((byte) (n >> 24 & 0xff)); + write((byte) (n >> 32 & 0xff)); + write((byte) (n >> 40 & 0xff)); + write((byte) (n >> 48 & 0xff)); + write((byte) (n >> 56 & 0xff)); + } + + /** + * Write the low four bytes of a value to the stream in bif endian order, at + * the specified position. If the position specified is beyond the end of + * the stream, this method will have no effect. + * + * Normally this method should be used in conjunction with {@link #size() + * size()}, when is is necessary to insert data into the stream before it is + * known what the actual value should be. For example: + * + *
+ * int pos = s.size(); + * s.write4BE(0); // make space for length data, + * // but final value is not yet known + * [ ...more write statements...] + * // later... when we know the length value + * s.poke4BE(pos, length); + *+ * + * + * @param offset + * the position in the stream. + * @param n + * the value to use. + */ + public void poke4BE(final int offset, final long n) { + if (offset < super.count) { + buf[offset + 0] = (byte) ((n & 0xff000000) >> 24); + buf[offset + 1] = (byte) ((n & 0xff0000) >> 16); + buf[offset + 2] = (byte) ((n & 0xff00) >> 8); + buf[offset + 3] = (byte) (n & 0xff); + } + } + + /** + * Write a string to the stream as an Erlang atom. + * + * @param atom + * the string to write. + */ + public void write_atom(final String atom) { + write1(OtpExternal.atomTag); + write2BE(atom.length()); + writeN(atom.getBytes()); + } + + /** + * Write an array of bytes to the stream as an Erlang binary. + * + * @param bin + * the array of bytes to write. + */ + public void write_binary(final byte[] bin) { + write1(OtpExternal.binTag); + write4BE(bin.length); + writeN(bin); + } + + /** + * Write an array of bytes to the stream as an Erlang bitstr. + * + * @param bin + * the array of bytes to write. + * @param pad_bits + * the number of zero pad bits at the low end of the last byte + */ + public void write_bitstr(final byte[] bin, final int pad_bits) { + if (pad_bits == 0) { + write_binary(bin); + return; + } + write1(OtpExternal.bitBinTag); + write4BE(bin.length); + write1(8 - pad_bits); + writeN(bin); + } + + /** + * Write a boolean value to the stream as the Erlang atom 'true' or 'false'. + * + * @param b + * the boolean value to write. + */ + public void write_boolean(final boolean b) { + write_atom(String.valueOf(b)); + } + + /** + * Write a single byte to the stream as an Erlang integer. The byte is + * really an IDL 'octet', that is, unsigned. + * + * @param b + * the byte to use. + */ + public void write_byte(final byte b) { + this.write_long(b & 0xffL, true); + } + + /** + * Write a character to the stream as an Erlang integer. The character may + * be a 16 bit character, kind of IDL 'wchar'. It is up to the Erlang side + * to take care of souch, if they should be used. + * + * @param c + * the character to use. + */ + public void write_char(final char c) { + this.write_long(c & 0xffffL, true); + } + + /** + * Write a double value to the stream. + * + * @param d + * the double to use. + */ + public void write_double(final double d) { + write1(OtpExternal.newFloatTag); + write8BE(Double.doubleToLongBits(d)); + } + + /** + * Write a float value to the stream. + * + * @param f + * the float to use. + */ + public void write_float(final float f) { + write_double(f); + } + + public void write_big_integer(BigInteger v) { + if (v.bitLength() < 64) { + this.write_long(v.longValue(), true); + return; + } + final int signum = v.signum(); + if (signum < 0) { + v = v.negate(); + } + final byte[] magnitude = v.toByteArray(); + final int n = magnitude.length; + // Reverse the array to make it little endian. + for (int i = 0, j = n; i < j--; i++) { + // Swap [i] with [j] + final byte b = magnitude[i]; + magnitude[i] = magnitude[j]; + magnitude[j] = b; + } + if ((n & 0xFF) == n) { + write1(OtpExternal.smallBigTag); + write1(n); // length + } else { + write1(OtpExternal.largeBigTag); + write4BE(n); // length + } + write1(signum < 0 ? 1 : 0); // sign + // Write the array + writeN(magnitude); + } + + void write_long(final long v, final boolean unsigned) { + /* + * If v<0 and unsigned==true the value + * java.lang.Long.MAX_VALUE-java.lang.Long.MIN_VALUE+1+v is written, i.e + * v is regarded as unsigned two's complement. + */ + if ((v & 0xffL) == v) { + // will fit in one byte + write1(OtpExternal.smallIntTag); + write1(v); + } else { + // note that v != 0L + if (v < 0 && unsigned || v < OtpExternal.erlMin + || v > OtpExternal.erlMax) { + // some kind of bignum + final long abs = unsigned ? v : v < 0 ? -v : v; + final int sign = unsigned ? 0 : v < 0 ? 1 : 0; + int n; + long mask; + for (mask = 0xFFFFffffL, n = 4; (abs & mask) != abs; n++, mask = mask << 8 | 0xffL) { + ; // count nonzero bytes + } + write1(OtpExternal.smallBigTag); + write1(n); // length + write1(sign); // sign + writeLE(abs, n); // value. obs! little endian + } else { + write1(OtpExternal.intTag); + write4BE(v); + } + } + } + + /** + * Write a long to the stream. + * + * @param l + * the long to use. + */ + public void write_long(final long l) { + this.write_long(l, false); + } + + /** + * Write a positive long to the stream. The long is interpreted as a two's + * complement unsigned long even if it is negative. + * + * @param ul + * the long to use. + */ + public void write_ulong(final long ul) { + this.write_long(ul, true); + } + + /** + * Write an integer to the stream. + * + * @param i + * the integer to use. + */ + public void write_int(final int i) { + this.write_long(i, false); + } + + /** + * Write a positive integer to the stream. The integer is interpreted as a + * two's complement unsigned integer even if it is negative. + * + * @param ui + * the integer to use. + */ + public void write_uint(final int ui) { + this.write_long(ui & 0xFFFFffffL, true); + } + + /** + * Write a short to the stream. + * + * @param s + * the short to use. + */ + public void write_short(final short s) { + this.write_long(s, false); + } + + /** + * Write a positive short to the stream. The short is interpreted as a two's + * complement unsigned short even if it is negative. + * + * @param s + * the short to use. + */ + public void write_ushort(final short us) { + this.write_long(us & 0xffffL, true); + } + + /** + * Write an Erlang list header to the stream. After calling this method, you + * must write 'arity' elements to the stream followed by nil, or it will not + * be possible to decode it later. + * + * @param arity + * the number of elements in the list. + */ + public void write_list_head(final int arity) { + if (arity == 0) { + write_nil(); + } else { + write1(OtpExternal.listTag); + write4BE(arity); + } + } + + /** + * Write an empty Erlang list to the stream. + */ + public void write_nil() { + write1(OtpExternal.nilTag); + } + + /** + * Write an Erlang tuple header to the stream. After calling this method, + * you must write 'arity' elements to the stream or it will not be possible + * to decode it later. + * + * @param arity + * the number of elements in the tuple. + */ + public void write_tuple_head(final int arity) { + if (arity < 0xff) { + write1(OtpExternal.smallTupleTag); + write1(arity); + } else { + write1(OtpExternal.largeTupleTag); + write4BE(arity); + } + } + + /** + * Write an Erlang PID to the stream. + * + * @param node + * the nodename. + * + * @param id + * an arbitrary number. Only the low order 15 bits will be used. + * + * @param serial + * another arbitrary number. Only the low order 13 bits will be + * used. + * + * @param creation + * yet another arbitrary number. Only the low order 2 bits will + * be used. + * + */ + public void write_pid(final String node, final int id, final int serial, + final int creation) { + write1(OtpExternal.pidTag); + write_atom(node); + write4BE(id & 0x7fff); // 15 bits + write4BE(serial & 0x1fff); // 13 bits + write1(creation & 0x3); // 2 bits + } + + /** + * Write an Erlang port to the stream. + * + * @param node + * the nodename. + * + * @param id + * an arbitrary number. Only the low order 28 bits will be used. + * + * @param creation + * another arbitrary number. Only the low order 2 bits will be + * used. + * + */ + public void write_port(final String node, final int id, final int creation) { + write1(OtpExternal.portTag); + write_atom(node); + write4BE(id & 0xfffffff); // 28 bits + write1(creation & 0x3); // 2 bits + } + + /** + * Write an old style Erlang ref to the stream. + * + * @param node + * the nodename. + * + * @param id + * an arbitrary number. Only the low order 18 bits will be used. + * + * @param creation + * another arbitrary number. Only the low order 2 bits will be + * used. + * + */ + public void write_ref(final String node, final int id, final int creation) { + write1(OtpExternal.refTag); + write_atom(node); + write4BE(id & 0x3ffff); // 18 bits + write1(creation & 0x3); // 2 bits + } + + /** + * Write a new style (R6 and later) Erlang ref to the stream. + * + * @param node + * the nodename. + * + * @param ids + * an array of arbitrary numbers. Only the low order 18 bits of + * the first number will be used. If the array contains only one + * number, an old style ref will be written instead. At most + * three numbers will be read from the array. + * + * @param creation + * another arbitrary number. Only the low order 2 bits will be + * used. + * + */ + public void write_ref(final String node, final int[] ids, final int creation) { + int arity = ids.length; + if (arity > 3) { + arity = 3; // max 3 words in ref + } + + if (arity == 1) { + // use old method + this.write_ref(node, ids[0], creation); + } else { + // r6 ref + write1(OtpExternal.newRefTag); + + // how many id values + write2BE(arity); + + write_atom(node); + + // note: creation BEFORE id in r6 ref + write1(creation & 0x3); // 2 bits + + // first int gets truncated to 18 bits + write4BE(ids[0] & 0x3ffff); + + // remaining ones are left as is + for (int i = 1; i < arity; i++) { + write4BE(ids[i]); + } + } + } + + /** + * Write a string to the stream. + * + * @param s + * the string to write. + */ + public void write_string(final String s) { + final int len = s.length(); + + switch (len) { + case 0: + write_nil(); + break; + default: + if (len <= 65535 && is8bitString(s)) { // 8-bit string + try { + final byte[] bytebuf = s.getBytes("ISO-8859-1"); + write1(OtpExternal.stringTag); + write2BE(len); + writeN(bytebuf); + } catch (final UnsupportedEncodingException e) { + write_nil(); // it should never ever get here... + } + } else { // unicode or longer, must code as list + final char[] charbuf = s.toCharArray(); + final int[] codePoints = OtpErlangString.stringToCodePoints(s); + write_list_head(codePoints.length); + for (final int codePoint : codePoints) { + write_int(codePoint); + } + write_nil(); + } + } + } + + private boolean is8bitString(final String s) { + for (int i = 0; i < s.length(); ++i) { + final char c = s.charAt(i); + if (c < 0 || c > 255) { + return false; + } + } + return true; + } + + /** + * Write an arbitrary Erlang term to the stream in compressed format. + * + * @param o + * the Erlang tem to write. + */ + public void write_compressed(final OtpErlangObject o) { + final OtpOutputStream oos = new OtpOutputStream(o); + write1(OtpExternal.compressedTag); + write4BE(oos.size()); + final java.io.FilterOutputStream fos = new java.io.FilterOutputStream( + this); + final java.util.zip.DeflaterOutputStream dos = new java.util.zip.DeflaterOutputStream( + fos); + try { + oos.writeTo(dos); + dos.close(); + } catch (final IOException e) { + throw new java.lang.IllegalArgumentException( + "Intremediate stream failed for Erlang object " + o); + } + } + + /** + * Write an arbitrary Erlang term to the stream. + * + * @param o + * the Erlang term to write. + */ + public void write_any(final OtpErlangObject o) { + // calls one of the above functions, depending on o + o.encode(this); + } + + public void write_fun(final OtpErlangPid pid, final String module, + final long old_index, final int arity, final byte[] md5, + final long index, final long uniq, final OtpErlangObject[] freeVars) { + if (arity == -1) { + write1(OtpExternal.funTag); + write4BE(freeVars.length); + pid.encode(this); + write_atom(module); + write_long(index); + write_long(uniq); + for (final OtpErlangObject fv : freeVars) { + fv.encode(this); + } + } else { + write1(OtpExternal.newFunTag); + final int saveSizePos = getPos(); + write4BE(0); // this is where we patch in the size + write1(arity); + writeN(md5); + write4BE(index); + write4BE(freeVars.length); + write_atom(module); + write_long(old_index); + write_long(uniq); + pid.encode(this); + for (final OtpErlangObject fv : freeVars) { + fv.encode(this); + } + poke4BE(saveSizePos, getPos() - saveSizePos); + } + } + + public void write_external_fun(final String module, final String function, + final int arity) { + write1(OtpExternal.externalFunTag); + write_atom(module); + write_atom(function); + write_long(arity); + } +} diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java new file mode 100644 index 0000000000..df5ce61820 --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java @@ -0,0 +1,86 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2000-2009. 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.UnknownHostException; + +/** + * Represents a remote OTP node. It acts only as a container for the nodename + * and other node-specific information that is needed by the + * {@link OtpConnection} class. + */ +public class OtpPeer extends AbstractNode { + int distChoose = 0; /* + * this is set by OtpConnection and is the highest + * common protocol version we both support + */ + + OtpPeer() { + super(); + } + + /** + * Create a peer node. + * + * @param node + * the name of the node. + */ + public OtpPeer(final String node) { + super(node); + } + + /** + * Create a connection to a remote node. + * + * @param self + * the local node from which you wish to connect. + * + * @return a connection to the remote node. + * + * @exception java.net.UnknownHostException + * if the remote host could not be found. + * + * @exception java.io.IOException + * if it was not possible to connect to the remote node. + * + * @exception OtpAuthException + * if the connection was refused by the remote node. + * + * @deprecated Use the corresponding method in {@link OtpSelf} instead. + */ + @Deprecated + public OtpConnection connect(final OtpSelf self) throws IOException, + UnknownHostException, OtpAuthException { + return new OtpConnection(self, this); + } + + // package + /* + * Get the port number used by the remote node. + * + * @return the port number used by the remote node, or 0 if the node was not + * registered with the port mapper. + * + * @exception java.io.IOException if the port mapper could not be contacted. + */ + int port() throws IOException { + return OtpEpmd.lookupPort(this); + } +} diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java new file mode 100644 index 0000000000..8e78cda894 --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java @@ -0,0 +1,221 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2000-2009. 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; +import java.net.UnknownHostException; + +/** + * Represents an OTP node. It is used to connect to remote nodes or accept + * incoming connections from remote nodes. + * + *
+ * When the Java node will be connecting to a remote Erlang, Java or C node, it + * must first identify itself as a node by creating an instance of this class, + * after which it may connect to the remote node. + * + *
+ * When you create an instance of this class, it will bind a socket to a port so + * that incoming connections can be accepted. However the port number will not + * be made available to other nodes wishing to connect until you explicitely + * register with the port mapper daemon by calling {@link #publishPort()}. + *
+ * + *+ * OtpSelf self = new OtpSelf("client", "authcookie"); // identify self + * OtpPeer other = new OtpPeer("server"); // identify peer + * + * OtpConnection conn = self.connect(other); // connect to peer + *+ * + */ +public class OtpSelf extends OtpLocalNode { + private final ServerSocket sock; + private final OtpErlangPid pid; + + /** + *
+ * Create a self 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". + *
+ * + *+ * If the file does not exist, an empty string is used. This method makes no + * attempt to create the file. + *
+ * + * @param node + * the name of this node. + * + */ + public OtpSelf(final String node) throws IOException { + this(node, defaultCookie, 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. + */ + public OtpSelf(final String node, final String cookie) throws IOException { + this(node, cookie, 0); + } + + public OtpSelf(final String node, final String cookie, final int port) + throws IOException { + super(node, cookie); + + sock = new ServerSocket(port); + + if (port != 0) { + this.port = port; + } else { + this.port = sock.getLocalPort(); + } + + pid = createPid(); + } + + /** + * Get the Erlang PID that will be used as the sender id in all "anonymous" + * messages sent by this node. Anonymous messages are those sent via send + * methods in {@link OtpConnection OtpConnection} that do not specify a + * sender. + * + * @return the Erlang PID that will be used as the sender id in all + * anonymous messages sent by this node. + */ + public OtpErlangPid pid() { + return pid; + } + + /** + * Make public the information needed by remote nodes that may wish to + * connect to this one. This method establishes a connection to the Erlang + * port mapper (Epmd) and registers the server node's name and port so that + * remote nodes are able to connect. + * + *+ * This method will fail if an Epmd process is not running on the localhost. + * See the Erlang documentation for information about starting Epmd. + * + *
+ * Note that once this method has been called, the node is expected to be + * available to accept incoming connections. For that reason you should make + * sure that you call {@link #accept()} shortly after calling + * {@link #publishPort()}. When you no longer intend to accept connections + * you should call {@link #unPublishPort()}. + * + * @return true if the operation was successful, false if the node was + * already registered. + * + * @exception java.io.IOException + * if the port mapper could not be contacted. + */ + public boolean publishPort() throws IOException { + if (getEpmd() != null) { + return false; // already published + } + + OtpEpmd.publishPort(this); + return getEpmd() != null; + } + + /** + * Unregister the server node's name and port number from the Erlang port + * mapper, thus preventing any new connections from remote nodes. + */ + public void unPublishPort() { + // unregister with epmd + OtpEpmd.unPublishPort(this); + + // close the local descriptor (if we have one) + try { + if (super.epmd != null) { + super.epmd.close(); + } + } catch (final IOException e) {/* ignore close errors */ + } + super.epmd = null; + } + + /** + * Accept an incoming connection from a remote node. A call to this method + * will block until an incoming connection is at least attempted. + * + * @return a connection to a remote node. + * + * @exception java.io.IOException + * if a remote node attempted to connect but no common + * protocol was found. + * + * @exception OtpAuthException + * if a remote node attempted to connect, but was not + * authorized to connect. + */ + public OtpConnection accept() throws IOException, OtpAuthException { + Socket newsock = null; + + while (true) { + try { + newsock = sock.accept(); + return new OtpConnection(this, newsock); + } catch (final IOException e) { + try { + if (newsock != null) { + newsock.close(); + } + } catch (final IOException f) {/* ignore close errors */ + } + throw e; + } + } + } + + /** + * Open a connection to a remote node. + * + * @param other + * the remote node to which you wish to connect. + * + * @return a connection to the remote node. + * + * @exception java.net.UnknownHostException + * if the remote host could not be found. + * + * @exception java.io.IOException + * if it was not possible to connect to the remote node. + * + * @exception OtpAuthException + * if the connection was refused by the remote node. + */ + public OtpConnection connect(final OtpPeer other) throws IOException, + UnknownHostException, OtpAuthException { + return new OtpConnection(this, other); + } +} diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServer.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServer.java new file mode 100644 index 0000000000..0de399ac61 --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServer.java @@ -0,0 +1,110 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2000-2009. 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; + +/** + * Represents a local OTP client or server node. It is used when you want other + * nodes to be able to establish connections to this one. + * + * When you create an instance of this class, it will bind a socket to a port so + * that incoming connections can be accepted. However the port number will not + * be made available to other nodes wishing to connect until you explicitely + * register with the port mapper daemon by calling {@link #publishPort()}. + * + *
+ * When the Java node will be connecting to a remote Erlang, Java or C node, it + * must first identify itself as a node by creating an instance of this class, + * after which it may connect to the remote node. + * + *
+ * Setting up a connection may be done as follows: + * + * + *
+ * OtpServer self = new OtpServer("server", "cookie"); // identify self + * self.publishPort(); // make port information available + * + * OtpConnection conn = self.accept(); // get incoming connection + *+ * + * @see OtpSelf + * + * @deprecated the functionality of this class has been moved to {@link OtpSelf}. + */ +@Deprecated +public class OtpServer extends OtpSelf { + /** + * Create an {@link OtpServer} from an existing {@link OtpSelf}. + * + * @param self + * an existing self node. + * + * @exception java.io.IOException + * if a ServerSocket could not be created. + * + */ + public OtpServer(final OtpSelf self) throws IOException { + super(self.node(), self.cookie()); + } + + /** + * Create an OtpServer, using a vacant port chosen by the operating system. + * To determine what port was chosen, call the object's {@link #port()} + * method. + * + * @param node + * the name of the node. + * + * @param cookie + * the authorization cookie that will be used by this node + * when accepts connections from remote nodes. + * + * @exception java.io.IOException + * if a ServerSocket could not be created. + * + */ + public OtpServer(final String node, final String cookie) throws IOException { + super(node, cookie); + } + + /** + * Create an OtpServer, using the specified port number. + * + * @param node + * a name for this node, as above. + * + * @param cookie + * the authorization cookie that will be used by this node + * when accepts connections from remote nodes. + * + * @param port + * the port number to bind the socket to. + * + * @exception java.io.IOException + * if a ServerSocket could not be created or if the + * chosen port number was not available. + * + */ + public OtpServer(final String node, final String cookie, final int port) + throws IOException { + super(node, cookie, port); + } +} diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSystem.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSystem.java new file mode 100644 index 0000000000..969da39d70 --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSystem.java @@ -0,0 +1,53 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2004-2009. 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; + +final class OtpSystem { + + // Place status variables here + + static { + + final String rel = System.getProperty("OtpCompatRel", "0"); + + try { + + switch (Integer.parseInt(rel)) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 0: + default: + break; + } + } catch (final NumberFormatException e) { + /* Ignore ... */ + } + + } + + // Place query functions here + +} diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/ignore_config_record.inf b/lib/jinterface/java_src/com/ericsson/otp/erlang/ignore_config_record.inf new file mode 100644 index 0000000000..0a5053eba3 --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/ignore_config_record.inf @@ -0,0 +1 @@ +This file makes clearmake use the -T switch for this subdirectory diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files new file mode 100644 index 0000000000..1390542194 --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/java_files @@ -0,0 +1,84 @@ +# -*-Makefile-*- + +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2000-2009. 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% +# + +# this file is included in the doc and src makefiles +# so the list of files only needs to be updated in one place +# i.e. here + +EXCEPTIONS = \ + OtpAuthException \ + OtpErlangDecodeException \ + OtpErlangException \ + OtpErlangExit \ + OtpErlangRangeException \ + OtpException + +COMM = \ + AbstractConnection \ + AbstractNode \ + GenericQueue \ + Link \ + Links \ + OtpConnection \ + OtpCookedConnection \ + OtpEpmd \ + OtpErlangFun \ + OtpErlangExternalFun \ + OtpExternal \ + OtpInputStream \ + OtpLocalNode \ + OtpNodeStatus \ + OtpMD5 \ + OtpMbox \ + OtpMsg \ + OtpNode \ + OtpOutputStream \ + OtpPeer \ + OtpSelf \ + OtpServer + +ERL = \ + OtpErlangAtom \ + OtpErlangBinary \ + OtpErlangBitstr \ + OtpErlangBoolean \ + OtpErlangByte \ + OtpErlangChar \ + OtpErlangDouble \ + OtpErlangFloat \ + OtpErlangInt \ + OtpErlangList \ + OtpErlangLong \ + OtpErlangObject \ + OtpErlangPid \ + OtpErlangPort \ + OtpErlangRef \ + OtpErlangShort\ + OtpErlangString\ + OtpErlangTuple \ + OtpErlangUInt \ + OtpErlangUShort + +MISC = \ + OtpSystem + +JAVA_FILES=$(EXCEPTIONS) $(ERL) $(COMM) $(MISC) + diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/package.html b/lib/jinterface/java_src/com/ericsson/otp/erlang/package.html new file mode 100644 index 0000000000..039a8778f2 --- /dev/null +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/package.html @@ -0,0 +1,157 @@ + + + + + + + + +
This package provides support for communication with Erlang and +representation of Erlang datatypes. + +
Note: By default, jinterface
is only guaranteed
+to be compatible with other Erlang/OTP components from the same release as
+jinterface
itself. For example, jinterface
+from the OTP R10 release is not compatible with an Erlang emulator from
+the OTP R9 release by default. jinterface
can be set in
+compatibility mode of an earlier release (not earlier that R7), though.
+The compatibility mode is set by usage of the OtpCompatRel
+property. By starting the jvm with the command-line argument
+-DOtpCompatRel=9
, jinterface
will be compatible
+with the R9 release of OTP. Warning! You may run into trouble if
+this feature is used carelessly. Always make sure that all communicating
+components are either from the same Erlang/OTP release, or from release
+X and release Y where all components from release Y are in compatibility
+mode of release X.
+
+
The classes +{@link com.ericsson.otp.erlang.OtpErlangList}, +{@link com.ericsson.otp.erlang.OtpErlangTuple}, +{@link com.ericsson.otp.erlang.OtpErlangBinary}, +{@link com.ericsson.otp.erlang.OtpErlangAtom}, +{@link com.ericsson.otp.erlang.OtpErlangBoolean}, +{@link com.ericsson.otp.erlang.OtpErlangByte}, +{@link com.ericsson.otp.erlang.OtpErlangChar}, +{@link com.ericsson.otp.erlang.OtpErlangDouble}, +{@link com.ericsson.otp.erlang.OtpErlangFloat}, +{@link com.ericsson.otp.erlang.OtpErlangLong}, +{@link com.ericsson.otp.erlang.OtpErlangInt}, +{@link com.ericsson.otp.erlang.OtpErlangUInt}, +{@link com.ericsson.otp.erlang.OtpErlangShort}, +{@link com.ericsson.otp.erlang.OtpErlangUShort}, +{@link com.ericsson.otp.erlang.OtpErlangString}, +{@link com.ericsson.otp.erlang.OtpErlangObject}, +{@link com.ericsson.otp.erlang.OtpErlangPid}, +{@link com.ericsson.otp.erlang.OtpErlangPort}, +and {@link com.ericsson.otp.erlang.OtpErlangRef} +represent the various Erlang datatypes. + + +
There are two basic mechanisms for communicating with Erlang, +described briefly here. Note that the two mechanisms are not intended +to be used together. Which mechanism you choose depends on your +application and the level of control it needs.
+ +You can use {@link com.ericsson.otp.erlang.OtpNode}, which can +manage incoming and outgoing connections for you. With {@link +com.ericsson.otp.erlang.OtpNode} a thread is automatically started to +listen for incoming connections, make necessary outgoing connections, +and dispatch messages to their recipients. {@link +com.ericsson.otp.erlang.OtpNode} supports the concept of {@link +com.ericsson.otp.erlang.OtpMbox mailboxes}, allowing you to have +several Java components communicating independently with Erlang. +
+ ++ OtpNode node = new OtpNode("bingo"); + OtpMbox mbox = node.createMbox(); + + mbox.send("foo@localhost",new OtpErlangAtom("hej")); ++ +
If you need more control (but less support from the library), you +can manage connections yourself using the {@link +com.ericsson.otp.erlang.OtpSelf} and {@link +com.ericsson.otp.erlang.OtpConnection} classes, in which case you can +control explicitly which connections are made and which messages are +sent. Received messages are not dispatched by {@link +com.ericsson.otp.erlang.OtpConnection}.
+ +The classes {@link com.ericsson.otp.erlang.OtpPeer}, {@link +com.ericsson.otp.erlang.OtpSelf} and {@link +com.ericsson.otp.erlang.OtpServer} are used to represent OTP nodes and +are neccessary in order to set up communication between the Java +thread and a remote node. Once a connection has been established, it +is represented by an {@link com.ericsson.otp.erlang.OtpConnection}, +through which all communication goes. + +
Setting up a connection with a remote node is straightforward. You +create objects representing the local and remote nodes, then call the +local node's {@link +com.ericsson.otp.erlang.OtpSelf#connect(com.ericsson.otp.erlang.OtpPeer) +connect()} method: + +
+ OtpSelf self = new OtpSelf("client","cookie"); + OtpPeer other = new OtpPeer("server"); + OtpConnection conn = self.connect(other); ++ +
If you wish to be able to accept incoming connections as well as +make outgoing ones, you first must register the listen port with EPMD +(described in the Erlang documentation). Once that is done, you can +accept incoming connections: + +
+ OtpServer self = new OtpSelf("server","cookie"); + self.publishPort(); + OtpConnection conn = self.accept(); ++ + +
Once the connection is established by one of the above methods ({@link +com.ericsson.otp.erlang.OtpSelf#connect(com.ericsson.otp.erlang.OtpPeer) +connect()} or {@link com.ericsson.otp.erlang.OtpSelf#accept() +accept()}), you can use the resulting {@link +com.ericsson.otp.erlang.OtpConnection OtpConnection} to send and +receive messages: + +
+ OtpErlangAtom msg = new ErlangOtpAtom("hello"); + conn.send("echoserver", msg); + + OtpErlangObject reply = conn.receive(); + System.out.println("Received " + reply); ++ +
Finally, you can get an even greater level of control (and even +less support from the library) by subclassing {@link +com.ericsson.otp.erlang.AbstractConnection} and implementing the +communication primitives needed by your application.
+ + + -- cgit v1.2.3