aboutsummaryrefslogtreecommitdiffstats
path: root/lib/jinterface/java_src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/jinterface/java_src')
-rw-r--r--lib/jinterface/java_src/Makefile51
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java1361
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java252
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/GenericQueue.java186
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java57
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Links.java123
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile.otp113
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpAuthException.java37
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java584
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java244
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java569
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java284
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java88
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java285
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java56
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java61
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java61
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java35
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java132
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangException.java40
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangExit.java112
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangExternalFun.java73
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java58
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java131
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.java61
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java505
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java399
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java190
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java210
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java169
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRangeException.java42
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java264
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java63
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java198
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java261
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java67
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java67
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java38
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java105
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java1203
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpLocalNode.java161
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java354
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java722
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java291
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java807
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNodeStatus.java100
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java816
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpPeer.java86
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSelf.java221
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpServer.java110
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpSystem.java53
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/ignore_config_record.inf1
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/java_files84
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/package.html157
54 files changed, 12798 insertions, 0 deletions
diff --git a/lib/jinterface/java_src/Makefile b/lib/jinterface/java_src/Makefile
new file mode 100644
index 0000000000..37a57352ad
--- /dev/null
+++ b/lib/jinterface/java_src/Makefile
@@ -0,0 +1,51 @@
+#
+# %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%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(JINTERFACE_VSN)
+
+# ----------------------------------------------------
+# Common Macros
+# ----------------------------------------------------
+
+# call recursive make explicitly below
+# due to separate makefiles for Ronja & OTP
+# SUB_DIRECTORIES = com/ericsson/otp/erlang
+
+SPECIAL_TARGETS =
+
+
+
+# ----------------------------------------------------
+# Default Subdir Targets
+# ----------------------------------------------------
+
+.PHONY: debug opt instr release docs release_docs tests release_tests clean depend
+
+debug opt instr release docs release_docs tests release_tests clean depend:
+ set -e; set -x; \
+ case "$(MAKE)" in *clearmake*) tflag="-T";; *) tflag="";; esac; \
+ if test -f com/ericsson/otp/erlang/ignore_config_record.inf; then xflag=$$tflag; fi; \
+ (cd com/ericsson/otp/erlang && $(MAKE) -f Makefile.otp $$xflag $@)
+
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
new file mode 100644
index 0000000000..ab0b299bf9
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
@@ -0,0 +1,1361 @@
+/*
+ * %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.io.InputStream;
+import java.net.Socket;
+import java.util.Random;
+
+/**
+ * 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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * Note that no receive methods are provided. Subclasses must provide methods
+ * for message delivery, and may implement their own receive methods.
+ * <p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ */
+public abstract class AbstractConnection extends Thread {
+ protected static final int headerLen = 2048; // more than enough
+
+ protected static final byte passThrough = (byte) 0x70;
+ protected static final byte version = (byte) 0x83;
+
+ // Erlang message header tags
+ protected static final int linkTag = 1;
+ protected static final int sendTag = 2;
+ protected static final int exitTag = 3;
+ protected static final int unlinkTag = 4;
+ protected static final int nodeLinkTag = 5;
+ protected static final int regSendTag = 6;
+ protected static final int groupLeaderTag = 7;
+ protected static final int exit2Tag = 8;
+
+ protected static final int sendTTTag = 12;
+ protected static final int exitTTTag = 13;
+ protected static final int regSendTTTag = 16;
+ protected static final int exit2TTTag = 18;
+
+ // MD5 challenge messsage tags
+ protected static final int ChallengeReply = 'r';
+ protected static final int ChallengeAck = 'a';
+ protected static final int ChallengeStatus = 's';
+
+ private volatile boolean done = false;
+
+ protected boolean connected = false; // connection status
+ protected Socket socket; // communication channel
+ protected OtpPeer peer; // who are we connected to
+ protected OtpLocalNode self; // this nodes id
+ String name; // local name of this connection
+
+ protected boolean cookieOk = false; // already checked the cookie for this
+ // connection
+ protected boolean sendCookie = true; // Send cookies in messages?
+
+ // tracelevel constants
+ protected int traceLevel = 0;
+
+ protected static int defaultLevel = 0;
+ protected static int sendThreshold = 1;
+ protected static int ctrlThreshold = 2;
+ protected static int handshakeThreshold = 3;
+
+ protected static Random random = null;
+
+ private int flags = 0;
+
+ static {
+ // trace this connection?
+ final String trace = System.getProperties().getProperty(
+ "OtpConnection.trace");
+ try {
+ if (trace != null) {
+ defaultLevel = Integer.valueOf(trace).intValue();
+ }
+ } catch (final NumberFormatException e) {
+ defaultLevel = 0;
+ }
+ random = new Random();
+ }
+
+ // private AbstractConnection() {
+ // }
+
+ /**
+ * 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
+ */
+ protected AbstractConnection(final OtpLocalNode self, final Socket s)
+ throws IOException, OtpAuthException {
+ this.self = self;
+ peer = new OtpPeer();
+ socket = s;
+
+ socket.setTcpNoDelay(true);
+
+ traceLevel = defaultLevel;
+ setDaemon(true);
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("<- ACCEPT FROM " + s.getInetAddress() + ":"
+ + s.getPort());
+ }
+
+ // get his info
+ recvName(peer);
+
+ // now find highest common dist value
+ if (peer.proto != self.proto || self.distHigh < peer.distLow
+ || self.distLow > peer.distHigh) {
+ close();
+ throw new IOException(
+ "No common protocol found - cannot accept connection");
+ }
+ // highest common version: min(peer.distHigh, self.distHigh)
+ peer.distChoose = peer.distHigh > self.distHigh ? self.distHigh
+ : peer.distHigh;
+
+ doAccept();
+ name = peer.node();
+ }
+
+ /**
+ * 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.
+ */
+ protected AbstractConnection(final OtpLocalNode self, final OtpPeer other)
+ throws IOException, OtpAuthException {
+ peer = other;
+ this.self = self;
+ socket = null;
+ int port;
+
+ traceLevel = defaultLevel;
+ setDaemon(true);
+
+ // now get a connection between the two...
+ port = OtpEpmd.lookupPort(peer);
+
+ // now find highest common dist value
+ if (peer.proto != self.proto || self.distHigh < peer.distLow
+ || self.distLow > peer.distHigh) {
+ throw new IOException("No common protocol found - cannot connect");
+ }
+
+ // highest common version: min(peer.distHigh, self.distHigh)
+ peer.distChoose = peer.distHigh > self.distHigh ? self.distHigh
+ : peer.distHigh;
+
+ doConnect(port);
+
+ name = peer.node();
+ connected = true;
+ }
+
+ /**
+ * Deliver communication exceptions to the recipient.
+ */
+ public abstract void deliver(Exception e);
+
+ /**
+ * Deliver messages to the recipient.
+ */
+ public abstract void deliver(OtpMsg 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.
+ */
+ protected void sendBuf(final OtpErlangPid from, final String dest,
+ final OtpOutputStream payload) throws IOException {
+ if (!connected) {
+ throw new IOException("Not connected");
+ }
+ final OtpOutputStream header = new OtpOutputStream(headerLen);
+
+ // preamble: 4 byte length + "passthrough" tag + version
+ header.write4BE(0); // reserve space for length
+ header.write1(passThrough);
+ header.write1(version);
+
+ // header info
+ header.write_tuple_head(4);
+ header.write_long(regSendTag);
+ header.write_any(from);
+ if (sendCookie) {
+ header.write_atom(self.cookie());
+ } else {
+ header.write_atom("");
+ }
+ header.write_atom(dest);
+
+ // version for payload
+ header.write1(version);
+
+ // fix up length in preamble
+ header.poke4BE(0, header.size() + payload.size() - 4);
+
+ do_send(header, 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.
+ */
+ protected void sendBuf(final OtpErlangPid from, final OtpErlangPid dest,
+ final OtpOutputStream payload) throws IOException {
+ if (!connected) {
+ throw new IOException("Not connected");
+ }
+ final OtpOutputStream header = new OtpOutputStream(headerLen);
+
+ // preamble: 4 byte length + "passthrough" tag + version
+ header.write4BE(0); // reserve space for length
+ header.write1(passThrough);
+ header.write1(version);
+
+ // header info
+ header.write_tuple_head(3);
+ header.write_long(sendTag);
+ if (sendCookie) {
+ header.write_atom(self.cookie());
+ } else {
+ header.write_atom("");
+ }
+ header.write_any(dest);
+
+ // version for payload
+ header.write1(version);
+
+ // fix up length in preamble
+ header.poke4BE(0, header.size() + payload.size() - 4);
+
+ do_send(header, payload);
+ }
+
+ /*
+ * Send an auth error to peer because he sent a bad cookie. The auth error
+ * uses his cookie (not revealing ours). This is just like send_reg
+ * otherwise
+ */
+ private void cookieError(final OtpLocalNode local,
+ final OtpErlangAtom cookie) throws OtpAuthException {
+ try {
+ final OtpOutputStream header = new OtpOutputStream(headerLen);
+
+ // preamble: 4 byte length + "passthrough" tag + version
+ header.write4BE(0); // reserve space for length
+ header.write1(passThrough);
+ header.write1(version);
+
+ header.write_tuple_head(4);
+ header.write_long(regSendTag);
+ header.write_any(local.createPid()); // disposable pid
+ header.write_atom(cookie.atomValue()); // important: his cookie,
+ // not mine...
+ header.write_atom("auth");
+
+ // version for payload
+ header.write1(version);
+
+ // the payload
+
+ // the no_auth message (copied from Erlang) Don't change this
+ // (Erlang will crash)
+ // {$gen_cast, {print, "~n** Unauthorized cookie ~w **~n",
+ // [foo@aule]}}
+ final OtpErlangObject[] msg = new OtpErlangObject[2];
+ final OtpErlangObject[] msgbody = new OtpErlangObject[3];
+
+ msgbody[0] = new OtpErlangAtom("print");
+ msgbody[1] = new OtpErlangString("~n** Bad cookie sent to " + local
+ + " **~n");
+ // Erlang will crash and burn if there is no third argument here...
+ msgbody[2] = new OtpErlangList(); // empty list
+
+ msg[0] = new OtpErlangAtom("$gen_cast");
+ msg[1] = new OtpErlangTuple(msgbody);
+
+ final OtpOutputStream payload = new OtpOutputStream(
+ new OtpErlangTuple(msg));
+
+ // fix up length in preamble
+ header.poke4BE(0, header.size() + payload.size() - 4);
+
+ try {
+ do_send(header, payload);
+ } catch (final IOException e) {
+ } // ignore
+ } finally {
+ close();
+ }
+ throw new OtpAuthException("Remote cookie not authorized: "
+ + cookie.atomValue());
+ }
+
+ // link to pid
+
+ /**
+ * 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 #sendUnlink 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.
+ */
+ protected void sendLink(final OtpErlangPid from, final OtpErlangPid dest)
+ throws IOException {
+ if (!connected) {
+ throw new IOException("Not connected");
+ }
+ final OtpOutputStream header = new OtpOutputStream(headerLen);
+
+ // preamble: 4 byte length + "passthrough" tag
+ header.write4BE(0); // reserve space for length
+ header.write1(passThrough);
+ header.write1(version);
+
+ // header
+ header.write_tuple_head(3);
+ header.write_long(linkTag);
+ header.write_any(from);
+ header.write_any(dest);
+
+ // fix up length in preamble
+ header.poke4BE(0, header.size() - 4);
+
+ do_send(header);
+ }
+
+ /**
+ * Remove a link between the local node and the specified process on the
+ * remote node. This method deactivates links created with {@link #sendLink
+ * 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.
+ */
+ protected void sendUnlink(final OtpErlangPid from, final OtpErlangPid dest)
+ throws IOException {
+ if (!connected) {
+ throw new IOException("Not connected");
+ }
+ final OtpOutputStream header = new OtpOutputStream(headerLen);
+
+ // preamble: 4 byte length + "passthrough" tag
+ header.write4BE(0); // reserve space for length
+ header.write1(passThrough);
+ header.write1(version);
+
+ // header
+ header.write_tuple_head(3);
+ header.write_long(unlinkTag);
+ header.write_any(from);
+ header.write_any(dest);
+
+ // fix up length in preamble
+ header.poke4BE(0, header.size() - 4);
+
+ do_send(header);
+ }
+
+ /* used internally when "processes" terminate */
+ protected void sendExit(final OtpErlangPid from, final OtpErlangPid dest,
+ final OtpErlangObject reason) throws IOException {
+ sendExit(exitTag, from, dest, reason);
+ }
+
+ /**
+ * 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.
+ */
+ protected void sendExit2(final OtpErlangPid from, final OtpErlangPid dest,
+ final OtpErlangObject reason) throws IOException {
+ sendExit(exit2Tag, from, dest, reason);
+ }
+
+ private void sendExit(final int tag, final OtpErlangPid from,
+ final OtpErlangPid dest, final OtpErlangObject reason)
+ throws IOException {
+ if (!connected) {
+ throw new IOException("Not connected");
+ }
+ final OtpOutputStream header = new OtpOutputStream(headerLen);
+
+ // preamble: 4 byte length + "passthrough" tag
+ header.write4BE(0); // reserve space for length
+ header.write1(passThrough);
+ header.write1(version);
+
+ // header
+ header.write_tuple_head(4);
+ header.write_long(tag);
+ header.write_any(from);
+ header.write_any(dest);
+ header.write_any(reason);
+
+ // fix up length in preamble
+ header.poke4BE(0, header.size() - 4);
+
+ do_send(header);
+ }
+
+ @Override
+ public void run() {
+ if (!connected) {
+ deliver(new IOException("Not connected"));
+ return;
+ }
+
+ final byte[] lbuf = new byte[4];
+ OtpInputStream ibuf;
+ OtpErlangObject traceobj;
+ int len;
+ final byte[] tock = { 0, 0, 0, 0 };
+
+ try {
+ receive_loop: while (!done) {
+ // don't return until we get a real message
+ // or a failure of some kind (e.g. EXIT)
+ // read length and read buffer must be atomic!
+ tick_loop: do {
+ // read 4 bytes - get length of incoming packet
+ // socket.getInputStream().read(lbuf);
+ readSock(socket, lbuf);
+ ibuf = new OtpInputStream(lbuf, flags);
+ len = ibuf.read4BE();
+
+ // received tick? send tock!
+ if (len == 0) {
+ synchronized (this) {
+ socket.getOutputStream().write(tock);
+ }
+ }
+
+ } while (len == 0); // tick_loop
+
+ // got a real message (maybe) - read len bytes
+ final byte[] tmpbuf = new byte[len];
+ // i = socket.getInputStream().read(tmpbuf);
+ readSock(socket, tmpbuf);
+ ibuf = new OtpInputStream(tmpbuf, flags);
+
+ if (ibuf.read1() != passThrough) {
+ break receive_loop;
+ }
+
+ // got a real message (really)
+ OtpErlangObject reason = null;
+ OtpErlangAtom cookie = null;
+ OtpErlangObject tmp = null;
+ OtpErlangTuple head = null;
+ OtpErlangAtom toName;
+ OtpErlangPid to;
+ OtpErlangPid from;
+ int tag;
+
+ // decode the header
+ tmp = ibuf.read_any();
+ if (!(tmp instanceof OtpErlangTuple)) {
+ break receive_loop;
+ }
+
+ head = (OtpErlangTuple) tmp;
+ if (!(head.elementAt(0) instanceof OtpErlangLong)) {
+ break receive_loop;
+ }
+
+ // lets see what kind of message this is
+ tag = (int) ((OtpErlangLong) head.elementAt(0)).longValue();
+
+ switch (tag) {
+ case sendTag: // { SEND, Cookie, ToPid }
+ case sendTTTag: // { SEND, Cookie, ToPid, TraceToken }
+ if (!cookieOk) {
+ // we only check this once, he can send us bad cookies
+ // later if he likes
+ if (!(head.elementAt(1) instanceof OtpErlangAtom)) {
+ break receive_loop;
+ }
+ cookie = (OtpErlangAtom) head.elementAt(1);
+ if (sendCookie) {
+ if (!cookie.atomValue().equals(self.cookie())) {
+ cookieError(self, cookie);
+ }
+ } else {
+ if (!cookie.atomValue().equals("")) {
+ cookieError(self, cookie);
+ }
+ }
+ cookieOk = true;
+ }
+
+ if (traceLevel >= sendThreshold) {
+ System.out.println("<- " + headerType(head) + " "
+ + head);
+
+ /* show received payload too */
+ ibuf.mark(0);
+ traceobj = ibuf.read_any();
+
+ if (traceobj != null) {
+ System.out.println(" " + traceobj);
+ } else {
+ System.out.println(" (null)");
+ }
+ ibuf.reset();
+ }
+
+ to = (OtpErlangPid) head.elementAt(2);
+
+ deliver(new OtpMsg(to, ibuf));
+ break;
+
+ case regSendTag: // { REG_SEND, FromPid, Cookie, ToName }
+ case regSendTTTag: // { REG_SEND, FromPid, Cookie, ToName,
+ // TraceToken }
+ if (!cookieOk) {
+ // we only check this once, he can send us bad cookies
+ // later if he likes
+ if (!(head.elementAt(2) instanceof OtpErlangAtom)) {
+ break receive_loop;
+ }
+ cookie = (OtpErlangAtom) head.elementAt(2);
+ if (sendCookie) {
+ if (!cookie.atomValue().equals(self.cookie())) {
+ cookieError(self, cookie);
+ }
+ } else {
+ if (!cookie.atomValue().equals("")) {
+ cookieError(self, cookie);
+ }
+ }
+ cookieOk = true;
+ }
+
+ if (traceLevel >= sendThreshold) {
+ System.out.println("<- " + headerType(head) + " "
+ + head);
+
+ /* show received payload too */
+ ibuf.mark(0);
+ traceobj = ibuf.read_any();
+
+ if (traceobj != null) {
+ System.out.println(" " + traceobj);
+ } else {
+ System.out.println(" (null)");
+ }
+ ibuf.reset();
+ }
+
+ from = (OtpErlangPid) head.elementAt(1);
+ toName = (OtpErlangAtom) head.elementAt(3);
+
+ deliver(new OtpMsg(from, toName.atomValue(), ibuf));
+ break;
+
+ case exitTag: // { EXIT, FromPid, ToPid, Reason }
+ case exit2Tag: // { EXIT2, FromPid, ToPid, Reason }
+ if (head.elementAt(3) == null) {
+ break receive_loop;
+ }
+ if (traceLevel >= ctrlThreshold) {
+ System.out.println("<- " + headerType(head) + " "
+ + head);
+ }
+
+ from = (OtpErlangPid) head.elementAt(1);
+ to = (OtpErlangPid) head.elementAt(2);
+ reason = head.elementAt(3);
+
+ deliver(new OtpMsg(tag, from, to, reason));
+ break;
+
+ case exitTTTag: // { EXIT, FromPid, ToPid, TraceToken, Reason }
+ case exit2TTTag: // { EXIT2, FromPid, ToPid, TraceToken,
+ // Reason
+ // }
+ // as above, but bifferent element number
+ if (head.elementAt(4) == null) {
+ break receive_loop;
+ }
+ if (traceLevel >= ctrlThreshold) {
+ System.out.println("<- " + headerType(head) + " "
+ + head);
+ }
+
+ from = (OtpErlangPid) head.elementAt(1);
+ to = (OtpErlangPid) head.elementAt(2);
+ reason = head.elementAt(4);
+
+ deliver(new OtpMsg(tag, from, to, reason));
+ break;
+
+ case linkTag: // { LINK, FromPid, ToPid}
+ case unlinkTag: // { UNLINK, FromPid, ToPid}
+ if (traceLevel >= ctrlThreshold) {
+ System.out.println("<- " + headerType(head) + " "
+ + head);
+ }
+
+ from = (OtpErlangPid) head.elementAt(1);
+ to = (OtpErlangPid) head.elementAt(2);
+
+ deliver(new OtpMsg(tag, from, to));
+ break;
+
+ // absolutely no idea what to do with these, so we ignore
+ // them...
+ case groupLeaderTag: // { GROUPLEADER, FromPid, ToPid}
+ case nodeLinkTag: // { NODELINK }
+ // (just show trace)
+ if (traceLevel >= ctrlThreshold) {
+ System.out.println("<- " + headerType(head) + " "
+ + head);
+ }
+ break;
+
+ default:
+ // garbage?
+ break receive_loop;
+ }
+ } // end receive_loop
+
+ // this section reachable only with break
+ // we have received garbage from peer
+ deliver(new OtpErlangExit("Remote is sending garbage"));
+
+ } // try
+
+ catch (final OtpAuthException e) {
+ deliver(e);
+ } catch (final OtpErlangDecodeException e) {
+ deliver(new OtpErlangExit("Remote is sending garbage"));
+ } catch (final IOException e) {
+ deliver(new OtpErlangExit("Remote has closed connection"));
+ } finally {
+ close();
+ }
+ }
+
+ /**
+ * <p>
+ * Set the trace level for this connection. Normally tracing is off by
+ * default unless System property OtpConnection.trace was set.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @param level
+ * the level to set.
+ *
+ * @return the previous trace level.
+ */
+ public int setTraceLevel(int level) {
+ final int oldLevel = traceLevel;
+
+ // pin the value
+ if (level < 0) {
+ level = 0;
+ } else if (level > 4) {
+ level = 4;
+ }
+
+ traceLevel = level;
+
+ return oldLevel;
+ }
+
+ /**
+ * Get the trace level for this connection.
+ *
+ * @return the current trace level.
+ */
+ public int getTraceLevel() {
+ return traceLevel;
+ }
+
+ /**
+ * Close the connection to the remote node.
+ */
+ public void close() {
+ done = true;
+ connected = false;
+ synchronized (this) {
+ try {
+ if (socket != null) {
+ if (traceLevel >= ctrlThreshold) {
+ System.out.println("-> CLOSE");
+ }
+ socket.close();
+ }
+ } catch (final IOException e) { /* ignore socket close errors */
+ } finally {
+ socket = null;
+ }
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ close();
+ }
+
+ /**
+ * Determine if the connection is still alive. Note that this method only
+ * reports the status of the connection, and that it is possible that there
+ * are unread messages waiting in the receive queue.
+ *
+ * @return true if the connection is alive.
+ */
+ public boolean isConnected() {
+ return connected;
+ }
+
+ // used by send and send_reg (message types with payload)
+ protected synchronized void do_send(final OtpOutputStream header,
+ final OtpOutputStream payload) throws IOException {
+ try {
+ if (traceLevel >= sendThreshold) {
+ // Need to decode header and output buffer to show trace
+ // message!
+ // First make OtpInputStream, then decode.
+ try {
+ final OtpErlangObject h = header.getOtpInputStream(5)
+ .read_any();
+ System.out.println("-> " + headerType(h) + " " + h);
+
+ OtpErlangObject o = payload.getOtpInputStream(0).read_any();
+ System.out.println(" " + o);
+ o = null;
+ } catch (final OtpErlangDecodeException e) {
+ System.out.println(" " + "can't decode output buffer:"
+ + e);
+ }
+ }
+
+ header.writeTo(socket.getOutputStream());
+ payload.writeTo(socket.getOutputStream());
+ } catch (final IOException e) {
+ close();
+ throw e;
+ }
+ }
+
+ // used by the other message types
+ protected synchronized void do_send(final OtpOutputStream header)
+ throws IOException {
+ try {
+ if (traceLevel >= ctrlThreshold) {
+ try {
+ final OtpErlangObject h = header.getOtpInputStream(5)
+ .read_any();
+ System.out.println("-> " + headerType(h) + " " + h);
+ } catch (final OtpErlangDecodeException e) {
+ System.out.println(" " + "can't decode output buffer: "
+ + e);
+ }
+ }
+ header.writeTo(socket.getOutputStream());
+ } catch (final IOException e) {
+ close();
+ throw e;
+ }
+ }
+
+ protected String headerType(final OtpErlangObject h) {
+ int tag = -1;
+
+ if (h instanceof OtpErlangTuple) {
+ tag = (int) ((OtpErlangLong) ((OtpErlangTuple) h).elementAt(0))
+ .longValue();
+ }
+
+ switch (tag) {
+ case linkTag:
+ return "LINK";
+
+ case sendTag:
+ return "SEND";
+
+ case exitTag:
+ return "EXIT";
+
+ case unlinkTag:
+ return "UNLINK";
+
+ case nodeLinkTag:
+ return "NODELINK";
+
+ case regSendTag:
+ return "REG_SEND";
+
+ case groupLeaderTag:
+ return "GROUP_LEADER";
+
+ case exit2Tag:
+ return "EXIT2";
+
+ case sendTTTag:
+ return "SEND_TT";
+
+ case exitTTTag:
+ return "EXIT_TT";
+
+ case regSendTTTag:
+ return "REG_SEND_TT";
+
+ case exit2TTTag:
+ return "EXIT2_TT";
+ }
+
+ return "(unknown type)";
+ }
+
+ /* this method now throws exception if we don't get full read */
+ protected int readSock(final Socket s, final byte[] b) throws IOException {
+ int got = 0;
+ final int len = b.length;
+ int i;
+ InputStream is = null;
+
+ synchronized (this) {
+ if (s == null) {
+ throw new IOException("expected " + len
+ + " bytes, socket was closed");
+ }
+ is = s.getInputStream();
+ }
+
+ while (got < len) {
+ i = is.read(b, got, len - got);
+
+ if (i < 0) {
+ throw new IOException("expected " + len
+ + " bytes, got EOF after " + got + " bytes");
+ } else if (i == 0 && len != 0) {
+ /*
+ * This is a corner case. According to
+ * http://java.sun.com/j2se/1.4.2/docs/api/ class InputStream
+ * is.read(,,l) can only return 0 if l==0. In other words it
+ * should not happen, but apparently did.
+ */
+ throw new IOException("Remote connection closed");
+ } else {
+ got += i;
+ }
+ }
+ return got;
+ }
+
+ protected void doAccept() throws IOException, OtpAuthException {
+ try {
+ sendStatus("ok");
+ final int our_challenge = genChallenge();
+ sendChallenge(peer.distChoose, self.flags, our_challenge);
+ final int her_challenge = recvChallengeReply(our_challenge);
+ final byte[] our_digest = genDigest(her_challenge, self.cookie());
+ sendChallengeAck(our_digest);
+ connected = true;
+ cookieOk = true;
+ sendCookie = false;
+ } catch (final IOException ie) {
+ close();
+ throw ie;
+ } catch (final OtpAuthException ae) {
+ close();
+ throw ae;
+ } catch (final Exception e) {
+ final String nn = peer.node();
+ close();
+ throw new IOException("Error accepting connection from " + nn);
+ }
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("<- MD5 ACCEPTED " + peer.host());
+ }
+ }
+
+ protected void doConnect(final int port) throws IOException,
+ OtpAuthException {
+ try {
+ socket = new Socket(peer.host(), port);
+ socket.setTcpNoDelay(true);
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("-> MD5 CONNECT TO " + peer.host() + ":"
+ + port);
+ }
+ sendName(peer.distChoose, self.flags);
+ recvStatus();
+ final int her_challenge = recvChallenge();
+ final byte[] our_digest = genDigest(her_challenge, self.cookie());
+ final int our_challenge = genChallenge();
+ sendChallengeReply(our_challenge, our_digest);
+ recvChallengeAck(our_challenge);
+ cookieOk = true;
+ sendCookie = false;
+ } catch (final OtpAuthException ae) {
+ close();
+ throw ae;
+ } catch (final Exception e) {
+ close();
+ throw new IOException("Cannot connect to peer node");
+ }
+ }
+
+ // This is nooo good as a challenge,
+ // XXX fix me.
+ static protected int genChallenge() {
+ return random.nextInt();
+ }
+
+ // Used to debug print a message digest
+ static String hex0(final byte x) {
+ final char tab[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f' };
+ int uint;
+ if (x < 0) {
+ uint = x & 0x7F;
+ uint |= 1 << 7;
+ } else {
+ uint = x;
+ }
+ return "" + tab[uint >>> 4] + tab[uint & 0xF];
+ }
+
+ static String hex(final byte[] b) {
+ final StringBuffer sb = new StringBuffer();
+ try {
+ int i;
+ for (i = 0; i < b.length; ++i) {
+ sb.append(hex0(b[i]));
+ }
+ } catch (final Exception e) {
+ // Debug function, ignore errors.
+ }
+ return sb.toString();
+
+ }
+
+ protected byte[] genDigest(final int challenge, final String cookie) {
+ int i;
+ long ch2;
+
+ if (challenge < 0) {
+ ch2 = 1L << 31;
+ ch2 |= challenge & 0x7FFFFFFF;
+ } else {
+ ch2 = challenge;
+ }
+ final OtpMD5 context = new OtpMD5();
+ context.update(cookie);
+ context.update("" + ch2);
+
+ final int[] tmp = context.final_bytes();
+ final byte[] res = new byte[tmp.length];
+ for (i = 0; i < tmp.length; ++i) {
+ res[i] = (byte) (tmp[i] & 0xFF);
+ }
+ return res;
+ }
+
+ protected void sendName(final int dist, final int flags) throws IOException {
+
+ final OtpOutputStream obuf = new OtpOutputStream();
+ final String str = self.node();
+ obuf.write2BE(str.length() + 7); // 7 bytes + nodename
+ obuf.write1(AbstractNode.NTYPE_R6);
+ obuf.write2BE(dist);
+ obuf.write4BE(flags);
+ obuf.write(str.getBytes());
+
+ obuf.writeTo(socket.getOutputStream());
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("-> " + "HANDSHAKE sendName" + " flags=" + flags
+ + " dist=" + dist + " local=" + self);
+ }
+ }
+
+ protected void sendChallenge(final int dist, final int flags,
+ final int challenge) throws IOException {
+
+ final OtpOutputStream obuf = new OtpOutputStream();
+ final String str = self.node();
+ obuf.write2BE(str.length() + 11); // 11 bytes + nodename
+ obuf.write1(AbstractNode.NTYPE_R6);
+ obuf.write2BE(dist);
+ obuf.write4BE(flags);
+ obuf.write4BE(challenge);
+ obuf.write(str.getBytes());
+
+ obuf.writeTo(socket.getOutputStream());
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("-> " + "HANDSHAKE sendChallenge" + " flags="
+ + flags + " dist=" + dist + " challenge=" + challenge
+ + " local=" + self);
+ }
+ }
+
+ protected byte[] read2BytePackage() throws IOException,
+ OtpErlangDecodeException {
+
+ final byte[] lbuf = new byte[2];
+ byte[] tmpbuf;
+
+ readSock(socket, lbuf);
+ final OtpInputStream ibuf = new OtpInputStream(lbuf, 0);
+ final int len = ibuf.read2BE();
+ tmpbuf = new byte[len];
+ readSock(socket, tmpbuf);
+ return tmpbuf;
+ }
+
+ protected void recvName(final OtpPeer peer) throws IOException {
+
+ String hisname = "";
+
+ try {
+ final byte[] tmpbuf = read2BytePackage();
+ final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
+ byte[] tmpname;
+ final int len = tmpbuf.length;
+ peer.ntype = ibuf.read1();
+ if (peer.ntype != AbstractNode.NTYPE_R6) {
+ throw new IOException("Unknown remote node type");
+ }
+ peer.distLow = peer.distHigh = ibuf.read2BE();
+ if (peer.distLow < 5) {
+ throw new IOException("Unknown remote node type");
+ }
+ peer.flags = ibuf.read4BE();
+ tmpname = new byte[len - 7];
+ ibuf.readN(tmpname);
+ hisname = OtpErlangString.newString(tmpname);
+ // Set the old nodetype parameter to indicate hidden/normal status
+ // When the old handshake is removed, the ntype should also be.
+ if ((peer.flags & AbstractNode.dFlagPublished) != 0) {
+ peer.ntype = AbstractNode.NTYPE_R4_ERLANG;
+ } else {
+ peer.ntype = AbstractNode.NTYPE_R4_HIDDEN;
+ }
+
+ if ((peer.flags & AbstractNode.dFlagExtendedReferences) == 0) {
+ throw new IOException(
+ "Handshake failed - peer cannot handle extended references");
+ }
+
+ if ((peer.flags & AbstractNode.dFlagExtendedPidsPorts) == 0) {
+ throw new IOException(
+ "Handshake failed - peer cannot handle extended pids and ports");
+ }
+
+ } catch (final OtpErlangDecodeException e) {
+ throw new IOException("Handshake failed - not enough data");
+ }
+
+ final int i = hisname.indexOf('@', 0);
+ peer.node = hisname;
+ peer.alive = hisname.substring(0, i);
+ peer.host = hisname.substring(i + 1, hisname.length());
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("<- " + "HANDSHAKE" + " ntype=" + peer.ntype
+ + " dist=" + peer.distHigh + " remote=" + peer);
+ }
+ }
+
+ protected int recvChallenge() throws IOException {
+
+ int challenge;
+
+ try {
+ final byte[] buf = read2BytePackage();
+ final OtpInputStream ibuf = new OtpInputStream(buf, 0);
+ peer.ntype = ibuf.read1();
+ if (peer.ntype != AbstractNode.NTYPE_R6) {
+ throw new IOException("Unexpected peer type");
+ }
+ peer.distLow = peer.distHigh = ibuf.read2BE();
+ peer.flags = ibuf.read4BE();
+ challenge = ibuf.read4BE();
+ final byte[] tmpname = new byte[buf.length - 11];
+ ibuf.readN(tmpname);
+ final String hisname = OtpErlangString.newString(tmpname);
+ if (!hisname.equals(peer.node)) {
+ throw new IOException(
+ "Handshake failed - peer has wrong name: " + hisname);
+ }
+
+ if ((peer.flags & AbstractNode.dFlagExtendedReferences) == 0) {
+ throw new IOException(
+ "Handshake failed - peer cannot handle extended references");
+ }
+
+ if ((peer.flags & AbstractNode.dFlagExtendedPidsPorts) == 0) {
+ throw new IOException(
+ "Handshake failed - peer cannot handle extended pids and ports");
+ }
+
+ } catch (final OtpErlangDecodeException e) {
+ throw new IOException("Handshake failed - not enough data");
+ }
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("<- " + "HANDSHAKE recvChallenge" + " from="
+ + peer.node + " challenge=" + challenge + " local=" + self);
+ }
+
+ return challenge;
+ }
+
+ protected void sendChallengeReply(final int challenge, final byte[] digest)
+ throws IOException {
+
+ final OtpOutputStream obuf = new OtpOutputStream();
+ obuf.write2BE(21);
+ obuf.write1(ChallengeReply);
+ obuf.write4BE(challenge);
+ obuf.write(digest);
+ obuf.writeTo(socket.getOutputStream());
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("-> " + "HANDSHAKE sendChallengeReply"
+ + " challenge=" + challenge + " digest=" + hex(digest)
+ + " local=" + self);
+ }
+ }
+
+ // Would use Array.equals in newer JDK...
+ private boolean digests_equals(final byte[] a, final byte[] b) {
+ int i;
+ for (i = 0; i < 16; ++i) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected int recvChallengeReply(final int our_challenge)
+ throws IOException, OtpAuthException {
+
+ int challenge;
+ final byte[] her_digest = new byte[16];
+
+ try {
+ final byte[] buf = read2BytePackage();
+ final OtpInputStream ibuf = new OtpInputStream(buf, 0);
+ final int tag = ibuf.read1();
+ if (tag != ChallengeReply) {
+ throw new IOException("Handshake protocol error");
+ }
+ challenge = ibuf.read4BE();
+ ibuf.readN(her_digest);
+ final byte[] our_digest = genDigest(our_challenge, self.cookie());
+ if (!digests_equals(her_digest, our_digest)) {
+ throw new OtpAuthException("Peer authentication error.");
+ }
+ } catch (final OtpErlangDecodeException e) {
+ throw new IOException("Handshake failed - not enough data");
+ }
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("<- " + "HANDSHAKE recvChallengeReply"
+ + " from=" + peer.node + " challenge=" + challenge
+ + " digest=" + hex(her_digest) + " local=" + self);
+ }
+
+ return challenge;
+ }
+
+ protected void sendChallengeAck(final byte[] digest) throws IOException {
+
+ final OtpOutputStream obuf = new OtpOutputStream();
+ obuf.write2BE(17);
+ obuf.write1(ChallengeAck);
+ obuf.write(digest);
+
+ obuf.writeTo(socket.getOutputStream());
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("-> " + "HANDSHAKE sendChallengeAck"
+ + " digest=" + hex(digest) + " local=" + self);
+ }
+ }
+
+ protected void recvChallengeAck(final int our_challenge)
+ throws IOException, OtpAuthException {
+
+ final byte[] her_digest = new byte[16];
+ try {
+ final byte[] buf = read2BytePackage();
+ final OtpInputStream ibuf = new OtpInputStream(buf, 0);
+ final int tag = ibuf.read1();
+ if (tag != ChallengeAck) {
+ throw new IOException("Handshake protocol error");
+ }
+ ibuf.readN(her_digest);
+ final byte[] our_digest = genDigest(our_challenge, self.cookie());
+ if (!digests_equals(her_digest, our_digest)) {
+ throw new OtpAuthException("Peer authentication error.");
+ }
+ } catch (final OtpErlangDecodeException e) {
+ throw new IOException("Handshake failed - not enough data");
+ } catch (final Exception e) {
+ throw new OtpAuthException("Peer authentication error.");
+ }
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("<- " + "HANDSHAKE recvChallengeAck" + " from="
+ + peer.node + " digest=" + hex(her_digest) + " local="
+ + self);
+ }
+ }
+
+ protected void sendStatus(final String status) throws IOException {
+
+ final OtpOutputStream obuf = new OtpOutputStream();
+ obuf.write2BE(status.length() + 1);
+ obuf.write1(ChallengeStatus);
+ obuf.write(status.getBytes());
+
+ obuf.writeTo(socket.getOutputStream());
+
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("-> " + "HANDSHAKE sendStatus" + " status="
+ + status + " local=" + self);
+ }
+ }
+
+ protected void recvStatus() throws IOException {
+
+ try {
+ final byte[] buf = read2BytePackage();
+ final OtpInputStream ibuf = new OtpInputStream(buf, 0);
+ final int tag = ibuf.read1();
+ if (tag != ChallengeStatus) {
+ throw new IOException("Handshake protocol error");
+ }
+ final byte[] tmpbuf = new byte[buf.length - 1];
+ ibuf.readN(tmpbuf);
+ final String status = OtpErlangString.newString(tmpbuf);
+
+ if (status.compareTo("ok") != 0) {
+ throw new IOException("Peer replied with status '" + status
+ + "' instead of 'ok'");
+ }
+ } catch (final OtpErlangDecodeException e) {
+ throw new IOException("Handshake failed - not enough data");
+ }
+ if (traceLevel >= handshakeThreshold) {
+ System.out.println("<- " + "HANDSHAKE recvStatus (ok)" + " local="
+ + self);
+ }
+ }
+
+ public void setFlags(final int flags) {
+ this.flags = flags;
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
new file mode 100644
index 0000000000..16cb544a16
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -0,0 +1,252 @@
+/*
+ * %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.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * <p>
+ * Represents an OTP node.
+ * </p>
+ *
+ * <p>
+ * 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 "[email protected]". 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * Instances of this class cannot be created directly, use one of the subclasses
+ * instead.
+ * </p>
+ */
+public class AbstractNode {
+ static String localHost = null;
+ String node;
+ String host;
+ String alive;
+ String cookie;
+ static String defaultCookie = null;
+
+ // Node types
+ static final int NTYPE_R6 = 110; // 'n' post-r5, all nodes
+ static final int NTYPE_R4_ERLANG = 109; // 'm' Only for source compatibility
+ static final int NTYPE_R4_HIDDEN = 104; // 'h' Only for source compatibility
+
+ // Node capability flags
+ static final int dFlagPublished = 1;
+ static final int dFlagAtomCache = 2;
+ static final int dFlagExtendedReferences = 4;
+ static final int dFlagDistMonitor = 8;
+ static final int dFlagFunTags = 0x10;
+ static final int dFlagDistMonitorName = 0x20; // NOT USED
+ static final int dFlagHiddenAtomCache = 0x40; // NOT SUPPORTED
+ static final int dflagNewFunTags = 0x80;
+ static final int dFlagExtendedPidsPorts = 0x100;
+ static final int dFlagExportPtrTag = 0x200; // NOT SUPPORTED
+ static final int dFlagBitBinaries = 0x400;
+ static final int dFlagNewFloats = 0x800;
+
+ int ntype = NTYPE_R6;
+ int proto = 0; // tcp/ip
+ int distHigh = 5; // Cannot talk to nodes before R6
+ int distLow = 5; // Cannot talk to nodes before R6
+ int creation = 0;
+ int flags = dFlagExtendedReferences | dFlagExtendedPidsPorts
+ | dFlagBitBinaries | dFlagNewFloats | dFlagFunTags
+ | dflagNewFunTags;
+
+ /* initialize hostname and default cookie */
+ static {
+ try {
+ localHost = InetAddress.getLocalHost().getHostName();
+ /*
+ * Make sure it's a short name, i.e. strip of everything after first
+ * '.'
+ */
+ final int dot = localHost.indexOf(".");
+ if (dot != -1) {
+ localHost = localHost.substring(0, dot);
+ }
+ } catch (final UnknownHostException e) {
+ localHost = "localhost";
+ }
+
+ final String dotCookieFilename = System.getProperty("user.home")
+ + File.separator + ".erlang.cookie";
+ BufferedReader br = null;
+
+ try {
+ final File dotCookieFile = new File(dotCookieFilename);
+
+ br = new BufferedReader(new FileReader(dotCookieFile));
+ defaultCookie = br.readLine().trim();
+ } catch (final IOException e) {
+ defaultCookie = "";
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (final IOException e) {
+ }
+ }
+ }
+
+ protected AbstractNode() {
+ }
+
+ /**
+ * Create a node with the given name and the default cookie.
+ */
+ protected AbstractNode(final String node) {
+ this(node, defaultCookie);
+ }
+
+ /**
+ * Create a node with the given name and cookie.
+ */
+ protected AbstractNode(final String name, final String cookie) {
+ this.cookie = cookie;
+
+ final int i = name.indexOf('@', 0);
+ if (i < 0) {
+ alive = name;
+ host = localHost;
+ } else {
+ alive = name.substring(0, i);
+ host = name.substring(i + 1, name.length());
+ }
+
+ if (alive.length() > 0xff) {
+ alive = alive.substring(0, 0xff);
+ }
+
+ node = alive + "@" + host;
+ }
+
+ /**
+ * Get the name of this node.
+ *
+ * @return the name of the node represented by this object.
+ */
+ public String node() {
+ return node;
+ }
+
+ /**
+ * Get the hostname part of the nodename. Nodenames are composed of two
+ * parts, an alivename and a hostname, separated by '@'. This method returns
+ * the part of the nodename following the '@'.
+ *
+ * @return the hostname component of the nodename.
+ */
+ public String host() {
+ return host;
+ }
+
+ /**
+ * Get the alivename part of the hostname. Nodenames are composed of two
+ * parts, an alivename and a hostname, separated by '@'. This method returns
+ * the part of the nodename preceding the '@'.
+ *
+ * @return the alivename component of the nodename.
+ */
+ public String alive() {
+ return alive;
+ }
+
+ /**
+ * Get the authorization cookie used by this node.
+ *
+ * @return the authorization cookie used by this node.
+ */
+ public String cookie() {
+ return cookie;
+ }
+
+ // package scope
+ int type() {
+ return ntype;
+ }
+
+ // package scope
+ int distHigh() {
+ return distHigh;
+ }
+
+ // package scope
+ int distLow() {
+ return distLow;
+ }
+
+ // package scope: useless information?
+ int proto() {
+ return proto;
+ }
+
+ // package scope
+ int creation() {
+ return creation;
+ }
+
+ /**
+ * Set the authorization cookie used by this node.
+ *
+ * @return the previous authorization cookie used by this node.
+ */
+ public String setCookie(final String cookie) {
+ final String prev = this.cookie;
+ this.cookie = cookie;
+ return prev;
+ }
+
+ @Override
+ public String toString() {
+ return node();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/GenericQueue.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/GenericQueue.java
new file mode 100644
index 0000000000..80bb02f16c
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/GenericQueue.java
@@ -0,0 +1,186 @@
+/*
+ * %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 implements a generic FIFO queue. There is no upper bound on the
+ * length of the queue, items are linked.
+ */
+
+public class GenericQueue {
+ private static final int open = 0;
+ private static final int closing = 1;
+ private static final int closed = 2;
+
+ private int status;
+ private Bucket head;
+ private Bucket tail;
+ private int count;
+
+ private void init() {
+ head = null;
+ tail = null;
+ count = 0;
+ }
+
+ /** Create an empty queue */
+ public GenericQueue() {
+ init();
+ status = open;
+ }
+
+ /** Clear a queue */
+ public void flush() {
+ init();
+ }
+
+ public void close() {
+ status = closing;
+ }
+
+ /**
+ * Add an object to the tail of the queue.
+ *
+ * @param o
+ * Object to insert in the queue
+ */
+ public synchronized void put(final Object o) {
+ final Bucket b = new Bucket(o);
+
+ if (tail != null) {
+ tail.setNext(b);
+ tail = b;
+ } else {
+ // queue was empty but has one element now
+ head = tail = b;
+ }
+ count++;
+
+ // notify any waiting tasks
+ notify();
+ }
+
+ /**
+ * Retrieve an object from the head of the queue, or block until one
+ * arrives.
+ *
+ * @return The object at the head of the queue.
+ */
+ public synchronized Object get() {
+ Object o = null;
+
+ while ((o = tryGet()) == null) {
+ try {
+ this.wait();
+ } catch (final InterruptedException e) {
+ }
+ }
+ return o;
+ }
+
+ /**
+ * Retrieve an object from the head of the queue, blocking until one arrives
+ * or until timeout occurs.
+ *
+ * @param timeout
+ * Maximum time to block on queue, in ms. Use 0 to poll the
+ * queue.
+ *
+ * @exception InterruptedException
+ * if the operation times out.
+ *
+ * @return The object at the head of the queue, or null if none arrived in
+ * time.
+ */
+ public synchronized Object get(final long timeout)
+ throws InterruptedException {
+ if (status == closed) {
+ return null;
+ }
+
+ long currentTime = System.currentTimeMillis();
+ final long stopTime = currentTime + timeout;
+ Object o = null;
+
+ while (true) {
+ if ((o = tryGet()) != null) {
+ return o;
+ }
+
+ currentTime = System.currentTimeMillis();
+ if (stopTime <= currentTime) {
+ throw new InterruptedException("Get operation timed out");
+ }
+
+ try {
+ this.wait(stopTime - currentTime);
+ } catch (final InterruptedException e) {
+ // ignore, but really should retry operation instead
+ }
+ }
+ }
+
+ // attempt to retrieve message from queue head
+ public Object tryGet() {
+ Object o = null;
+
+ if (head != null) {
+ o = head.getContents();
+ head = head.getNext();
+ count--;
+
+ if (head == null) {
+ tail = null;
+ count = 0;
+ }
+ }
+
+ return o;
+ }
+
+ public synchronized int getCount() {
+ return count;
+ }
+
+ /*
+ * The Bucket class. The queue is implemented as a linked list of Buckets.
+ * The container holds the queued object and a reference to the next Bucket.
+ */
+ class Bucket {
+ private Bucket next;
+ private final Object contents;
+
+ public Bucket(final Object o) {
+ next = null;
+ contents = o;
+ }
+
+ public void setNext(final Bucket newNext) {
+ next = newNext;
+ }
+
+ public Bucket getNext() {
+ return next;
+ }
+
+ public Object getContents() {
+ return contents;
+ }
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java
new file mode 100644
index 0000000000..2b085761e3
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java
@@ -0,0 +1,57 @@
+/*
+ * %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 Link {
+ private final OtpErlangPid local;
+ private final OtpErlangPid remote;
+ private int hashCodeValue = 0;
+
+ public Link(final OtpErlangPid local, final OtpErlangPid remote) {
+ this.local = local;
+ this.remote = remote;
+ }
+
+ public OtpErlangPid local() {
+ return local;
+ }
+
+ public OtpErlangPid remote() {
+ return remote;
+ }
+
+ public boolean contains(final OtpErlangPid pid) {
+ return local.equals(pid) || remote.equals(pid);
+ }
+
+ public boolean equals(final OtpErlangPid local, final OtpErlangPid remote) {
+ return this.local.equals(local) && this.remote.equals(remote)
+ || this.local.equals(remote) && this.remote.equals(local);
+ }
+
+ public int hashCode() {
+ if (hashCodeValue == 0) {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(5);
+ hash.combine(local.hashCode() + remote.hashCode());
+ hashCodeValue = hash.valueOf();
+ }
+ return hashCodeValue;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Links.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/Links.java
new file mode 100644
index 0000000000..0bb4a708a3
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Links.java
@@ -0,0 +1,123 @@
+/*
+ * %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 Links {
+ Link[] links;
+ int count;
+
+ Links() {
+ this(10);
+ }
+
+ Links(final int initialSize) {
+ links = new Link[initialSize];
+ count = 0;
+ }
+
+ synchronized void addLink(final OtpErlangPid local,
+ final OtpErlangPid remote) {
+ if (find(local, remote) == -1) {
+ if (count >= links.length) {
+ final Link[] tmp = new Link[count * 2];
+ System.arraycopy(links, 0, tmp, 0, count);
+ links = tmp;
+ }
+ links[count++] = new Link(local, remote);
+ }
+ }
+
+ synchronized void removeLink(final OtpErlangPid local,
+ final OtpErlangPid remote) {
+ int i;
+
+ if ((i = find(local, remote)) != -1) {
+ count--;
+ links[i] = links[count];
+ links[count] = null;
+ }
+ }
+
+ synchronized boolean exists(final OtpErlangPid local,
+ final OtpErlangPid remote) {
+ return find(local, remote) != -1;
+ }
+
+ synchronized int find(final OtpErlangPid local, final OtpErlangPid remote) {
+ for (int i = 0; i < count; i++) {
+ if (links[i].equals(local, remote)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ int count() {
+ return count;
+ }
+
+ /* all local pids get notified about broken connection */
+ synchronized OtpErlangPid[] localPids() {
+ OtpErlangPid[] ret = null;
+ if (count != 0) {
+ ret = new OtpErlangPid[count];
+ for (int i = 0; i < count; i++) {
+ ret[i] = links[i].local();
+ }
+ }
+ return ret;
+ }
+
+ /* all remote pids get notified about failed pid */
+ synchronized OtpErlangPid[] remotePids() {
+ OtpErlangPid[] ret = null;
+ if (count != 0) {
+ ret = new OtpErlangPid[count];
+ for (int i = 0; i < count; i++) {
+ ret[i] = links[i].remote();
+ }
+ }
+ return ret;
+ }
+
+ /* clears the link table, returns a copy */
+ synchronized Link[] clearLinks() {
+ Link[] ret = null;
+ if (count != 0) {
+ ret = new Link[count];
+ for (int i = 0; i < count; i++) {
+ ret[i] = links[i];
+ links[i] = null;
+ }
+ count = 0;
+ }
+ return ret;
+ }
+
+ /* returns a copy of the link table */
+ synchronized Link[] links() {
+ Link[] ret = null;
+ if (count != 0) {
+ ret = new Link[count];
+ System.arraycopy(links, 0, ret, 0, count);
+ }
+ return ret;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile.otp b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile.otp
new file mode 100644
index 0000000000..d0ff9cda34
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile.otp
@@ -0,0 +1,113 @@
+# -*-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%
+#
+include $(ERL_TOP)/make/target.mk
+
+JAVA_DEST_ROOT = $(ERL_TOP)/lib/jinterface/priv/
+JAVA_SRC_ROOT = $(ERL_TOP)/lib/jinterface/java_src/
+JAVA_CLASS_SUBDIR = com/ericsson/otp/erlang/
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include $(ERL_TOP)/lib/jinterface/vsn.mk
+VSN=$(JINTERFACE_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/jinterface-$(VSN)
+
+#
+# JAVA macros
+#
+
+# don't add filenames to the Makefile!
+# all java sourcefiles listed in common include file
+include $(ERL_TOP)/lib/jinterface/java_src/$(JAVA_CLASS_SUBDIR)/java_files
+
+TARGET_FILES= $(JAVA_FILES:%=$(JAVA_DEST_ROOT)$(JAVA_CLASS_SUBDIR)%.class)
+JAVA_SRC= $(JAVA_FILES:%=%.java)
+
+JARFILE= OtpErlang.jar
+
+
+# ----------------------------------------------------
+# Programs and Flags
+# ----------------------------------------------------
+
+JAR= jar
+
+CLASSPATH = $(JAVA_SRC_ROOT)
+
+JAVADOCFLAGS=-d $(DOCDIR)
+JAVAFLAGS=-d $(JAVA_DEST_ROOT)
+JARFLAGS=-cvf
+
+JAVA_OPTIONS =
+
+ifeq ($(TESTROOT),)
+RELEASE_PATH=$(ERL_TOP)/release/$(TARGET)
+else
+RELEASE_PATH=$(TESTROOT)
+endif
+
+
+# ----------------------------------------------------
+# Make Rules
+# ----------------------------------------------------
+
+debug opt: make_dirs $(JAVA_DEST_ROOT)$(JARFILE)
+
+make_dirs:
+ if [ ! -d "$(JAVA_DEST_ROOT)" ];then mkdir "$(JAVA_DEST_ROOT)"; fi
+
+$(JAVA_DEST_ROOT)$(JARFILE): $(TARGET_FILES)
+ @(cd $(JAVA_DEST_ROOT) ; $(JAR) $(JARFLAGS) $(JARFILE) $(JAVA_CLASS_SUBDIR))
+
+clean:
+ rm -f $(TARGET_FILES) *~
+
+docs:
+
+# ----------------------------------------------------
+# Release Targets
+# ----------------------------------------------------
+
+# include $(ERL_TOP)/make/otp_release_targets.mk
+
+release release_docs release_tests release_html:
+ $(MAKE) -f Makefile.otp $(MFLAGS) RELEASE_PATH=$(RELEASE_PATH) $(TARGET_MAKEFILE) $@_spec
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/java_src/com/ericsson/otp/erlang
+ $(INSTALL_DATA) $(JAVA_SRC) $(RELSYSDIR)/java_src/com/ericsson/otp/erlang
+ $(INSTALL_DIR) $(RELSYSDIR)/priv
+ $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) $(RELSYSDIR)/priv
+
+release_docs_spec:
+
+
+
+
+# ----------------------------------------------------
+
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpAuthException.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpAuthException.java
new file mode 100644
index 0000000000..39d254d9fa
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpAuthException.java
@@ -0,0 +1,37 @@
+/*
+ * %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 node attempts to establish a communication channel
+ * when it is not authorized to do so, or when a node sends a message containing
+ * an invalid cookie on an established channel.
+ *
+ * @see OtpConnection
+ */
+public class OtpAuthException extends OtpException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Provides a detailed message.
+ */
+ public OtpAuthException(final String s) {
+ super(s);
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
new file mode 100644
index 0000000000..8e8bd473c8
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
@@ -0,0 +1,584 @@
+/*
+ * %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;
+
+/**
+ * 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.
+ *
+ * <p>
+ * 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).
+ *
+ * <p>
+ * The various receive methods are all blocking and will return only when a
+ * valid message has been received or an exception is raised.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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:
+ *
+ * <pre>
+ * { self, { call, Mod, Fun, Args, user } }
+ * </pre>
+ *
+ * <p>
+ * Note that this method has unpredicatble results if the remote node is not
+ * an Erlang node.
+ * </p>
+ *
+ * @param mod
+ * the name of the Erlang module containing the function to
+ * be called.
+ * @param fun
+ * the name of the function to call.
+ * @param args
+ * an array of Erlang terms, to be used as arguments to the
+ * function.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void sendRPC(final String mod, final String fun,
+ final OtpErlangObject[] args) throws IOException {
+ sendRPC(mod, fun, new OtpErlangList(args));
+ }
+
+ /**
+ * 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:
+ *
+ * <pre>
+ * { self, { call, Mod, Fun, Args, user } }
+ * </pre>
+ *
+ * <p>
+ * Note that this method has unpredicatble results if the remote node is not
+ * an Erlang node.
+ * </p>
+ *
+ * @param mod
+ * the name of the Erlang module containing the function to
+ * be called.
+ * @param fun
+ * the name of the function to call.
+ * @param args
+ * a list of Erlang terms, to be used as arguments to the
+ * function.
+ *
+ * @exception java.io.IOException
+ * if the connection is not active or a communication
+ * error occurs.
+ */
+ public void sendRPC(final String mod, final String fun,
+ final OtpErlangList args) throws IOException {
+ final OtpErlangObject[] rpc = new OtpErlangObject[2];
+ final OtpErlangObject[] call = new OtpErlangObject[5];
+
+ /* {self, { call, Mod, Fun, Args, user}} */
+
+ call[0] = new OtpErlangAtom("call");
+ call[1] = new OtpErlangAtom(mod);
+ call[2] = new OtpErlangAtom(fun);
+ call[3] = args;
+ call[4] = new OtpErlangAtom("user");
+
+ rpc[0] = self.pid();
+ rpc[1] = new OtpErlangTuple(call);
+
+ send("rex", new OtpErlangTuple(rpc));
+ }
+
+ /**
+ * Receive an RPC reply from the remote Erlang node. This convenience
+ * function receives a message from the remote node, and expects it to have
+ * the following format:
+ *
+ * <pre>
+ * { rex, Term }
+ * </pre>
+ *
+ * @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;
+
+/**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * The various receive methods are all blocking and will return only when a
+ * valid message has been received or an exception is raised.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * The message delivery methods in this class deliver directly to
+ * {@link OtpMbox mailboxes} in the {@link OtpNode OtpNode} class.
+ * </p>
+ *
+ * <p>
+ * It is not possible to create an instance of this class directly.
+ * OtpCookedConnection objects are created as needed by the underlying mailbox
+ * mechanism.
+ * </p>
+ */
+public class OtpCookedConnection extends AbstractConnection {
+ protected OtpNode self;
+
+ /*
+ * The connection needs to know which local pids have links that pass
+ * through here, so that they can be notified in case of connection failure
+ */
+ protected Links links = null;
+
+ /*
+ * 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
+ OtpCookedConnection(final OtpNode self, final Socket s) throws IOException,
+ OtpAuthException {
+ super(self, s);
+ this.self = self;
+ links = new Links(25);
+ 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
+ OtpCookedConnection(final OtpNode self, final OtpPeer other)
+ throws IOException, OtpAuthException {
+ super(self, other);
+ this.self = self;
+ links = new Links(25);
+ start();
+ }
+
+ // pass the error to the node
+ @Override
+ public void deliver(final Exception e) {
+ self.deliverError(this, e);
+ return;
+ }
+
+ /*
+ * pass the message to the node for final delivery. Note that the connection
+ * itself needs to know about links (in case of connection failure), so we
+ * snoop for link/unlink too here.
+ */
+ @Override
+ public void deliver(final OtpMsg msg) {
+ final boolean delivered = self.deliver(msg);
+
+ switch (msg.type()) {
+ case OtpMsg.linkTag:
+ if (delivered) {
+ links.addLink(msg.getRecipientPid(), msg.getSenderPid());
+ } else {
+ try {
+ // no such pid - send exit to sender
+ super.sendExit(msg.getRecipientPid(), msg.getSenderPid(),
+ new OtpErlangAtom("noproc"));
+ } catch (final IOException e) {
+ }
+ }
+ break;
+
+ case OtpMsg.unlinkTag:
+ case OtpMsg.exitTag:
+ links.removeLink(msg.getRecipientPid(), msg.getSenderPid());
+ break;
+
+ case OtpMsg.exit2Tag:
+ break;
+ }
+
+ return;
+ }
+
+ /*
+ * send to pid
+ */
+ void send(final OtpErlangPid from, final OtpErlangPid dest,
+ final OtpErlangObject msg) throws IOException {
+ // encode and send the message
+ sendBuf(from, dest, new OtpOutputStream(msg));
+ }
+
+ /*
+ * send to remote name dest is recipient's registered name, the nodename is
+ * implied by the choice of connection.
+ */
+ void send(final OtpErlangPid from, final String dest,
+ final OtpErlangObject msg) throws IOException {
+ // encode and send the message
+ sendBuf(from, dest, new OtpOutputStream(msg));
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ breakLinks();
+ }
+
+ @Override
+ protected void finalize() {
+ close();
+ }
+
+ /*
+ * this one called by dying/killed process
+ */
+ void exit(final OtpErlangPid from, final OtpErlangPid to,
+ final OtpErlangObject reason) {
+ try {
+ super.sendExit(from, to, reason);
+ } catch (final Exception e) {
+ }
+ }
+
+ /*
+ * this one called explicitely by user code => use exit2
+ */
+ void exit2(final OtpErlangPid from, final OtpErlangPid to,
+ final OtpErlangObject reason) {
+ try {
+ super.sendExit2(from, to, reason);
+ } catch (final Exception e) {
+ }
+ }
+
+ /*
+ * snoop for outgoing links and update own table
+ */
+ synchronized void link(final OtpErlangPid from, final OtpErlangPid to)
+ throws OtpErlangExit {
+ try {
+ super.sendLink(from, to);
+ links.addLink(from, to);
+ } catch (final IOException e) {
+ throw new OtpErlangExit("noproc", to);
+ }
+ }
+
+ /*
+ * snoop for outgoing unlinks and update own table
+ */
+ synchronized void unlink(final OtpErlangPid from, final OtpErlangPid to) {
+ links.removeLink(from, to);
+ try {
+ super.sendUnlink(from, to);
+ } catch (final IOException e) {
+ }
+ }
+
+ /*
+ * When the connection fails - send exit to all local pids with links
+ * through this connection
+ */
+ synchronized void breakLinks() {
+ if (links != null) {
+ final Link[] l = links.clearLinks();
+
+ if (l != null) {
+ final int len = l.length;
+
+ for (int i = 0; i < len; i++) {
+ // send exit "from" remote pids to local ones
+ self.deliver(new OtpMsg(OtpMsg.exitTag, l[i].remote(), l[i]
+ .local(), new OtpErlangAtom("noconnection")));
+ }
+ }
+ }
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
new file mode 100644
index 0000000000..3bb678c2cc
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
@@ -0,0 +1,569 @@
+/*
+ * %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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+/**
+ * Provides methods for registering, unregistering and looking up nodes with the
+ * Erlang portmapper daemon (Epmd). For each registered node, Epmd maintains
+ * information about the port on which incoming connections are accepted, as
+ * well as which versions of the Erlang communication protocolt the node
+ * supports.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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&lt;N&gt;, where N is the number of bytes contained in the object
+ * or #bin&lt;N-M&gt; 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:
+ *
+ * <ul>
+ * <li> an error in communication has occurred
+ * <li> a remote process has sent an exit signal
+ * <li> a linked process has exited
+ * </ul>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>
+ * Equivalent to <code>OtpErlangExit(new
+ * OtpErlangAtom(reason)</code>.
+ * </p>
+ *
+ * @param reason
+ * the reason this exit signal has been sent.
+ *
+ * @see #OtpErlangExit(OtpErlangObject)
+ */
+ public OtpErlangExit(final String reason) {
+ this(new OtpErlangAtom(reason));
+ }
+
+ /**
+ * Create an OtpErlangExit exception with the given reason and sender pid.
+ *
+ * @param reason
+ * the reason this exit signal has been sent.
+ *
+ * @param pid
+ * the pid that sent this exit.
+ */
+ public OtpErlangExit(final OtpErlangObject reason, final OtpErlangPid pid) {
+ super(reason.toString());
+ this.reason = reason;
+ this.pid = pid;
+ }
+
+ /**
+ * <p>
+ * Equivalent to <code>OtpErlangExit(new OtpErlangAtom(reason),
+ * pid)</code>.
+ * </p>
+ *
+ * @param reason
+ * the reason this exit signal has been sent.
+ *
+ * @param pid
+ * the pid that sent this exit.
+ *
+ * @see #OtpErlangExit(OtpErlangObject, OtpErlangPid)
+ */
+ public OtpErlangExit(final String reason, final OtpErlangPid pid) {
+ this(new OtpErlangAtom(reason), pid);
+ }
+
+ /**
+ * Get the reason associated with this exit signal.
+ */
+ public OtpErlangObject reason() {
+ return reason;
+ }
+
+ /**
+ * Get the pid that sent this exit.
+ */
+ public OtpErlangPid pid() {
+ return pid;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangExternalFun.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangExternalFun.java
new file mode 100644
index 0000000000..09f36b1ff4
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangExternalFun.java
@@ -0,0 +1,73 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 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;
+
+public class OtpErlangExternalFun extends OtpErlangObject {
+ // don't change this!
+ private static final long serialVersionUID = 6443965570641913886L;
+
+ private final String module;
+ private final String function;
+ private final int arity;
+
+ public OtpErlangExternalFun(final String module, final String function,
+ final int arity) {
+ super();
+ this.module = module;
+ this.function = function;
+ this.arity = arity;
+ }
+
+ public OtpErlangExternalFun(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final OtpErlangExternalFun f = buf.read_external_fun();
+ module = f.module;
+ function = f.function;
+ arity = f.arity;
+ }
+
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_external_fun(module, function, arity);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangExternalFun)) {
+ return false;
+ }
+ final OtpErlangExternalFun f = (OtpErlangExternalFun) o;
+ return module.equals(f.module) && function.equals(f.function)
+ && arity == f.arity;
+ }
+
+ @Override
+ protected int doHashCode() {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(14);
+ hash.combine(module.hashCode(), function.hashCode());
+ hash.combine(arity);
+ return hash.valueOf();
+ }
+
+ @Override
+ public String toString() {
+ return "#Fun<" + module + "." + function + "." + arity + ">";
+ }
+
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java
new file mode 100644
index 0000000000..8662b74c53
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java
@@ -0,0 +1,58 @@
+/*
+ * %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.
+ */
+public class OtpErlangFloat extends OtpErlangDouble implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = -2231546377289456934L;
+
+ /**
+ * Create an Erlang float from the given float value.
+ */
+ public OtpErlangFloat(final float f) {
+ super(f);
+ }
+
+ /**
+ * Create an Erlang float from a stream containing a float 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.
+ *
+ * @exception OtpErlangRangeException
+ * if the value cannot be represented as a Java float.
+ */
+ public OtpErlangFloat(final OtpInputStream buf)
+ throws OtpErlangDecodeException, OtpErlangRangeException {
+ super(buf);
+
+ final float f = floatValue();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java
new file mode 100644
index 0000000000..fc104e9564
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java
@@ -0,0 +1,131 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 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;
+import java.util.Arrays;
+
+public class OtpErlangFun extends OtpErlangObject implements Serializable {
+ // don't change this!
+ private static final long serialVersionUID = -3423031125356706472L;
+
+ private final OtpErlangPid pid;
+ private final String module;
+ private final long index;
+ private final long old_index;
+ private final long uniq;
+ private final OtpErlangObject[] freeVars;
+ private final int arity;
+ private final byte[] md5;
+
+ public OtpErlangFun(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final OtpErlangFun f = buf.read_fun();
+ pid = f.pid;
+ module = f.module;
+ arity = f.arity;
+ md5 = f.md5;
+ index = f.index;
+ old_index = f.old_index;
+ uniq = f.uniq;
+ freeVars = f.freeVars;
+ }
+
+ public OtpErlangFun(final OtpErlangPid pid, final String module,
+ final long index, final long uniq, final OtpErlangObject[] freeVars) {
+ this.pid = pid;
+ this.module = module;
+ arity = -1;
+ md5 = null;
+ this.index = index;
+ old_index = 0;
+ this.uniq = uniq;
+ this.freeVars = freeVars;
+ }
+
+ public OtpErlangFun(final OtpErlangPid pid, final String module,
+ final int arity, final byte[] md5, final int index,
+ final long old_index, final long uniq,
+ final OtpErlangObject[] freeVars) {
+ this.pid = pid;
+ this.module = module;
+ this.arity = arity;
+ this.md5 = md5;
+ this.index = index;
+ this.old_index = old_index;
+ this.uniq = uniq;
+ this.freeVars = freeVars;
+ }
+
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf
+ .write_fun(pid, module, old_index, arity, md5, index, uniq,
+ freeVars);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangFun)) {
+ return false;
+ }
+ final OtpErlangFun f = (OtpErlangFun) o;
+ if (!pid.equals(f.pid) || !module.equals(f.module) || arity != f.arity) {
+ return false;
+ }
+ if (md5 == null) {
+ if (f.md5 != null) {
+ return false;
+ }
+ } else {
+ if (!md5.equals(f.md5)) {
+ return false;
+ }
+ }
+ if (index != f.index || uniq != f.uniq) {
+ return false;
+ }
+ if (freeVars == null) {
+ return f.freeVars == null;
+ }
+ return freeVars.equals(f.freeVars);
+ }
+
+ @Override
+ protected int doHashCode() {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(1);
+ hash.combine(pid.hashCode(), module.hashCode());
+ hash.combine(arity);
+ if (md5 != null) hash.combine(md5);
+ hash.combine(index);
+ hash.combine(uniq);
+ if (freeVars != null) {
+ for (OtpErlangObject o: freeVars) {
+ hash.combine(o.hashCode(), 1);
+ }
+ }
+ return hash.valueOf();
+ }
+
+ @Override
+ public String toString() {
+ return "#Fun<" + module + "." + old_index + "." + uniq + ">";
+ }
+
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.java
new file mode 100644
index 0000000000..d947421459
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.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 OtpErlangInt extends OtpErlangLong implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 1229430977614805556L;
+
+ /**
+ * Create an Erlang integer from the given value.
+ *
+ * @param i
+ * the int value to use.
+ */
+ public OtpErlangInt(final int i) {
+ super(i);
+ }
+
+ /**
+ * 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.
+ */
+ public OtpErlangInt(final OtpInputStream buf)
+ throws OtpErlangRangeException, OtpErlangDecodeException {
+ super(buf);
+
+ final int j = intValue();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java
new file mode 100644
index 0000000000..3456fd7412
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java
@@ -0,0 +1,505 @@
+/*
+ * %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;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Provides a Java representation of Erlang lists. Lists are created from zero
+ * or more arbitrary Erlang terms.
+ *
+ * <p>
+ * The arity of the list is the number of elements it contains.
+ */
+public class OtpErlangList extends OtpErlangObject implements
+ Iterable<OtpErlangObject>, Serializable, Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 5999112769036676548L;
+
+ private static final OtpErlangObject[] NO_ELEMENTS = new OtpErlangObject[0];
+
+ final private OtpErlangObject[] elems;
+
+ private OtpErlangObject lastTail = null;
+
+ /**
+ * Create an empty list.
+ */
+ public OtpErlangList() {
+ elems = NO_ELEMENTS;
+ }
+
+ /**
+ * Create a list of Erlang integers representing Unicode codePoints.
+ * This method does not check if the string contains valid code points.
+ *
+ * @param str
+ * the characters from which to create the list.
+ */
+ public OtpErlangList(final String str) {
+ if (str == null || str.length() == 0) {
+ elems = NO_ELEMENTS;
+ } else {
+ final int[] codePoints = OtpErlangString.stringToCodePoints(str);
+ elems = new OtpErlangObject[codePoints.length];
+ for (int i = 0; i < elems.length; i++) {
+ elems[i] = new OtpErlangInt(codePoints[i]);
+ }
+ }
+ }
+
+ /**
+ * Create a list containing one element.
+ *
+ * @param elem
+ * the elememet to make the list from.
+ */
+ public OtpErlangList(final OtpErlangObject elem) {
+ elems = new OtpErlangObject[] { elem };
+ }
+
+ /**
+ * Create a list from an array of arbitrary Erlang terms.
+ *
+ * @param elems
+ * the array of terms from which to create the list.
+ */
+ public OtpErlangList(final OtpErlangObject[] elems) {
+ this(elems, 0, elems.length);
+ }
+
+ /**
+ * Create a list from an array of arbitrary Erlang terms. Tail can be
+ * specified, if not null, the list will not be proper.
+ *
+ * @param elems
+ * array of terms from which to create the list
+ * @param lastTail
+ * @throws OtpErlangException
+ */
+ public OtpErlangList(final OtpErlangObject[] elems,
+ final OtpErlangObject lastTail) throws OtpErlangException {
+ this(elems, 0, elems.length);
+ if (elems.length == 0 && lastTail != null) {
+ throw new OtpErlangException("Bad list, empty head, non-empty tail");
+ }
+ this.lastTail = lastTail;
+ }
+
+ /**
+ * Create a list from an array of arbitrary Erlang terms.
+ *
+ * @param elems
+ * the array of terms from which to create the list.
+ * @param start
+ * the offset of the first term to insert.
+ * @param count
+ * the number of terms to insert.
+ */
+ public OtpErlangList(final OtpErlangObject[] elems, final int start,
+ final int count) {
+ if (elems != null && count > 0) {
+ this.elems = new OtpErlangObject[count];
+ System.arraycopy(elems, start, this.elems, 0, count);
+ } else {
+ this.elems = NO_ELEMENTS;
+ }
+ }
+
+ /**
+ * Create a list from a stream containing an list encoded in Erlang external
+ * format.
+ *
+ * @param buf
+ * the stream containing the encoded list.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang list.
+ */
+ public OtpErlangList(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final int arity = buf.read_list_head();
+ if (arity > 0) {
+ elems = new OtpErlangObject[arity];
+ for (int i = 0; i < arity; i++) {
+ elems[i] = buf.read_any();
+ }
+ /* discard the terminating nil (empty list) or read tail */
+ if (buf.peek1() == OtpExternal.nilTag) {
+ buf.read_nil();
+ } else {
+ lastTail = buf.read_any();
+ }
+ } else {
+ elems = NO_ELEMENTS;
+ }
+ }
+
+ /**
+ * Get the arity of the list.
+ *
+ * @return the number of elements contained in the list.
+ */
+ public int arity() {
+ return elems.length;
+ }
+
+ /**
+ * Get the specified element from the list.
+ *
+ * @param i
+ * the index of the requested element. List 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 list as an array.
+ *
+ * @return an array containing all of the list's elements.
+ */
+ public OtpErlangObject[] elements() {
+ if (arity() == 0) {
+ return NO_ELEMENTS;
+ } else {
+ final OtpErlangObject[] res = new OtpErlangObject[arity()];
+ System.arraycopy(elems, 0, res, 0, res.length);
+ return res;
+ }
+ }
+
+ /**
+ * Get the string representation of the list.
+ *
+ * @return the string representation of the list.
+ */
+
+ @Override
+ public String toString() {
+ return toString(0);
+ }
+
+ protected String toString(final int start) {
+ final StringBuffer s = new StringBuffer();
+ s.append("[");
+
+ for (int i = start; i < arity(); i++) {
+ if (i > start) {
+ s.append(",");
+ }
+ s.append(elems[i].toString());
+ }
+ if (lastTail != null) {
+ s.append("|").append(lastTail.toString());
+ }
+ s.append("]");
+
+ return s.toString();
+ }
+
+ /**
+ * Convert this list to the equivalent Erlang external representation. Note
+ * that this method never encodes lists as strings, even when it is possible
+ * to do so.
+ *
+ * @param buf
+ * An output stream to which the encoded list should be written.
+ *
+ */
+
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ encode(buf, 0);
+ }
+
+ protected void encode(final OtpOutputStream buf, final int start) {
+ final int arity = arity() - start;
+
+ if (arity > 0) {
+ buf.write_list_head(arity);
+
+ for (int i = start; i < arity + start; i++) {
+ buf.write_any(elems[i]);
+ }
+ }
+ if (lastTail == null) {
+ buf.write_nil();
+ } else {
+ buf.write_any(lastTail);
+ }
+ }
+
+ /**
+ * Determine if two lists are equal. Lists are equal if they have the same
+ * arity and all of the elements are equal.
+ *
+ * @param o
+ * the list to compare to.
+ *
+ * @return true if the lists have the same arity and all the elements are
+ * equal.
+ */
+
+ @Override
+ public boolean equals(final Object o) {
+
+ /*
+ * Be careful to use methods even for "this", so that equals work also
+ * for sublists
+ */
+
+ if (!(o instanceof OtpErlangList)) {
+ return false;
+ }
+
+ final OtpErlangList l = (OtpErlangList) o;
+
+ final int a = arity();
+ if (a != l.arity()) {
+ return false;
+ }
+ for (int i = 0; i < a; i++) {
+ if (!elementAt(i).equals(l.elementAt(i))) {
+ return false; // early exit
+ }
+ }
+ final OtpErlangObject otherTail = l.getLastTail();
+ if (getLastTail() == null && otherTail == null) {
+ return true;
+ }
+ if (getLastTail() == null) {
+ return false;
+ }
+ return getLastTail().equals(l.getLastTail());
+ }
+
+ public OtpErlangObject getLastTail() {
+ return lastTail;
+ }
+
+ @Override
+ protected int doHashCode() {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(4);
+ final int a = arity();
+ if (a == 0) {
+ return (int)3468870702L;
+ }
+ for (int i = 0; i < a; i++) {
+ hash.combine(elementAt(i).hashCode());
+ }
+ final OtpErlangObject t = getLastTail();
+ if (t != null) {
+ int h = t.hashCode();
+ hash.combine(h, h);
+ }
+ return hash.valueOf();
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return new OtpErlangList(elements(), getLastTail());
+ } catch (final OtpErlangException e) {
+ return null;
+ }
+ }
+
+ public Iterator<OtpErlangObject> iterator() {
+ return iterator(0);
+ }
+
+ private Iterator<OtpErlangObject> iterator(final int start) {
+ return new Itr(start);
+ }
+
+ /**
+ * @return true if the list is proper, i.e. the last tail is nil
+ */
+ public boolean isProper() {
+ return lastTail == null;
+ }
+
+ public OtpErlangObject getHead() {
+ if (arity() > 0) {
+ return elems[0];
+ }
+ return null;
+ }
+
+ public OtpErlangObject getTail() {
+ return getNthTail(1);
+ }
+
+ public OtpErlangObject getNthTail(final int n) {
+ final int arity = arity();
+ if (arity >= n) {
+ if (arity == n && lastTail != null) {
+ return lastTail;
+ } else {
+ return new SubList(this, n);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Convert a list of integers into a Unicode string,
+ * interpreting each integer as a Unicode code point value.
+ *
+ * @return A java.lang.String object created through its
+ * constructor String(int[], int, int).
+ *
+ * @exception OtpErlangException
+ * for non-proper and non-integer lists.
+ *
+ * @exception OtpErlangRangeException
+ * if any integer does not fit into a Java int.
+ *
+ * @exception java.security.InvalidParameterException
+ * if any integer is not within the Unicode range.
+ *
+ * @see String#String(int[], int, int)
+ *
+ */
+
+ public String stringValue() throws OtpErlangException {
+ if (! isProper()) {
+ throw new OtpErlangException("Non-proper list: " + this);
+ }
+ final int[] values = new int[arity()];
+ for (int i = 0; i < values.length; ++i) {
+ final OtpErlangObject o = elementAt(i);
+ if (! (o instanceof OtpErlangLong)) {
+ throw new OtpErlangException("Non-integer term: " + o);
+ }
+ final OtpErlangLong l = (OtpErlangLong) o;
+ values[i] = l.intValue();
+ }
+ return new String(values, 0, values.length);
+ }
+
+
+
+ public static class SubList extends OtpErlangList {
+ private static final long serialVersionUID = OtpErlangList.serialVersionUID;
+
+ private final int start;
+
+ private final OtpErlangList parent;
+
+ private SubList(final OtpErlangList parent, final int start) {
+ super();
+ this.parent = parent;
+ this.start = start;
+ }
+
+ @Override
+ public int arity() {
+ return parent.arity() - start;
+ }
+
+ @Override
+ public OtpErlangObject elementAt(final int i) {
+ return parent.elementAt(i + start);
+ }
+
+ @Override
+ public OtpErlangObject[] elements() {
+ final int n = parent.arity() - start;
+ final OtpErlangObject[] res = new OtpErlangObject[n];
+ for (int i = 0; i < res.length; i++) {
+ res[i] = parent.elementAt(i + start);
+ }
+ return res;
+ }
+
+ @Override
+ public boolean isProper() {
+ return parent.isProper();
+ }
+
+ @Override
+ public OtpErlangObject getHead() {
+ return parent.elementAt(start);
+ }
+
+ @Override
+ public OtpErlangObject getNthTail(final int n) {
+ return parent.getNthTail(n + start);
+ }
+
+ @Override
+ public String toString() {
+ return parent.toString(start);
+ }
+
+ @Override
+ public void encode(final OtpOutputStream stream) {
+ parent.encode(stream, start);
+ }
+
+ @Override
+ public OtpErlangObject getLastTail() {
+ return parent.getLastTail();
+ }
+
+ @Override
+ public Iterator<OtpErlangObject> iterator() {
+ return parent.iterator(start);
+ }
+ }
+
+ private class Itr implements Iterator<OtpErlangObject> {
+ /**
+ * Index of element to be returned by subsequent call to next.
+ */
+ private int cursor;
+
+ private Itr(final int cursor) {
+ this.cursor = cursor;
+ }
+
+ public boolean hasNext() {
+ return cursor < elems.length;
+ }
+
+ public OtpErlangObject next() {
+ try {
+ return elems[cursor++];
+ } catch (final IndexOutOfBoundsException e) {
+ throw new NoSuchElementException();
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException(
+ "OtpErlangList cannot be modified!");
+ }
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
new file mode 100644
index 0000000000..7e3e2a7296
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
@@ -0,0 +1,399 @@
+/*
+ * %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;
+import java.math.BigInteger;
+
+/**
+ * Provides a Java representation of Erlang integral types. Erlang does not
+ * distinguish between different integral types, however this class and its
+ * subclasses {@link OtpErlangByte}, {@link OtpErlangChar},
+ * {@link OtpErlangInt}, and {@link OtpErlangShort} attempt to map the Erlang
+ * types onto the various Java integral types. Two additional classes,
+ * {@link OtpErlangUInt} and {@link OtpErlangUShort} are provided for Corba
+ * compatibility. See the documentation for IC for more information.
+ */
+public class OtpErlangLong extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 1610466859236755096L;
+
+ private long val;
+ private BigInteger bigVal = null;
+
+ /**
+ * Create an Erlang integer from the given value.
+ *
+ * @param l
+ * the long value to use.
+ */
+ public OtpErlangLong(final long l) {
+ val = l;
+ }
+
+ /**
+ * Create an Erlang integer from the given value.
+ *
+ * @param val
+ * the long value to use.
+ */
+ public OtpErlangLong(final BigInteger v) {
+ if (v == null) {
+ throw new java.lang.NullPointerException();
+ }
+ if (v.bitLength() < 64) {
+ val = v.longValue();
+ } else {
+ bigVal = v;
+ }
+ }
+
+ /**
+ * 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.
+ */
+ public OtpErlangLong(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final byte[] b = buf.read_integer_byte_array();
+ try {
+ val = OtpInputStream.byte_array_to_long(b, false);
+ } catch (final OtpErlangDecodeException e) {
+ bigVal = new BigInteger(b);
+ }
+ }
+
+ /**
+ * Get this number as a BigInteger.
+ *
+ * @return the value of this number, as a BigInteger.
+ */
+ public BigInteger bigIntegerValue() {
+ if (bigVal != null) {
+ return bigVal;
+ } else {
+ return BigInteger.valueOf(val);
+ }
+ }
+
+ /**
+ * Get this number as a long, or rather truncate all but the least
+ * significant 64 bits from the 2's complement representation of this number
+ * and return them as a long.
+ *
+ * @return the value of this number, as a long.
+ */
+ public long longValue() {
+ if (bigVal != null) {
+ return bigVal.longValue();
+ } else {
+ return val;
+ }
+ }
+
+ /**
+ * Determine if this value can be represented as a long without truncation.
+ *
+ * @return true if this value fits in a long, false otherwise.
+ */
+ public boolean isLong() {
+ // To just chech this.bigVal is a wee bit to simple, since
+ // there just might have be a mean bignum that arrived on
+ // a stream, and was a long disguised as more than 8 byte integer.
+ if (bigVal != null) {
+ return bigVal.bitLength() < 64;
+ }
+ return true;
+ }
+
+ /**
+ * Determine if this value can be represented as an unsigned long without
+ * truncation, that is if the value is non-negative and its bit pattern
+ * completely fits in a long.
+ *
+ * @return true if this value is non-negative and fits in a long false
+ * otherwise.
+ */
+ public boolean isULong() {
+ // Here we have the same problem as for isLong(), plus
+ // the whole range 1<<63 .. (1<<64-1) is allowed.
+ if (bigVal != null) {
+ return bigVal.signum() >= 0 && bigVal.bitLength() <= 64;
+ }
+ return val >= 0;
+ }
+
+ /**
+ * Returns the number of bits in the minimal two's-complement representation
+ * of this BigInteger, excluding a sign bit.
+ *
+ * @return number of bits in the minimal two's-complement representation of
+ * this BigInteger, excluding a sign bit.
+ */
+ public int bitLength() {
+ if (bigVal != null) {
+ return bigVal.bitLength();
+ }
+ if (val == 0 || val == -1) {
+ return 0;
+ } else {
+ // Binary search for bit length
+ int i = 32; // mask length
+ long m = (1L << i) - 1; // AND mask with ones in little end
+ if (val < 0) {
+ m = ~m; // OR mask with ones in big end
+ for (int j = i >> 1; j > 0; j >>= 1) { // mask delta
+ if ((val | m) == val) { // mask >= enough
+ i -= j;
+ m >>= j; // try less bits
+ } else {
+ i += j;
+ m <<= j; // try more bits
+ }
+ }
+ if ((val | m) != val) {
+ i++; // mask < enough
+ }
+ } else {
+ for (int j = i >> 1; j > 0; j >>= 1) { // mask delta
+ if ((val & m) == val) { // mask >= enough
+ i -= j;
+ m >>= j; // try less bits
+ } else {
+ i += j;
+ m = m << j | m; // try more bits
+ }
+ }
+ if ((val & m) != val) {
+ i++; // mask < enough
+ }
+ }
+ return i;
+ }
+ }
+
+ /**
+ * Return the signum function of this object.
+ *
+ * @return -1, 0 or 1 as the value is negative, zero or positive.
+ */
+ public int signum() {
+ if (bigVal != null) {
+ return bigVal.signum();
+ } else {
+ return val > 0 ? 1 : val < 0 ? -1 : 0;
+ }
+ }
+
+ /**
+ * Get this number as an int.
+ *
+ * @return the value of this number, as an int.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as an int.
+ */
+ public int intValue() throws OtpErlangRangeException {
+ final long l = longValue();
+ final int i = (int) l;
+
+ if (i != l) {
+ throw new OtpErlangRangeException("Value too large for int: " + val);
+ }
+
+ return i;
+ }
+
+ /**
+ * Get this number as a non-negative int.
+ *
+ * @return the value of this number, as an int.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as an int,
+ * or if the value is negative.
+ */
+ public int uIntValue() throws OtpErlangRangeException {
+ final long l = longValue();
+ final int i = (int) l;
+
+ if (i != l) {
+ throw new OtpErlangRangeException("Value too large for int: " + val);
+ } else if (i < 0) {
+ throw new OtpErlangRangeException("Value not positive: " + val);
+ }
+
+ return i;
+ }
+
+ /**
+ * Get this number as a short.
+ *
+ * @return the value of this number, as a short.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as a
+ * short.
+ */
+ public short shortValue() throws OtpErlangRangeException {
+ final long l = longValue();
+ final short i = (short) l;
+
+ if (i != l) {
+ throw new OtpErlangRangeException("Value too large for short: "
+ + val);
+ }
+
+ return i;
+ }
+
+ /**
+ * Get this number as a non-negative short.
+ *
+ * @return the value of this number, as a short.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as a
+ * short, or if the value is negative.
+ */
+ public short uShortValue() throws OtpErlangRangeException {
+ final long l = longValue();
+ final short i = (short) l;
+
+ if (i != l) {
+ throw new OtpErlangRangeException("Value too large for short: "
+ + val);
+ } else if (i < 0) {
+ throw new OtpErlangRangeException("Value not positive: " + val);
+ }
+
+ return i;
+ }
+
+ /**
+ * Get this number as a char.
+ *
+ * @return the char value of this number.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as a char.
+ */
+ public char charValue() throws OtpErlangRangeException {
+ final long l = longValue();
+ final char i = (char) l;
+
+ if (i != l) {
+ throw new OtpErlangRangeException("Value too large for char: "
+ + val);
+ }
+
+ return i;
+ }
+
+ /**
+ * Get this number as a byte.
+ *
+ * @return the byte value of this number.
+ *
+ * @exception OtpErlangRangeException
+ * if the value is too large to be represented as a byte.
+ */
+ public byte byteValue() throws OtpErlangRangeException {
+ final long l = longValue();
+ final byte i = (byte) l;
+
+ if (i != l) {
+ throw new OtpErlangRangeException("Value too large for byte: "
+ + val);
+ }
+
+ return i;
+ }
+
+ /**
+ * Get the string representation of this number.
+ *
+ * @return the string representation of this number.
+ */
+ @Override
+ public String toString() {
+ if (bigVal != null) {
+ return "" + bigVal;
+ } else {
+ return "" + val;
+ }
+ }
+
+ /**
+ * Convert this number to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded number should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ if (bigVal != null) {
+ buf.write_big_integer(bigVal);
+ } else {
+ buf.write_long(val);
+ }
+ }
+
+ /**
+ * Determine if two numbers are equal. Numbers are equal if they contain the
+ * same value.
+ *
+ * @param o
+ * the number to compare to.
+ *
+ * @return true if the numbers have the same value.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangLong)) {
+ return false;
+ }
+
+ final OtpErlangLong that = (OtpErlangLong) o;
+
+ if (bigVal != null && that.bigVal != null) {
+ return bigVal.equals(that.bigVal);
+ } else if (bigVal == null && that.bigVal == null) {
+ return val == that.val;
+ }
+ return false;
+ }
+
+ @Override
+ protected int doHashCode() {
+ if (bigVal != null) {
+ return bigVal.hashCode();
+ } else {
+ return BigInteger.valueOf(val).hashCode();
+ }
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
new file mode 100644
index 0000000000..81220c5685
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
@@ -0,0 +1,190 @@
+/*
+ * %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;
+
+/**
+ * Base class of the Erlang data type classes. This class is used to represent
+ * an arbitrary Erlang term.
+ */
+public abstract class OtpErlangObject implements Serializable, Cloneable {
+ protected int hashCodeValue = 0;
+
+ // don't change this!
+ static final long serialVersionUID = -8435938572339430044L;
+
+ /**
+ * @return the printable representation of the object. This is usually
+ * similar to the representation used by Erlang for the same type of
+ * object.
+ */
+ @Override
+ public abstract String toString();
+
+ /**
+ * Convert the object according to the rules of the Erlang external format.
+ * This is mainly used for sending Erlang terms in messages, however it can
+ * also be used for storing terms to disk.
+ *
+ * @param buf
+ * an output stream to which the encoded term should be
+ * written.
+ */
+ public abstract void encode(OtpOutputStream buf);
+
+ /**
+ * Read binary data in the Erlang external format, and produce a
+ * corresponding Erlang data type object. This method is normally used when
+ * Erlang terms are received in messages, however it can also be used for
+ * reading terms from disk.
+ *
+ * @param buf
+ * an input stream containing one or more encoded Erlang
+ * terms.
+ *
+ * @return an object representing one of the Erlang data types.
+ *
+ * @exception OtpErlangDecodeException
+ * if the stream does not contain a valid representation
+ * of an Erlang term.
+ */
+ public static OtpErlangObject decode(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ return buf.read_any();
+ }
+
+ /**
+ * Determine if two Erlang objects are equal. In general, Erlang objects are
+ * equal if the components they consist of are equal.
+ *
+ * @param o
+ * the object to compare to.
+ *
+ * @return true if the objects are identical.
+ */
+ @Override
+ public abstract boolean equals(Object o);
+
+ @Override
+ public int hashCode() {
+ if (hashCodeValue == 0) {
+ hashCodeValue = doHashCode();
+ }
+ return hashCodeValue;
+ }
+
+ protected int doHashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (final CloneNotSupportedException e) {
+ /* cannot happen */
+ throw new InternalError(e.toString());
+ }
+ }
+
+ protected final static class Hash {
+ int abc[] = {0, 0, 0};
+
+ /* Hash function suggested by Bob Jenkins.
+ * The same as in the Erlang VM (beam); utils.c.
+ */
+
+ private final static int HASH_CONST[] = {
+ 0, // not used
+ 0x9e3779b9, // the golden ratio; an arbitrary value
+ 0x3c6ef372, // (hashHConst[1] * 2) % (1<<32)
+ 0xdaa66d2b, // 1 3
+ 0x78dde6e4, // 1 4
+ 0x1715609d, // 1 5
+ 0xb54cda56, // 1 6
+ 0x5384540f, // 1 7
+ 0xf1bbcdc8, // 1 8
+ 0x8ff34781, // 1 9
+ 0x2e2ac13a, // 1 10
+ 0xcc623af3, // 1 11
+ 0x6a99b4ac, // 1 12
+ 0x08d12e65, // 1 13
+ 0xa708a81e, // 1 14
+ 0x454021d7, // 1 15
+ };
+
+ protected Hash(int i) {
+ abc[0] = abc[1] = HASH_CONST[i];
+ abc[2] = 0;
+ }
+
+ //protected Hash() {
+ // Hash(1);
+ //}
+
+ private void mix() {
+ abc[0] -= abc[1]; abc[0] -= abc[2]; abc[0] ^= (abc[2]>>>13);
+ abc[1] -= abc[2]; abc[1] -= abc[0]; abc[1] ^= (abc[0]<<8);
+ abc[2] -= abc[0]; abc[2] -= abc[1]; abc[2] ^= (abc[1]>>>13);
+ abc[0] -= abc[1]; abc[0] -= abc[2]; abc[0] ^= (abc[2]>>>12);
+ abc[1] -= abc[2]; abc[1] -= abc[0]; abc[1] ^= (abc[0]<<16);
+ abc[2] -= abc[0]; abc[2] -= abc[1]; abc[2] ^= (abc[1]>>>5);
+ abc[0] -= abc[1]; abc[0] -= abc[2]; abc[0] ^= (abc[2]>>>3);
+ abc[1] -= abc[2]; abc[1] -= abc[0]; abc[1] ^= (abc[0]<<10);
+ abc[2] -= abc[0]; abc[2] -= abc[1]; abc[2] ^= (abc[1]>>>15);
+ }
+
+ protected void combine(int a) {
+ abc[0] += a;
+ mix();
+ }
+
+ protected void combine(long a) {
+ combine((int)(a >>> 32), (int) a);
+ }
+
+ protected void combine(int a, int b) {
+ abc[0] += a;
+ abc[1] += b;
+ mix();
+ }
+
+ protected void combine(byte b[]) {
+ int j, k;
+ for (j = 0, k = 0;
+ j + 4 < b.length;
+ j += 4, k += 1, k %= 3) {
+ abc[k] += ((int)b[j+0] & 0xFF) + ((int)b[j+1]<<8 & 0xFF00)
+ + ((int)b[j+2]<<16 & 0xFF0000) + ((int)b[j+3]<<24);
+ mix();
+ }
+ for (int n = 0, m = 0xFF;
+ j < b.length;
+ j++, n += 8, m <<= 8) {
+ abc[k] += (int)b[j]<<n & m;
+ }
+ mix();
+ }
+
+ protected int valueOf() {
+ return abc[2];
+ }
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
new file mode 100644
index 0000000000..fe81ce302d
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
@@ -0,0 +1,210 @@
+/*
+ * %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 PIDs. PIDs represent Erlang
+ * processes and consist of a nodename and a number of integers.
+ */
+public class OtpErlangPid extends OtpErlangObject implements Serializable,
+ Cloneable, Comparable<Object> {
+ // don't change this!
+ static final long serialVersionUID = 1664394142301803659L;
+
+ private final String node;
+ private final int id;
+ private final int serial;
+ private final int creation;
+
+ /**
+ * Create a unique Erlang PID belonging to the local node.
+ *
+ * @param self
+ * the local node.
+ *
+ * @deprecated use OtpLocalNode:createPid() instead
+ */
+ @Deprecated
+ public OtpErlangPid(final OtpLocalNode self) {
+ final OtpErlangPid p = self.createPid();
+
+ id = p.id;
+ serial = p.serial;
+ creation = p.creation;
+ node = p.node;
+ }
+
+ /**
+ * Create an Erlang PID from a stream containing a PID encoded in Erlang
+ * external format.
+ *
+ * @param buf
+ * the stream containing the encoded PID.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang PID.
+ */
+ public OtpErlangPid(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final OtpErlangPid p = buf.read_pid();
+
+ node = p.node();
+ id = p.id();
+ serial = p.serial();
+ creation = p.creation();
+ }
+
+ /**
+ * Create an Erlang pid from its components.
+ *
+ * @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 OtpErlangPid(final String node, final int id, final int serial,
+ final int creation) {
+ this.node = node;
+ this.id = id & 0x7fff; // 15 bits
+ this.serial = serial & 0x1fff; // 13 bits
+ this.creation = creation & 0x03; // 2 bits
+ }
+
+ /**
+ * Get the serial number from the PID.
+ *
+ * @return the serial number from the PID.
+ */
+ public int serial() {
+ return serial;
+ }
+
+ /**
+ * Get the id number from the PID.
+ *
+ * @return the id number from the PID.
+ */
+ public int id() {
+ return id;
+ }
+
+ /**
+ * Get the creation number from the PID.
+ *
+ * @return the creation number from the PID.
+ */
+ public int creation() {
+ return creation;
+ }
+
+ /**
+ * Get the node name from the PID.
+ *
+ * @return the node name from the PID.
+ */
+ public String node() {
+ return node;
+ }
+
+ /**
+ * Get the string representation of the PID. Erlang PIDs are printed as
+ * #Pid&lt;node.id.serial&gt;
+ *
+ * @return the string representation of the PID.
+ */
+ @Override
+ public String toString() {
+ return "#Pid<" + node.toString() + "." + id + "." + serial + ">";
+ }
+
+ /**
+ * Convert this PID to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded PID should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_pid(node, id, serial, creation);
+ }
+
+ /**
+ * Determine if two PIDs are equal. PIDs are equal if their components are
+ * equal.
+ *
+ * @param port
+ * the other PID to compare to.
+ *
+ * @return true if the PIDs are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangPid)) {
+ return false;
+ }
+
+ final OtpErlangPid pid = (OtpErlangPid) o;
+
+ return creation == pid.creation && serial == pid.serial && id == pid.id
+ && node.compareTo(pid.node) == 0;
+ }
+
+ @Override
+ protected int doHashCode() {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(5);
+ hash.combine(creation, serial);
+ hash.combine(id, node.hashCode());
+ return hash.valueOf();
+ }
+
+ public int compareTo(final Object o) {
+ if (!(o instanceof OtpErlangPid)) {
+ return -1;
+ }
+
+ final OtpErlangPid pid = (OtpErlangPid) o;
+ if (creation == pid.creation) {
+ if (serial == pid.serial) {
+ if (id == pid.id) {
+ return node.compareTo(pid.node);
+ } else {
+ return id - pid.id;
+ }
+ } else {
+ return serial - pid.serial;
+ }
+ } else {
+ return creation - pid.creation;
+ }
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java
new file mode 100644
index 0000000000..2a0eab0a9c
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java
@@ -0,0 +1,169 @@
+/*
+ * %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 ports.
+ */
+public class OtpErlangPort extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 4037115468007644704L;
+
+ private final String node;
+ private final int id;
+ private final int creation;
+
+ /*
+ * Create a unique Erlang port belonging to the local node. Since it isn't
+ * meaninful to do so, this constructor is private...
+ *
+ * @param self the local node.
+ *
+ * @deprecated use OtpLocalNode:createPort() instead
+ */
+ private OtpErlangPort(final OtpSelf self) {
+ final OtpErlangPort p = self.createPort();
+
+ id = p.id;
+ creation = p.creation;
+ node = p.node;
+ }
+
+ /**
+ * Create an Erlang port from a stream containing a port encoded in Erlang
+ * external format.
+ *
+ * @param buf
+ * the stream containing the encoded port.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang port.
+ */
+ public OtpErlangPort(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final OtpErlangPort p = buf.read_port();
+
+ node = p.node();
+ id = p.id();
+ creation = p.creation();
+ }
+
+ /**
+ * Create an Erlang port from its components.
+ *
+ * @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 OtpErlangPort(final String node, final int id, final int creation) {
+ this.node = node;
+ this.id = id & 0xfffffff; // 28 bits
+ this.creation = creation & 0x03; // 2 bits
+ }
+
+ /**
+ * Get the id number from the port.
+ *
+ * @return the id number from the port.
+ */
+ public int id() {
+ return id;
+ }
+
+ /**
+ * Get the creation number from the port.
+ *
+ * @return the creation number from the port.
+ */
+ public int creation() {
+ return creation;
+ }
+
+ /**
+ * Get the node name from the port.
+ *
+ * @return the node name from the port.
+ */
+ public String node() {
+ return node;
+ }
+
+ /**
+ * Get the string representation of the port. Erlang ports are printed as
+ * #Port&lt;node.id&gt;.
+ *
+ * @return the string representation of the port.
+ */
+ @Override
+ public String toString() {
+ return "#Port<" + node + "." + id + ">";
+ }
+
+ /**
+ * Convert this port to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded port should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_port(node, id, creation);
+ }
+
+ /**
+ * Determine if two ports are equal. Ports are equal if their components are
+ * equal.
+ *
+ * @param o
+ * the other port to compare to.
+ *
+ * @return true if the ports are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangPort)) {
+ return false;
+ }
+
+ final OtpErlangPort port = (OtpErlangPort) o;
+
+ return creation == port.creation && id == port.id
+ && node.compareTo(port.node) == 0;
+ }
+
+ @Override
+ protected int doHashCode() {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(6);
+ hash.combine(creation);
+ hash.combine(id, node.hashCode());
+ return hash.valueOf();
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRangeException.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRangeException.java
new file mode 100644
index 0000000000..a78b6df6ef
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRangeException.java
@@ -0,0 +1,42 @@
+/*
+ * %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 with data
+ * that is out of range for the term in question.
+ *
+ * @see OtpErlangByte
+ * @see OtpErlangChar
+ * @see OtpErlangInt
+ * @see OtpErlangUInt
+ * @see OtpErlangShort
+ * @see OtpErlangUShort
+ * @see OtpErlangLong
+ */
+public class OtpErlangRangeException extends OtpErlangException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Provides a detailed message.
+ */
+ public OtpErlangRangeException(final String msg) {
+ super(msg);
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java
new file mode 100644
index 0000000000..8056439962
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java
@@ -0,0 +1,264 @@
+/*
+ * %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 refs. There are two styles of Erlang
+ * refs, old style (one id value) and new style (array of id values). This class
+ * manages both types.
+ */
+public class OtpErlangRef extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = -7022666480768586521L;
+
+ private final String node;
+ private final int creation;
+
+ // old style refs have one 18-bit id
+ // r6 "new" refs have array of ids, first one is only 18 bits however
+ private int ids[] = null;
+
+ /**
+ * Create a unique Erlang ref belonging to the local node.
+ *
+ * @param self
+ * the local node.
+ *
+ * @deprecated use OtpLocalNode:createRef() instead
+ */
+ @Deprecated
+ public OtpErlangRef(final OtpLocalNode self) {
+ final OtpErlangRef r = self.createRef();
+
+ ids = r.ids;
+ creation = r.creation;
+ node = r.node;
+ }
+
+ /**
+ * Create an Erlang ref from a stream containing a ref encoded in Erlang
+ * external format.
+ *
+ * @param buf
+ * the stream containing the encoded ref.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang ref.
+ */
+ public OtpErlangRef(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ final OtpErlangRef r = buf.read_ref();
+
+ node = r.node();
+ creation = r.creation();
+
+ ids = r.ids();
+ }
+
+ /**
+ * Create an old style Erlang ref from its components.
+ *
+ * @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 OtpErlangRef(final String node, final int id, final int creation) {
+ this.node = node;
+ ids = new int[1];
+ ids[0] = id & 0x3ffff; // 18 bits
+ this.creation = creation & 0x03; // 2 bits
+ }
+
+ /**
+ * Create a new style Erlang ref from its components.
+ *
+ * @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 OtpErlangRef(final String node, final int[] ids, final int creation) {
+ this.node = node;
+ this.creation = creation & 0x03; // 2 bits
+
+ // use at most 82 bits (18 + 32 + 32)
+ int len = ids.length;
+ this.ids = new int[3];
+ this.ids[0] = 0;
+ this.ids[1] = 0;
+ this.ids[2] = 0;
+
+ if (len > 3) {
+ len = 3;
+ }
+ System.arraycopy(ids, 0, this.ids, 0, len);
+ this.ids[0] &= 0x3ffff; // only 18 significant bits in first number
+ }
+
+ /**
+ * Get the id number from the ref. Old style refs have only one id number.
+ * If this is a new style ref, the first id number is returned.
+ *
+ * @return the id number from the ref.
+ */
+ public int id() {
+ return ids[0];
+ }
+
+ /**
+ * Get the array of id numbers from the ref. If this is an old style ref,
+ * the array is of length 1. If this is a new style ref, the array has
+ * length 3.
+ *
+ * @return the array of id numbers from the ref.
+ */
+ public int[] ids() {
+ return ids;
+ }
+
+ /**
+ * Determine whether this is a new style ref.
+ *
+ * @return true if this ref is a new style ref, false otherwise.
+ */
+ public boolean isNewRef() {
+ return ids.length > 1;
+ }
+
+ /**
+ * Get the creation number from the ref.
+ *
+ * @return the creation number from the ref.
+ */
+ public int creation() {
+ return creation;
+ }
+
+ /**
+ * Get the node name from the ref.
+ *
+ * @return the node name from the ref.
+ */
+ public String node() {
+ return node;
+ }
+
+ /**
+ * Get the string representation of the ref. Erlang refs are printed as
+ * #Ref&lt;node.id&gt;
+ *
+ * @return the string representation of the ref.
+ */
+ @Override
+ public String toString() {
+ String s = "#Ref<" + node;
+
+ for (int i = 0; i < ids.length; i++) {
+ s += "." + ids[i];
+ }
+
+ s += ">";
+
+ return s;
+ }
+
+ /**
+ * Convert this ref to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded ref should be
+ * written.
+ */
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_ref(node, ids, creation);
+ }
+
+ /**
+ * Determine if two refs are equal. Refs are equal if their components are
+ * equal. New refs and old refs are considered equal if the node, creation
+ * and first id numnber are equal.
+ *
+ * @param o
+ * the other ref to compare to.
+ *
+ * @return true if the refs are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof OtpErlangRef)) {
+ return false;
+ }
+
+ final OtpErlangRef ref = (OtpErlangRef) o;
+
+ if (!(node.equals(ref.node()) && creation == ref.creation())) {
+ return false;
+ }
+
+ if (isNewRef() && ref.isNewRef()) {
+ return ids[0] == ref.ids[0] && ids[1] == ref.ids[1]
+ && ids[2] == ref.ids[2];
+ }
+ return ids[0] == ref.ids[0];
+ }
+
+ /**
+ * Compute the hashCode value for a given ref. This function is compatible
+ * with equal.
+ *
+ * @return the hashCode of the node.
+ **/
+
+ @Override
+ protected int doHashCode() {
+ OtpErlangObject.Hash hash = new OtpErlangObject.Hash(7);
+ hash.combine(creation, ids[0]);
+ if (isNewRef()) {
+ hash.combine(ids[1], ids[2]);
+ }
+ return hash.valueOf();
+ }
+
+ @Override
+ public Object clone() {
+ final OtpErlangRef newRef = (OtpErlangRef) super.clone();
+ newRef.ids = ids.clone();
+ return newRef;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java
new file mode 100644
index 0000000000..cd232570dd
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java
@@ -0,0 +1,63 @@
+/*
+ * %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 OtpErlangShort extends OtpErlangLong implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = 7162345156603088099L;
+
+ /**
+ * Create an Erlang integer from the given value.
+ *
+ * @param s
+ * the short value to use.
+ */
+ public OtpErlangShort(final short s) {
+ super(s);
+ }
+
+ /**
+ * 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.
+ */
+ public OtpErlangShort(final OtpInputStream buf)
+ throws OtpErlangRangeException, OtpErlangDecodeException {
+ super(buf);
+
+ final short j = shortValue();
+ }
+
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
new file mode 100644
index 0000000000..19ee92e0d0
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
@@ -0,0 +1,198 @@
+/*
+ * %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;
+import java.lang.Character;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Provides a Java representation of Erlang strings.
+ */
+public class OtpErlangString extends OtpErlangObject implements Serializable,
+ Cloneable {
+ // don't change this!
+ static final long serialVersionUID = -7053595217604929233L;
+
+ private final String str;
+
+ /**
+ * Create an Erlang string from the given string.
+ */
+ public OtpErlangString(final String str) {
+ this.str = str;
+ }
+
+ /**
+ * Create an Erlang string from a list of integers.
+ *
+ * @return an Erlang string with Unicode code units.
+ *
+ * @throws OtpErlangException
+ * for non-proper and non-integer lists.
+ * @throws OtpErlangRangeException
+ * if an integer in the list is not
+ * a valid Unicode code point according to Erlang.
+ */
+ public OtpErlangString(final OtpErlangList list)
+ throws OtpErlangException {
+ String s = list.stringValue();
+ final int n = s.length();
+ for (int i = 0; i < n; i = s.offsetByCodePoints(i, 1)) {
+ int cp = s.codePointAt(i);
+ if (! isValidCodePoint(cp)) {
+ throw new OtpErlangRangeException("Invalid CodePoint: " + cp);
+ }
+ }
+ str = s;
+ }
+
+ /**
+ * Create an Erlang string from a stream containing a string encoded in
+ * Erlang external format.
+ *
+ * @param buf
+ * the stream containing the encoded string.
+ *
+ * @exception OtpErlangDecodeException
+ * if the buffer does not contain a valid external
+ * representation of an Erlang string.
+ */
+ public OtpErlangString(final OtpInputStream buf)
+ throws OtpErlangDecodeException {
+ str = buf.read_string();
+ }
+
+ /**
+ * 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 stringValue() {
+ return str;
+ }
+
+ /**
+ * Get the printable version of the string contained in this object.
+ *
+ * @return the string contained in this object, quoted.
+ *
+ * @see #stringValue
+ */
+
+ @Override
+ public String toString() {
+ return "\"" + str + "\"";
+ }
+
+ /**
+ * Convert this string to the equivalent Erlang external representation.
+ *
+ * @param buf
+ * an output stream to which the encoded string should be
+ * written.
+ */
+
+ @Override
+ public void encode(final OtpOutputStream buf) {
+ buf.write_string(str);
+ }
+
+ /**
+ * Determine if two strings are equal. They are equal if they represent the
+ * same sequence of characters. This method can be used to compare
+ * OtpErlangStrings with each other and with Strings.
+ *
+ * @param o
+ * the OtpErlangString or String to compare to.
+ *
+ * @return true if the strings consist of the same sequence of characters,
+ * false otherwise.
+ */
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o instanceof String) {
+ return str.compareTo((String) o) == 0;
+ } else if (o instanceof OtpErlangString) {
+ return str.compareTo(((OtpErlangString) o).str) == 0;
+ }
+
+ return false;
+ }
+
+ protected int doHashCode() {
+ return str.hashCode();
+ }
+
+ /**
+ * Create Unicode code points from a String.
+ *
+ * @param s
+ * a String to convert to an Unicode code point array
+ *
+ * @return the corresponding array of integers representing
+ * Unicode code points
+ */
+
+ public static int[] stringToCodePoints(final String s) {
+ final int m = s.codePointCount(0, s.length());
+ final int [] codePoints = new int[m];
+ for (int i = 0, j = 0; j < m; i = s.offsetByCodePoints(i, 1), j++) {
+ codePoints[j] = s.codePointAt(i);
+ }
+ return codePoints;
+ }
+
+ /**
+ * Validate a code point according to Erlang definition; Unicode 3.0.
+ * That is; valid in the range U+0..U+10FFFF, but not in the range
+ * U+D800..U+DFFF (surrogat pairs), nor U+FFFE..U+FFFF (non-characters).
+ *
+ * @param cp
+ * the code point value to validate
+ *
+ * @return true if the code point is valid,
+ * false otherwise.
+ */
+
+ public static boolean isValidCodePoint(final int cp) {
+ // Erlang definition of valid Unicode code points;
+ // Unicode 3.0, XML, et.al.
+ return (cp>>>16) <= 0x10 // in 0..10FFFF; Unicode range
+ && (cp & ~0x7FF) != 0xD800 // not in D800..DFFF; surrogate range
+ && (cp & ~1) != 0xFFFE; // not in FFFE..FFFF; non-characters
+ }
+
+ /**
+ * Construct a String from a Latin-1 (ISO-8859-1) encoded byte array,
+ * if Latin-1 is available, otherwise use the default encoding.
+ *
+ */
+ public static String newString(final byte[] bytes) {
+ try {
+ return new String(bytes, "ISO-8859-1");
+ } catch (final UnsupportedEncodingException e) {
+ }
+ return new String(bytes);
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
new file mode 100644
index 0000000000..b3a1021992
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
@@ -0,0 +1,261 @@
+/*
+ * %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 tuples. Tuples are created from one
+ * or more arbitrary Erlang terms.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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;
+
+/**
+ * <p>
+ * Provides a simple mechanism for exchanging messages with Erlang processes or
+ * other instances of this class.
+ * </p>
+ *
+ * <p>
+ * 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()}.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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}.
+ * </p>
+ *
+ * <p>
+ * Messages to remote nodes are externalized for transmission, and as a result
+ * the recipient receives a <b>copy</b> of the original Java object. To ensure
+ * consistent behaviour when messages are sent between local mailboxes, such
+ * messages are cloned before delivery.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * When retrieving messages from a mailbox that has received an exit signal, an
+ * {@link OtpErlangExit OtpErlangExit} exception will be raised. Note that the
+ * exception is queued in the mailbox along with other messages, and will not be
+ * raised until it reaches the head of the queue and is about to be retrieved.
+ * </p>
+ *
+ */
+public class OtpMbox {
+ OtpNode home;
+ OtpErlangPid self;
+ GenericQueue queue;
+ String name;
+ Links links;
+
+ // package constructor: called by OtpNode:createMbox(name)
+ // to create a named mbox
+ OtpMbox(final OtpNode home, final OtpErlangPid self, final String name) {
+ this.self = self;
+ this.home = home;
+ this.name = name;
+ queue = new GenericQueue();
+ links = new Links(10);
+ }
+
+ // package constructor: called by OtpNode:createMbox()
+ // to create an anonymous
+ OtpMbox(final OtpNode home, final OtpErlangPid self) {
+ this(home, self, null);
+ }
+
+ /**
+ * <p>
+ * Get the identifying {@link OtpErlangPid pid} associated with this
+ * mailbox.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @return the self pid for this mailbox.
+ */
+ public OtpErlangPid self() {
+ return self;
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @param name
+ * the name to register for the mailbox. Specify null to
+ * unregister the existing name from this mailbox.
+ *
+ * @return true if the name was available, or false otherwise.
+ */
+ public synchronized boolean registerName(final String name) {
+ return home.registerName(name, this);
+ }
+
+ /**
+ * Get the registered name of this mailbox.
+ *
+ * @return the registered name of this mailbox, or null if the mailbox had
+ * no registerd name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Block until a message arrives for this mailbox.
+ *
+ * @return an {@link OtpErlangObject OtpErlangObject} representing the body
+ * of the next message waiting in this mailbox.
+ *
+ * @exception OtpErlangDecodeException
+ * if the message can not be decoded.
+ *
+ * @exception OtpErlangExit
+ * if a linked {@link OtpErlangPid pid} has exited or has
+ * sent an exit signal to this mailbox.
+ */
+ public OtpErlangObject receive() throws OtpErlangExit,
+ OtpErlangDecodeException {
+ try {
+ return receiveMsg().getMsg();
+ } catch (final OtpErlangExit e) {
+ throw e;
+ } catch (final OtpErlangDecodeException f) {
+ throw f;
+ }
+ }
+
+ /**
+ * Wait for a message to arrive for this mailbox.
+ *
+ * @param timeout
+ * the time, in milliseconds, to wait for a message before
+ * returning null.
+ *
+ * @return an {@link OtpErlangObject OtpErlangObject} representing the body
+ * of the next message waiting in this mailbox.
+ *
+ * @exception OtpErlangDecodeException
+ * if the message can not be decoded.
+ *
+ * @exception OtpErlangExit
+ * if a linked {@link OtpErlangPid pid} has exited or has
+ * sent an exit signal to this mailbox.
+ */
+ public OtpErlangObject receive(final long timeout) throws OtpErlangExit,
+ OtpErlangDecodeException {
+ try {
+ final OtpMsg m = receiveMsg(timeout);
+ if (m != null) {
+ return m.getMsg();
+ }
+ } catch (final OtpErlangExit e) {
+ throw e;
+ } catch (final OtpErlangDecodeException f) {
+ throw f;
+ } catch (final InterruptedException g) {
+ }
+ return null;
+ }
+
+ /**
+ * Block until a message arrives for this mailbox.
+ *
+ * @return a byte array representing the still-encoded body of the next
+ * message waiting in this mailbox.
+ *
+ * @exception OtpErlangExit
+ * if a linked {@link OtpErlangPid pid} has exited or has
+ * sent an exit signal to this mailbox.
+ *
+ */
+ public OtpInputStream receiveBuf() throws OtpErlangExit {
+ return receiveMsg().getMsgBuf();
+ }
+
+ /**
+ * Wait for a message to arrive for this mailbox.
+ *
+ * @param timeout
+ * the time, in milliseconds, to wait for a message before
+ * returning null.
+ *
+ * @return a byte array representing the still-encoded body of the next
+ * message waiting in this mailbox.
+ *
+ * @exception OtpErlangExit
+ * if a linked {@link OtpErlangPid pid} has exited or has
+ * sent an exit signal to this mailbox.
+ *
+ * @exception InterruptedException
+ * if no message if the method times out before a message
+ * becomes available.
+ */
+ public OtpInputStream receiveBuf(final long timeout)
+ throws InterruptedException, OtpErlangExit {
+ final OtpMsg m = receiveMsg(timeout);
+ if (m != null) {
+ return m.getMsgBuf();
+ }
+
+ return null;
+ }
+
+ /**
+ * Block until a message arrives for this mailbox.
+ *
+ * @return an {@link OtpMsg OtpMsg} containing the header information as
+ * well as the body of the next message waiting in this mailbox.
+ *
+ * @exception OtpErlangExit
+ * if a linked {@link OtpErlangPid pid} has exited or has
+ * sent an exit signal to this mailbox.
+ *
+ */
+ public OtpMsg receiveMsg() throws OtpErlangExit {
+
+ final OtpMsg m = (OtpMsg) queue.get();
+
+ switch (m.type()) {
+ case OtpMsg.exitTag:
+ case OtpMsg.exit2Tag:
+ try {
+ final OtpErlangObject o = m.getMsg();
+ throw new OtpErlangExit(o, m.getSenderPid());
+ } catch (final OtpErlangDecodeException e) {
+ throw new OtpErlangExit("unknown", m.getSenderPid());
+ }
+
+ default:
+ return m;
+ }
+ }
+
+ /**
+ * Wait for a message to arrive for this mailbox.
+ *
+ * @param timeout
+ * the time, in milliseconds, to wait for a message.
+ *
+ * @return an {@link OtpMsg OtpMsg} containing the header information as
+ * well as the body of the next message waiting in this mailbox.
+ *
+ * @exception OtpErlangExit
+ * if a linked {@link OtpErlangPid pid} has exited or has
+ * sent an exit signal to this mailbox.
+ *
+ * @exception InterruptedException
+ * if no message if the method times out before a message
+ * becomes available.
+ */
+ public OtpMsg receiveMsg(final long timeout) throws InterruptedException,
+ OtpErlangExit {
+ final OtpMsg m = (OtpMsg) queue.get(timeout);
+
+ if (m == null) {
+ return null;
+ }
+
+ switch (m.type()) {
+ case OtpMsg.exitTag:
+ case OtpMsg.exit2Tag:
+ try {
+ final OtpErlangObject o = m.getMsg();
+ throw new OtpErlangExit(o, m.getSenderPid());
+ } catch (final OtpErlangDecodeException e) {
+ throw new OtpErlangExit("unknown", m.getSenderPid());
+ }
+
+ default:
+ return m;
+ }
+ }
+
+ /**
+ * Send a message to a remote {@link OtpErlangPid pid}, representing either
+ * another {@link OtpMbox mailbox} or an Erlang process.
+ *
+ * @param to
+ * the {@link OtpErlangPid pid} identifying the intended
+ * recipient of the message.
+ *
+ * @param msg
+ * the body of the message to send.
+ *
+ */
+ public void send(final OtpErlangPid to, final OtpErlangObject msg) {
+ try {
+ final String node = to.node();
+ if (node.equals(home.node())) {
+ home.deliver(new OtpMsg(to, (OtpErlangObject) msg.clone()));
+ } else {
+ final OtpCookedConnection conn = home.getConnection(node);
+ if (conn == null) {
+ return;
+ }
+ conn.send(self, to, msg);
+ }
+ } catch (final Exception e) {
+ }
+ }
+
+ /**
+ * Send a message to a named mailbox created from the same node as this
+ * mailbox.
+ *
+ * @param name
+ * the registered name of recipient mailbox.
+ *
+ * @param msg
+ * the body of the message to send.
+ *
+ */
+ public void send(final String name, final OtpErlangObject msg) {
+ home.deliver(new OtpMsg(self, name, (OtpErlangObject) msg.clone()));
+ }
+
+ /**
+ * Send a message to a named mailbox created from another node.
+ *
+ * @param name
+ * the registered name of recipient mailbox.
+ *
+ * @param node
+ * the name of the remote node where the recipient mailbox is
+ * registered.
+ *
+ * @param msg
+ * the body of the message to send.
+ *
+ */
+ public void send(final String name, final String node,
+ final OtpErlangObject msg) {
+ try {
+ final String currentNode = home.node();
+ if (node.equals(currentNode)) {
+ send(name, msg);
+ } else if (node.indexOf('@', 0) < 0
+ && node.equals(currentNode.substring(0, currentNode
+ .indexOf('@', 0)))) {
+ send(name, msg);
+ } else {
+ // other node
+ final OtpCookedConnection conn = home.getConnection(node);
+ if (conn == null) {
+ return;
+ }
+ conn.send(self, name, msg);
+ }
+ } catch (final Exception e) {
+ }
+ }
+
+ /**
+ * Close this mailbox with the given reason.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @param reason
+ * an Erlang term describing the reason for the exit.
+ */
+ public void exit(final OtpErlangObject reason) {
+ home.closeMbox(this, reason);
+ }
+
+ /**
+ * Equivalent to <code>exit(new OtpErlangAtom(reason))</code>.
+ * </p>
+ *
+ * @see #exit(OtpErlangObject)
+ */
+ public void exit(final String reason) {
+ exit(new OtpErlangAtom(reason));
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * Equivalent to <code>exit(to, new
+ * OtpErlangAtom(reason))</code>.
+ * </p>
+ *
+ * @see #exit(OtpErlangPid, OtpErlangObject)
+ */
+ public void exit(final OtpErlangPid to, final String reason) {
+ exit(to, new OtpErlangAtom(reason));
+ }
+
+ // this function used internally when "process" dies
+ // since Erlang discerns between exit and exit/2.
+ private void exit(final int arity, final OtpErlangPid to,
+ final OtpErlangObject reason) {
+ try {
+ final String node = to.node();
+ if (node.equals(home.node())) {
+ home.deliver(new OtpMsg(OtpMsg.exitTag, self, to, reason));
+ } else {
+ final OtpCookedConnection conn = home.getConnection(node);
+ if (conn == null) {
+ return;
+ }
+ switch (arity) {
+ case 1:
+ conn.exit(self, to, reason);
+ break;
+
+ case 2:
+ conn.exit2(self, to, reason);
+ break;
+ }
+ }
+ } catch (final Exception e) {
+ }
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * If the remote process cannot be reached in order to set the link, the
+ * exception is raised immediately.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * 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}.
+ * </p>
+ *
+ * @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) {
+ }
+ }
+
+ /**
+ * <p>
+ * Create a connection to a remote node.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * This method makes it possible to wait for a node to come up, however, or
+ * check that a node is still alive.
+ * </p>
+ *
+ * <p>
+ * This method calls a method with the same name in {@link OtpNode#ping
+ * Otpnode} but is provided here for convenience.
+ * </p>
+ *
+ * @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);
+ }
+
+ /**
+ * <p>
+ * Get a list of all known registered names on the same {@link OtpNode node}
+ * as this mailbox.
+ * </p>
+ *
+ * <p>
+ * This method calls a method with the same name in {@link OtpNode#getNames
+ * Otpnode} but is provided here for convenience.
+ * </p>
+ *
+ * @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}.
+ *
+ * <p>
+ * This method calls a method with the same name in {@link OtpNode#whereis
+ * Otpnode} but is provided here for convenience.
+ * </p>
+ *
+ * @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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * This is equivalent to {@link #exit(String) exit("normal")}.
+ * </p>
+ */
+ 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;
+
+/**
+ * <p>
+ * Provides a carrier for Erlang messages.
+ * </p>
+ *
+ * <p>
+ * 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()}.
+ * </p>
+ *
+ * <p>
+ * The header information that is available is as follows: <lu>
+ * <li> a tag indicating the type of message
+ * <li> the intended recipient of the message, either as a
+ * {@link OtpErlangPid pid} or as a String, but never both.
+ * <li> (sometimes) the sender of the message. Due to some eccentric
+ * characteristics of the Erlang distribution protocol, not all messages have
+ * information about the sending process. In particular, only messages whose tag
+ * is {@link OtpMsg#regSendTag regSendTag} contain sender information. </lu>
+ *
+ * <p>
+ * 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.
+ * </p>
+ */
+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;
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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:
+ * </p>
+ *
+ * <ul>
+ * <li> sendTag identifies a "normal" message. The recipient is a
+ * {@link OtpErlangPid Pid} and it is available through {@link
+ * #getRecipientPid getRecipientPid()}. Sender information is not available.
+ * The message body can be retrieved with {@link #getMsg getMsg()}. </li>
+ *
+ * <li> regSendTag also identifies a "normal" message. The recipient here is
+ * a String and it is available through {@link #getRecipientName
+ * getRecipientName()}. Sender information is available through
+ * #getSenderPid getSenderPid()}. The message body can be retrieved with
+ * {@link #getMsg getMsg()}. </li>
+ *
+ * <li> linkTag identifies a link request. The Pid of the sender is
+ * available, as well as the Pid to which the link should be made. </li>
+ *
+ * <li> exitTag and exit2Tag messages are sent as a result of broken links.
+ * Both sender and recipient Pids and are available through the
+ * corresponding methods, and the "reason" is available through
+ * {@link #getMsg getMsg()}. </li>
+ * </ul>
+ */
+ public int type() {
+ return tag;
+ }
+
+ /**
+ * <p>
+ * Deserialize and return a new copy of the message contained in this
+ * OtpMsg.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>
+ * Get the name of the recipient for this message.
+ * </p>
+ *
+ * <p>
+ * Messages are sent to Pids or names. If this message was sent to a name
+ * then the name is returned by this method.
+ * </p>
+ *
+ * @return the name of the recipient, or null if the recipient was in fact a
+ * Pid.
+ */
+ public String getRecipientName() {
+ return toName;
+ }
+
+ /**
+ * <p>
+ * Get the Pid of the recipient for this message, if it is a sendTag
+ * message.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @return the Pid of the recipient, or null if the recipient was in fact a
+ * name.
+ */
+ public OtpErlangPid getRecipientPid() {
+ return to;
+ }
+
+ /**
+ * <p>
+ * Get the name of the recipient for this message, if it is a regSendTag
+ * message.
+ * </p>
+ *
+ * <p>
+ * Messages are sent to Pids or names. If this message was sent to a name
+ * then the name is returned by this method.
+ * </p>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>
+ * Get the Pid of the sender of this message.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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;
+
+/**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ */
+public class OtpNode extends OtpLocalNode {
+ private boolean initDone = false;
+
+ // thread to manage incoming connections
+ private Acceptor acceptor = null;
+
+ // keep track of all connections
+ Hashtable<String, OtpCookedConnection> connections = null;
+
+ // keep track of all mailboxes
+ Mailboxes mboxes = null;
+
+ // handle status changes
+ OtpNodeStatus handler;
+
+ // flags
+ private int flags = 0;
+
+ /**
+ * <p>
+ * Create a node using the default cookie. The default cookie is found by
+ * reading the first line of the .erlang.cookie file in the user's home
+ * directory. The home directory is obtained from the System property
+ * "user.home".
+ * </p>
+ *
+ * <p>
+ * If the file does not exist, an empty string is used. This method makes no
+ * attempt to create the file.
+ * </p>
+ *
+ * @param node
+ * the name of this node.
+ *
+ * @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<String, OtpCookedConnection>(17,
+ (float) 0.95);
+ mboxes = new Mailboxes();
+ acceptor = new Acceptor(port);
+ initDone = true;
+ }
+ }
+
+ /**
+ * Close the node. Unpublish the node from Epmd (preventing new connections)
+ * and close all existing connections.
+ */
+ public synchronized void close() {
+ acceptor.quit();
+ OtpCookedConnection conn;
+ final Collection<OtpCookedConnection> coll = connections.values();
+ final Iterator<OtpCookedConnection> it = coll.iterator();
+
+ mboxes.clear();
+
+ while (it.hasNext()) {
+ conn = it.next();
+ it.remove();
+ conn.close();
+ }
+ initDone = false;
+ }
+
+ @Override
+ protected void finalize() {
+ close();
+ }
+
+ /**
+ * Create an unnamed {@link OtpMbox 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 associated
+ * {@link OtpMbox#self pid}.
+ *
+ * @return a mailbox.
+ */
+ public OtpMbox createMbox() {
+ return mboxes.create();
+ }
+
+ /**
+ * Close the specified mailbox with reason 'normal'.
+ *
+ * @param mbox
+ * the mailbox to close.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ */
+ 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.
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ */
+ 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);
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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:
+ * </p>
+ *
+ * <pre>
+ * // ping every 2 seconds until positive response
+ * while (!me.ping(him, 2000))
+ * ;
+ * </pre>
+ *
+ * @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<[email protected]>,'',net_kernel}
+ * {'$gen_call',{#Pid<[email protected]>,#Ref<[email protected]>},{is_auth,bingo@aule}}
+ *
+ * the reply: <- SEND {2,'',#Pid<[email protected]>} {#Ref<[email protected]>,yes}
+ */
+ public boolean ping(final String node, final long timeout) {
+ if (node.equals(this.node)) {
+ return true;
+ } else if (node.indexOf('@', 0) < 0
+ && node.equals(this.node
+ .substring(0, this.node.indexOf('@', 0)))) {
+ return true;
+ }
+
+ // other node
+ OtpMbox mbox = null;
+ try {
+ mbox = createMbox();
+ mbox.send("net_kernel", node, getPingTuple(mbox));
+ final OtpErlangObject reply = mbox.receive(timeout);
+
+ final OtpErlangTuple t = (OtpErlangTuple) reply;
+ final OtpErlangAtom a = (OtpErlangAtom) t.elementAt(1);
+ return "yes".equals(a.atomValue());
+ } catch (final Exception e) {
+ } finally {
+ closeMbox(mbox);
+ }
+ return false;
+ }
+
+ /* create the outgoing ping message */
+ private OtpErlangTuple getPingTuple(final OtpMbox mbox) {
+ final OtpErlangObject[] ping = new OtpErlangObject[3];
+ final OtpErlangObject[] pid = new OtpErlangObject[2];
+ final OtpErlangObject[] node = new OtpErlangObject[2];
+
+ pid[0] = mbox.self();
+ pid[1] = createRef();
+
+ node[0] = new OtpErlangAtom("is_auth");
+ node[1] = new OtpErlangAtom(node());
+
+ ping[0] = new OtpErlangAtom("$gen_call");
+ ping[1] = new OtpErlangTuple(pid);
+ ping[2] = new OtpErlangTuple(node);
+
+ return new OtpErlangTuple(ping);
+ }
+
+ /*
+ * this method simulates net_kernel only for the purpose of replying to
+ * pings.
+ */
+ private boolean netKernel(final OtpMsg m) {
+ OtpMbox mbox = null;
+ try {
+ final OtpErlangTuple t = (OtpErlangTuple) m.getMsg();
+ final OtpErlangTuple req = (OtpErlangTuple) t.elementAt(1); // actual
+ // request
+
+ final OtpErlangPid pid = (OtpErlangPid) req.elementAt(0); // originating
+ // pid
+
+ final OtpErlangObject[] pong = new OtpErlangObject[2];
+ pong[0] = req.elementAt(1); // his #Ref
+ pong[1] = new OtpErlangAtom("yes");
+
+ mbox = createMbox();
+ mbox.send(pid, new OtpErlangTuple(pong));
+ return true;
+ } catch (final Exception e) {
+ } finally {
+ closeMbox(mbox);
+ }
+ return false;
+ }
+
+ /*
+ * OtpCookedConnection delivers messages here return true if message was
+ * delivered successfully, or false otherwise.
+ */
+ boolean deliver(final OtpMsg m) {
+ OtpMbox mbox = null;
+
+ try {
+ final int t = m.type();
+
+ if (t == OtpMsg.regSendTag) {
+ final String name = m.getRecipientName();
+ /* special case for netKernel requests */
+ if (name.equals("net_kernel")) {
+ return netKernel(m);
+ } else {
+ mbox = mboxes.get(name);
+ }
+ } else {
+ mbox = mboxes.get(m.getRecipientPid());
+ }
+
+ if (mbox == null) {
+ return false;
+ }
+ mbox.deliver(m);
+ } catch (final Exception e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * OtpCookedConnection delivers errors here, we send them on to the handler
+ * specified by the application
+ */
+ void deliverError(final OtpCookedConnection conn, final Exception e) {
+ removeConnection(conn);
+ remoteStatus(conn.name, false, e);
+ }
+
+ /*
+ * find or create a connection to the given node
+ */
+ OtpCookedConnection getConnection(final String node) {
+ OtpPeer peer = null;
+ OtpCookedConnection conn = null;
+
+ synchronized (connections) {
+ // first just try looking up the name as-is
+ conn = connections.get(node);
+
+ if (conn == null) {
+ // in case node had no '@' add localhost info and try again
+ peer = new OtpPeer(node);
+ conn = connections.get(peer.node());
+
+ if (conn == null) {
+ try {
+ conn = new OtpCookedConnection(this, peer);
+ conn.setFlags(flags);
+ addConnection(conn);
+ } catch (final Exception e) {
+ /* false = outgoing */
+ connAttempt(peer.node(), false, e);
+ }
+ }
+ }
+ return conn;
+ }
+ }
+
+ void addConnection(final OtpCookedConnection conn) {
+ if (conn != null && conn.name != null) {
+ connections.put(conn.name, conn);
+ remoteStatus(conn.name, true, null);
+ }
+ }
+
+ private void removeConnection(final OtpCookedConnection conn) {
+ if (conn != null && conn.name != null) {
+ connections.remove(conn.name);
+ }
+ }
+
+ /* use these wrappers to call handler functions */
+ private synchronized void remoteStatus(final String node, final boolean up,
+ final Object info) {
+ if (handler == null) {
+ return;
+ }
+ try {
+ handler.remoteStatus(node, up, info);
+ } catch (final Exception e) {
+ }
+ }
+
+ synchronized void localStatus(final String node, final boolean up,
+ final Object info) {
+ if (handler == null) {
+ return;
+ }
+ try {
+ handler.localStatus(node, up, info);
+ } catch (final Exception e) {
+ }
+ }
+
+ synchronized void connAttempt(final String node, final boolean incoming,
+ final Object info) {
+ if (handler == null) {
+ return;
+ }
+ try {
+ handler.connAttempt(node, incoming, info);
+ } catch (final Exception e) {
+ }
+ }
+
+ /*
+ * this class used to wrap the mailbox hashtables so we can use weak
+ * references
+ */
+ public class Mailboxes {
+ // mbox pids here
+ private Hashtable<OtpErlangPid, WeakReference<OtpMbox>> byPid = null;
+ // mbox names here
+ private Hashtable<String, WeakReference<OtpMbox>> byName = null;
+
+ public Mailboxes() {
+ byPid = new Hashtable<OtpErlangPid, WeakReference<OtpMbox>>(17,
+ (float) 0.95);
+ byName = new Hashtable<String, WeakReference<OtpMbox>>(17,
+ (float) 0.95);
+ }
+
+ public OtpMbox create(final String name) {
+ OtpMbox m = null;
+
+ synchronized (byName) {
+ if (get(name) != null) {
+ return null;
+ }
+ final OtpErlangPid pid = createPid();
+ m = new OtpMbox(OtpNode.this, pid, name);
+ byPid.put(pid, new WeakReference<OtpMbox>(m));
+ byName.put(name, new WeakReference<OtpMbox>(m));
+ }
+ return m;
+ }
+
+ public OtpMbox create() {
+ final OtpErlangPid pid = createPid();
+ final OtpMbox m = new OtpMbox(OtpNode.this, pid);
+ byPid.put(pid, new WeakReference<OtpMbox>(m));
+ return m;
+ }
+
+ public void clear() {
+ byPid.clear();
+ byName.clear();
+ }
+
+ public String[] names() {
+ String allnames[] = null;
+
+ synchronized (byName) {
+ final int n = byName.size();
+ final Enumeration<String> keys = byName.keys();
+ allnames = new String[n];
+
+ int i = 0;
+ while (keys.hasMoreElements()) {
+ allnames[i++] = keys.nextElement();
+ }
+ }
+ return allnames;
+ }
+
+ public boolean register(final String name, final OtpMbox mbox) {
+ if (name == null) {
+ if (mbox.name != null) {
+ byName.remove(mbox.name);
+ mbox.name = null;
+ }
+ } else {
+ synchronized (byName) {
+ if (get(name) != null) {
+ return false;
+ }
+ byName.put(name, new WeakReference<OtpMbox>(mbox));
+ mbox.name = name;
+ }
+ }
+ return true;
+ }
+
+ /*
+ * look up a mailbox based on its name. If the mailbox has gone out of
+ * scope we also remove the reference from the hashtable so we don't
+ * find it again.
+ */
+ public OtpMbox get(final String name) {
+ final WeakReference<OtpMbox> wr = byName.get(name);
+
+ if (wr != null) {
+ final OtpMbox m = wr.get();
+
+ if (m != null) {
+ return m;
+ }
+ byName.remove(name);
+ }
+ return null;
+ }
+
+ /*
+ * look up a mailbox based on its pid. If the mailbox has gone out of
+ * scope we also remove the reference from the hashtable so we don't
+ * find it again.
+ */
+ public OtpMbox get(final OtpErlangPid pid) {
+ final WeakReference<OtpMbox> wr = byPid.get(pid);
+
+ if (wr != null) {
+ final OtpMbox m = wr.get();
+
+ if (m != null) {
+ return m;
+ }
+ byPid.remove(pid);
+ }
+ return null;
+ }
+
+ public void remove(final OtpMbox mbox) {
+ byPid.remove(mbox.self);
+ if (mbox.name != null) {
+ byName.remove(mbox.name);
+ }
+ }
+ }
+
+ /*
+ * this thread simply listens for incoming connections
+ */
+ public class Acceptor extends Thread {
+ private final ServerSocket sock;
+ private final int port;
+ private volatile boolean done = false;
+
+ Acceptor(final int port) throws IOException {
+ sock = new ServerSocket(port);
+ this.port = sock.getLocalPort();
+ OtpNode.this.port = this.port;
+
+ setDaemon(true);
+ setName("acceptor");
+ publishPort();
+ start();
+ }
+
+ private boolean publishPort() throws IOException {
+ if (getEpmd() != null) {
+ return false; // already published
+ }
+ OtpEpmd.publishPort(OtpNode.this);
+ return true;
+ }
+
+ private void unPublishPort() {
+ // unregister with epmd
+ OtpEpmd.unPublishPort(OtpNode.this);
+
+ // close the local descriptor (if we have one)
+ closeSock(epmd);
+ epmd = null;
+ }
+
+ public void quit() {
+ unPublishPort();
+ done = true;
+ closeSock(sock);
+ localStatus(node, false, null);
+ }
+
+ private void closeSock(final ServerSocket s) {
+ try {
+ if (s != null) {
+ s.close();
+ }
+ } catch (final Exception e) {
+ }
+ }
+
+ private void closeSock(final Socket s) {
+ try {
+ if (s != null) {
+ s.close();
+ }
+ } catch (final Exception e) {
+ }
+ }
+
+ public int port() {
+ return port;
+ }
+
+ @Override
+ public void run() {
+ Socket newsock = null;
+ OtpCookedConnection conn = null;
+
+ localStatus(node, true, null);
+
+ accept_loop: while (!done) {
+ conn = null;
+
+ try {
+ newsock = sock.accept();
+ } catch (final Exception e) {
+ // Problem in java1.2.2: accept throws SocketException
+ // when socket is closed. This will happen when
+ // acceptor.quit()
+ // is called. acceptor.quit() will call localStatus(...), so
+ // we have to check if that's where we come from.
+ if (!done) {
+ localStatus(node, false, e);
+ }
+ break accept_loop;
+ }
+
+ try {
+ synchronized (connections) {
+ conn = new OtpCookedConnection(OtpNode.this, newsock);
+ conn.setFlags(flags);
+ addConnection(conn);
+ }
+ } catch (final OtpAuthException e) {
+ if (conn != null && conn.name != null) {
+ connAttempt(conn.name, true, e);
+ } else {
+ connAttempt("unknown", true, e);
+ }
+ closeSock(newsock);
+ } catch (final IOException e) {
+ if (conn != null && conn.name != null) {
+ connAttempt(conn.name, true, e);
+ } else {
+ connAttempt("unknown", true, e);
+ }
+ closeSock(newsock);
+ } catch (final Exception e) {
+ closeSock(newsock);
+ closeSock(sock);
+ localStatus(node, false, e);
+ break accept_loop;
+ }
+ } // while
+
+ // if we have exited loop we must do this too
+ unPublishPort();
+ }
+ }
+
+ public void setFlags(final int flags) {
+ this.flags = flags;
+ }
+}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNodeStatus.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNodeStatus.java
new file mode 100644
index 0000000000..aee1f8b67a
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNodeStatus.java
@@ -0,0 +1,100 @@
+/*
+ * %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;
+
+/**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * <b> Note that this class is likely to change in the near future </b>
+ * </p>
+ */
+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.
+ *
+ * <p>
+ * 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:
+ *
+ * <pre>
+ * 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);
+ * </pre>
+ *
+ *
+ * @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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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()}.
+ * </p>
+ *
+ * <pre>
+ * OtpSelf self = new OtpSelf(&quot;client&quot;, &quot;authcookie&quot;); // identify self
+ * OtpPeer other = new OtpPeer(&quot;server&quot;); // identify peer
+ *
+ * OtpConnection conn = self.connect(other); // connect to peer
+ * </pre>
+ *
+ */
+public class OtpSelf extends OtpLocalNode {
+ private final ServerSocket sock;
+ private final OtpErlangPid pid;
+
+ /**
+ * <p>
+ * 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".
+ * </p>
+ *
+ * <p>
+ * If the file does not exist, an empty string is used. This method makes no
+ * attempt to create the file.
+ * </p>
+ *
+ * @param node
+ * the name of this node.
+ *
+ */
+ 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.
+ *
+ * <p>
+ * This method will fail if an Epmd process is not running on the localhost.
+ * See the Erlang documentation for information about starting Epmd.
+ *
+ * <p>
+ * 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()}.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * Setting up a connection may be done as follows:
+ *
+ *
+ * <pre>
+ * OtpServer self = new OtpServer(&quot;server&quot;, &quot;cookie&quot;); // identify self
+ * self.publishPort(); // make port information available
+ *
+ * OtpConnection conn = self.accept(); // get incoming connection
+ * </pre>
+ *
+ * @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 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+
+ %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%
+-->
+<html>
+<head>
+<!--
+
+ File: package.html
+
+ Copyright ...
+
+-->
+</head>
+<body bgcolor="white">
+
+<p> This package provides support for communication with Erlang and
+representation of Erlang datatypes.
+
+<p><em>Note:</em> By default, <code>jinterface</code> is only guaranteed
+to be compatible with other Erlang/OTP components from the same release as
+<code>jinterface</code> itself. For example, <code>jinterface</code>
+from the OTP R10 release is not compatible with an Erlang emulator from
+the OTP R9 release by default. <code>jinterface</code> can be set in
+compatibility mode of an earlier release (not earlier that R7), though.
+The compatibility mode is set by usage of the <code>OtpCompatRel</code>
+property. By starting the jvm with the command-line argument
+<code>-DOtpCompatRel=9</code>, <code>jinterface</code> will be compatible
+with the R9 release of OTP. <em>Warning!</em> 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.
+
+<p> 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.
+
+
+<p> 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. </p>
+
+<p> 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.
+</p>
+
+<pre>
+ OtpNode node = new OtpNode("bingo");
+ OtpMbox mbox = node.createMbox();
+
+ mbox.send("foo@localhost",new OtpErlangAtom("hej"));
+</pre>
+
+<p> 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}. </p>
+
+<p> 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.
+
+<p> 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:
+
+<pre>
+ OtpSelf self = new OtpSelf("client","cookie");
+ OtpPeer other = new OtpPeer("server");
+ OtpConnection conn = self.connect(other);
+</pre>
+
+<p>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:
+
+<pre>
+ OtpServer self = new OtpSelf("server","cookie");
+ self.publishPort();
+ OtpConnection conn = self.accept();
+</pre>
+
+
+<p>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:
+
+<pre>
+ OtpErlangAtom msg = new ErlangOtpAtom("hello");
+ conn.send("echoserver", msg);
+
+ OtpErlangObject reply = conn.receive();
+ System.out.println("Received " + reply);
+</pre>
+
+<p> 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. </p>
+
+</body>
+</html>