Skip to content

Instantly share code, notes, and snippets.

@eriksoe
Created August 15, 2011 20:58
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save eriksoe/1147842 to your computer and use it in GitHub Desktop.
Save eriksoe/1147842 to your computer and use it in GitHub Desktop.
An Erlang-Java Interop Demo - executable summary
#!/bin/bash -x
#######################################################################
#
# Demo of Erlang IDL, as applied to property testing of Java code.
# We'll need Triq.
git clone git://github.com/krestenkrab/triq.git
(cd triq && ./rebar compile)
ERL_ROOT=`erl -noshell -eval 'io:format("~s\n", [code:root_dir()]), init:stop().'`
# Let's define an interface:
cat > foo.idl <<EOF
interface Foo {
string quuxinate(in string s);
};
EOF
# Compile the interface:
erlc '+{be,java}' foo.idl
# Now for some implementation:
cat > FooImpl.java <<EOF
public class FooImpl extends _FooImplBase {
public String quuxinate(String s) {
int[] stats = new int[256];
for (int i=0; i<s.length(); i++) {
char c = s.charAt(i);
if (c<255 && ++stats[c] >= 3) throw new RuntimeException("WTF");//return s;
}
return new StringBuilder(s).reverse().toString();
}
/** The request environment is exposed for error handling reasons. */
public com.ericsson.otp.ic.Environment getEnv() {
return _env;
}
}
EOF
cat > FooServer.java <<EOF
public class FooServer {
// The following is based on the program in lib/ic/examples/java-client-server/server.java
static java.lang.String snode = "javaserver";
static java.lang.String cookie = "xyz";
public static void main(String[] args) throws java.io.IOException, com.ericsson.otp.erlang.OtpAuthException {
com.ericsson.otp.erlang.OtpServer self = new com.ericsson.otp.erlang.OtpServer(snode, cookie);
System.err.print("Registering with EPMD...");
boolean res = self.publishPort();
if (!res) throw new RuntimeException("Node name was already taken.");
System.err.println("done");
do {
try {
com.ericsson.otp.erlang.OtpConnection connection = self.accept();
System.err.println("Incoming connection.");
try {
handleConnection(connection);
} catch (Exception e) {
System.err.println("Server terminated: "+e);
} finally {
connection.close();
System.err.println("Connection terminated.");
}
} catch (Exception e) {
System.err.println("Error accepting connection: "+e);
}
} while (true);
}
static void handleConnection(com.ericsson.otp.erlang.OtpConnection connection) throws Exception {
while (connection.isConnected() == true) {
FooImpl srv = new FooImpl();
com.ericsson.otp.erlang.OtpInputStream request= connection.receiveBuf();
try {
com.ericsson.otp.erlang.OtpOutputStream reply = srv.invoke(request);
if (reply != null) {
connection.sendBuf(srv.__getCallerPid(), reply);
}
} catch (Exception e) {
System.err.println("Server exception: "+e);
e.printStackTrace(System.err);
handleException(e, connection, srv.getEnv());
}
}
}
static void handleException(Exception e, com.ericsson.otp.erlang.OtpConnection connection, com.ericsson.otp.ic.Environment env) throws Exception {
// Write exception reply:
com.ericsson.otp.erlang.OtpOutputStream err_reply = new com.ericsson.otp.erlang.OtpOutputStream();
err_reply.write_tuple_head(2);
err_reply.write_any(env.getSref());
err_reply.write_tuple_head(2); // Construct return value {error, ErrorText}
err_reply.write_atom("error");
err_reply.write_string(e.toString());
connection.sendBuf(env.getScaller(), err_reply);
}
}
EOF
# Compile Java parts:
echo "ERL_ROOT is $ERL_ROOT"
IC_JAR=`ls -1 $ERL_ROOT/lib/ic-*/priv/ic.jar`
JI_JAR=`ls -1 $ERL_ROOT/lib/jinterface-*/priv/OtpErlang.jar`
CLASSPATH=".:$IC_JAR:$JI_JAR"
javac -classpath "$CLASSPATH" *.java
echo "CLASSPATH is $CLASSPATH"
# Run the server:
echo "Starting the server..."
# To run manual tests, use these lines:
#java -classpath "$CLASSPATH" FooServer ; read
#exit 0
# To run automatic tests, use these lines:
java -classpath "$CLASSPATH" FooServer 2>> server.log &
JAVAPID=$!
echo "JAVAPID=$JAVAPID"
# The server should now be registered with epmd:
sleep 1; echo; epmd -names ; echo
# On the Erlang side, first a simple test:
cat <<EOF | erl -sname tester -setcookie xyz
{ok,Host}=inet:gethostname(),
JavaServer = {dummy, list_to_atom("javaserver@"++Host)},
gen_server:call(JavaServer, {quuxinate, "Testing, 1-2-3"}).
init:stop().
EOF
# Then a real property test:
cat > test.erl <<EOF
-module(test).
-include_lib("triq/include/triq.hrl").
-export([main/0]).
prop_reverse(JavaServer) -> % The property
?FORALL(S, ascii_string(),
gen_server:call(JavaServer, {quuxinate, S})
== lists:reverse(S)).
ascii_string() -> % A data generator
list(choose(0,127)).
main() ->
{ok,Host}=inet:gethostname(),
JavaServer = {dummy, list_to_atom("javaserver@"++Host)},
triq:check(prop_reverse(JavaServer), 100), % Do the magic
init:stop(). % Shut down cleanly
EOF
erlc -I triq/include -pa triq/ebin test.erl
# Run it:
erl -noshell -sname tester -setcookie xyz -pa triq/ebin -run test main
#----------
# Cleanup:
kill $JAVAPID
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment