Created
August 15, 2011 20:58
An Erlang-Java Interop Demo - executable summary
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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