[thrift] spruce up Erlang binding for tonight's release
Summary:
* got rid of most of the otp_base jonx ... save that for a future release unfortunately
* cleaned up the tutorial server, added -erl to tutorial.thrift's shebang
* made better README and TODO
Test Plan: checked out a copy, read my directions, built and ran the tutorial, and pretended that it didn't blow
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665273 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/erl/src/Makefile b/lib/erl/src/Makefile
new file mode 100644
index 0000000..1a07b0a
--- /dev/null
+++ b/lib/erl/src/Makefile
@@ -0,0 +1,112 @@
+# $Id: Makefile,v 1.3 2004/08/13 16:35:59 mlogan Exp $
+#
+include ../build/otp.mk
+include ../build/colors.mk
+include ../build/buildtargets.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+
+include ../vsn.mk
+APP_NAME=thrift
+PFX=thrift
+VSN=$(THRIFT_VSN)
+
+# ----------------------------------------------------
+# Install directory specification
+# WARNING: INSTALL_DIR the command to install a directory.
+# INSTALL_DST is the target directory
+# ----------------------------------------------------
+INSTALL_DST = $(ERLANG_OTP)/lib/$(APP_NAME)-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+
+MODULES = $(shell find -name \*.erl | sed s:^\\./:: | sed s/\\.erl//)
+MODULES_STRING_LIST = $(shell find -name \*.erl | sed s:^\\./:\": | sed s/\\.erl/\",/)
+
+HRL_FILES=
+INTERNAL_HRL_FILES= $(APP_NAME).hrl
+ERL_FILES= $(MODULES:%=%.erl)
+DOC_FILES=$(ERL_FILES)
+
+APP_FILE= $(APP_NAME).app
+APPUP_FILE= $(APP_NAME).appup
+
+APP_SRC= $(APP_FILE).src
+APPUP_SRC= $(APPUP_FILE).src
+
+APP_TARGET= $(EBIN)/$(APP_FILE)
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+BEAMS= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+TARGET_FILES= $(BEAMS) $(APP_TARGET) $(APPUP_TARGET)
+
+WEB_TARGET=/var/yaws/www/$(APP_NAME)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_FLAGS +=
+ERL_COMPILE_FLAGS += -I../include -I../../fslib/include -I../../system_status/include
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+all debug opt: $(EBIN) $(TARGET_FILES)
+
+#$(EBIN)/rm_logger.beam: $(APP_NAME).hrl
+include ../build/docs.mk
+
+# Note: In the open-source build clean must not destroy the preloaded
+# beam files.
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+ rm -rf $(EBIN)
+ rm -rf *html
+
+$(EBIN):
+ mkdir $(EBIN)
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk $(BEAMS)
+ sed -e 's;%VSN%;$(VSN);' \
+ -e 's;%PFX%;$(PFX);' \
+ -e 's;%APP_NAME%;$(APP_NAME);' \
+ -e 's;%MODULES%;%MODULES%$(MODULES_STRING_LIST);' \
+ $< > $<".tmp"
+ sed -e 's/%MODULES%\(.*\),/\1/' \
+ $<".tmp" > $@
+ rm $<".tmp"
+
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(WEB_TARGET): ../markup/*
+ rm -rf $(WEB_TARGET)
+ mkdir $(WEB_TARGET)
+ cp -r ../markup/ $(WEB_TARGET)
+ cp -r ../skins/ $(WEB_TARGET)
+
+# ----------------------------------------------------
+# Install Target
+# ----------------------------------------------------
+
+install: all $(WEB_TARGET)
+# $(INSTALL_DIR) $(INSTALL_DST)/src
+# $(INSTALL_DATA) $(ERL_FILES) $(INSTALL_DST)/src
+# $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(INSTALL_DST)/src
+# $(INSTALL_DIR) $(INSTALL_DST)/include
+# $(INSTALL_DATA) $(HRL_FILES) $(INSTALL_DST)/include
+# $(INSTALL_DIR) $(INSTALL_DST)/ebin
+# $(INSTALL_DATA) $(TARGET_FILES) $(INSTALL_DST)/ebin
diff --git a/lib/erl/src/oop.erl b/lib/erl/src/oop.erl
new file mode 100644
index 0000000..e3685ee
--- /dev/null
+++ b/lib/erl/src/oop.erl
@@ -0,0 +1,184 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(oop).
+
+-export([get/2, set/3, call/2, call/3, inspect/1, start_new/2, is_object/1, class/1]).
+-export([behaviour_info/1]).
+
+-include("thrift.hrl").
+-include("oop.hrl").
+
+%%%
+%%% behavior definition
+%%%
+
+behaviour_info(callbacks) ->
+ [
+ {attr, 4},
+ {super, 0}
+ ];
+behaviour_info(_) ->
+ undefined.
+
+%%
+
+-define(TRIED, lists:reverse([TryModule|TriedRev])).
+
+%% no super attr defined
+-define(NOSUPEROBJ, exit({missing_attr_super, {inspect(Obj), ?TRIED}})).
+
+-define(NOMETHOD, exit({missing_method, {Method, inspect(Obj), tl(Args), ?TRIED}})).
+
+-define(NOATTR, exit({missing_attr, {hd(tl(Args)), inspect(FirstObj), ?TRIED}})).
+
+-define(NOATTR_SET, exit({missing_attr, {Field, inspect(Obj), ".." %% TODO: give a backtrace
+ }})).
+
+
+%%% get(Obj, Field) -> term()
+%%% looks up Field in Obj or its ancestor objects
+
+get(Obj, Field) ->
+ call(Obj, attr, [get, Field, get]).
+
+set(Obj, Field, Value) -> %% TODO: could be tail-recursive
+ Module = ?CLASS(Obj),
+ try
+ Module:attr(Obj, set, Field, Value)
+ catch
+ error:Kind when Kind == undef; Kind == function_clause ->
+ case get_superobject(Obj) of
+ { ok, Superobj } ->
+ Super1 = set(Superobj, Field, Value),
+ try
+ Module:attr(Obj, set, super, Super1)
+ catch %% TODO(cpiro): remove check
+ X -> exit({burnsauce, X})
+ end;
+ none ->
+ ?NOATTR_SET
+ end
+ end.
+
+
+%%% C++ <-> Erlang
+%%% classes modules
+%%% class b : public a a:super() -> b.
+%%%
+
+get_superobject(Obj) ->
+ try
+ {ok, (?CLASS(Obj)):attr(Obj, get, super, get)}
+ catch
+ error:Kind when Kind == undef; Kind == function_clause ->
+ none
+ end.
+
+is_object(Obj) when is_tuple(Obj) ->
+ try
+ (?CLASS(Obj)):super(), %% if it's an object its first element will be a class name, and it'll have super/0
+ true
+ catch
+ error:Kind when Kind == undef; Kind == function_clause ->
+ false
+ end;
+is_object(_) ->
+ false.
+
+call(Obj, Method, ArgsProper) ->
+ %% error_logger:info_msg("call called: Obj=~p Method=~p ArgsProper=~p", [inspect(Obj), Method, ArgsProper]),
+ Args = [Obj|ArgsProper], %% prepend This to args
+ TryModule = ?CLASS(Obj),
+ call_loop(Obj, Method, Args, TryModule, [], Obj).
+
+call(Obj, Method) ->
+ call(Obj, Method, []).
+
+call_loop(Obj, Method, Args, TryModule, TriedRev, FirstObj) ->
+ try
+ %% error_logger:info_msg("call_loop~n ~p~n ~p~n ~p~n ~p", [inspect(Obj), Method, Args, TryModule]),
+ apply(TryModule, Method, Args)
+ catch
+ error:Kind when Kind == undef; Kind == function_clause ->
+ case { TryModule:super(), Method } of
+ { none, attr } ->
+ ?NOATTR;
+
+ { none, _ } ->
+ ?NOMETHOD;
+
+ { Superclass, attr } ->
+ %% look for attrs in the "super object"
+
+ case get_superobject(Obj) of
+ {ok, Superobj} when (TryModule == ?CLASS(Obj)) ->
+ %% replace This with Superobj
+ NewArgs = [Superobj|tl(Args)],
+ call_loop(Superobj, Method, NewArgs,
+ Superclass, [TryModule|TriedRev], FirstObj);
+
+ {ok, _Superobj} -> % failed guard TODO(cpiro): removeme
+ exit(oh_noes);
+
+ none -> ?NOSUPEROBJ
+ end;
+
+ { SuperClass, _ } ->
+ call_loop(Obj, Method, Args,
+ SuperClass, [TryModule|TriedRev], FirstObj)
+ end
+ end.
+
+class(Obj) when is_tuple(Obj) ->
+ case is_object(Obj) of
+ true ->
+ ?CLASS(Obj);
+ false ->
+ none
+ end;
+class(_) ->
+ none.
+
+%% careful: not robust against records beginning with a class name
+%% (note: we can't just guard with is_record(?CLASS(Obj), Obj) since we
+%% can't/really really shouldn't require all record definitions in this file
+inspect(Obj) ->
+ try
+ case is_object(Obj) of
+ true ->
+ DeepList = inspect_loop(Obj, "#<"),
+ lists:flatten(DeepList);
+ false ->
+ thrift_utils:sformat("~p", [Obj])
+ end
+ catch
+ _:E ->
+ thrift_utils:sformat("INSPECT_ERROR(~p) ~p", [E, Obj])
+
+ %% TODO(cpiro): bring this back once we're done testing:
+ %% _:E -> thrift_utils:sformat("~p", [Obj])
+ end.
+
+inspect_loop(Obj, Str) ->
+ Class = ?CLASS(Obj),
+ Inspect = Class:inspect(Obj),
+ Current = atom_to_list(Class) ++ ": " ++ Inspect,
+
+ case get_superobject(Obj) of
+ { ok, Superobj } ->
+ inspect_loop(Superobj, Str ++ Current ++ " | ");
+ none ->
+ Str ++ Current ++ ">"
+ end.
+
+%% TODO: voids take only ok as return?
+start_new(none=Resv, _) ->
+ error_logger:format("can't instantiate ~p: class name is a reserved word", [Resv]),
+ error;
+start_new(Class, Args) ->
+ {ok, Pid} = gen_server:start_link(thrift_oop_server, {Class, Args}, []),
+ Pid.
diff --git a/lib/erl/src/protocol/tBinaryProtocol.erl b/lib/erl/src/protocol/tBinaryProtocol.erl
new file mode 100644
index 0000000..b745298
--- /dev/null
+++ b/lib/erl/src/protocol/tBinaryProtocol.erl
@@ -0,0 +1,214 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tBinaryProtocol).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("protocol/tProtocolException.hrl").
+-include("protocol/tBinaryProtocol.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([
+ new/1,
+
+ writeMessageBegin/4,
+ writeFieldBegin/4, writeFieldStop/1,
+ writeMapBegin/4,
+ writeListBegin/3,
+ writeSetBegin/3,
+
+ writeBool/2, writeByte/2, writeI16/2, writeI32/2,
+ writeI64/2, writeDouble/2, writeString/2,
+
+ readMessageBegin/1,
+ readFieldBegin/1,
+ readMapBegin/1,
+ readListBegin/1,
+ readSetBegin/1,
+
+ readBool/1, readByte/1, readI16/1, readI32/1,
+ readI64/1, readDouble/1, readString/1
+]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tProtocol.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new(Trans) ->
+ Super = (super()):new(Trans),
+ #?MODULE{super=Super}.
+
+%%%
+%%% instance methods
+%%%
+
+writeMessageBegin(This, Name, Type, Seqid) ->
+ ?L1(writeI32, ?VERSION_1 bor Type),
+ ?L1(writeString, Name),
+ ?L1(writeI32, Seqid),
+ ok.
+
+writeFieldBegin(This, _Name, Type, Id) ->
+ ?L1(writeByte, Type),
+ ?L1(writeI16, Id),
+ ok.
+
+writeFieldStop(This) ->
+ ?L1(writeByte, ?tType_STOP),
+ ok.
+
+writeMapBegin(This, Ktype, Vtype, Size) ->
+ ?L1(writeByte, Ktype),
+ ?L1(writeByte, Vtype),
+ ?L1(writeI32, Size),
+ ok.
+
+writeListBegin(This, Etype, Size) ->
+ ?L1(writeByte, Etype),
+ ?L1(writeI32, Size),
+ ok.
+
+writeSetBegin(This, Etype, Size) ->
+ ?L1(writeByte, Etype),
+ ?L1(writeI32, Size),
+ ok.
+
+%
+
+writeBool(This, Bool) ->
+ case Bool of
+ true -> ?L1(writeByte, 1);
+ false -> ?L1(writeByte, 0)
+ end.
+
+writeByte(This, Byte) ->
+ Trans = oop:get(This, trans),
+ ?R1(Trans, write, binary_to_list(<<Byte:8/big>>)).
+
+writeI16(This, I16) ->
+ Trans = oop:get(This, trans),
+ ?R1(Trans, write, binary_to_list(<<I16:16/big>>)).
+
+writeI32(This, I32) ->
+ Trans = oop:get(This, trans),
+ ?R1(Trans, write, binary_to_list(<<I32:32/big>>)).
+
+writeI64(This, I64) ->
+ Trans = oop:get(This, trans),
+ ?R1(Trans, write, binary_to_list(<<I64:64/big>>)).
+
+writeDouble(This, Double) ->
+ Trans = oop:get(This, trans),
+ ?R1(Trans, write, binary_to_list(<<Double:64/big>>)).
+
+writeString(This, Str) ->
+ Trans = oop:get(This, trans),
+ ?L1(writeI32, length(Str)),
+ ?R1(Trans, write, Str).
+
+%
+
+readMessageBegin(This) ->
+ Version = ?L0(readI32),
+ if
+ (Version band ?VERSION_MASK) /= ?VERSION_1 ->
+ throw(tProtocolException:new(?tProtocolException_BAD_VERSION,
+ "Missing version identifier"));
+ true -> ok
+ end,
+ Type = Version band 16#000000ff,
+ Name = ?L0(readString),
+ Seqid = ?L0(readI32),
+ { Name, Type, Seqid }.
+
+readFieldBegin(This) ->
+ Type = ?L0(readByte),
+ case Type of
+ ?tType_STOP ->
+ { nil, Type, 0 }; % WATCH
+ _ ->
+ Id = ?L0(readI16),
+ { nil, Type, Id }
+ end.
+
+readMapBegin(This) ->
+ Ktype = ?L0(readByte),
+ Vtype = ?L0(readByte),
+ Size = ?L0(readI32),
+ { Ktype, Vtype, Size }.
+
+readListBegin(This) ->
+ Etype = ?L0(readByte),
+ Size = ?L0(readI32),
+ { Etype, Size }.
+
+readSetBegin(This) ->
+ Etype = ?L0(readByte),
+ Size = ?L0(readI32),
+ { Etype, Size }.
+
+%
+
+readBool(This) ->
+ Byte = ?L0(readByte),
+ (Byte /= 0).
+
+readByte(This) ->
+ Trans = oop:get(This, trans),
+ <<Val:8/integer-signed-big, _/binary>> = ?R1(Trans, readAll, 1),
+ Val.
+
+readI16(This) ->
+ Trans = oop:get(This, trans),
+ <<Val:16/integer-signed-big, _/binary>> = ?R1(Trans, readAll, 2),
+ Val.
+
+readI32(This) ->
+ Trans = oop:get(This, trans),
+ <<Val:32/integer-signed-big, _/binary>> = ?R1(Trans, readAll, 4),
+ Val.
+
+readI64(This) ->
+ Trans = oop:get(This, trans),
+ <<Val:64/integer-signed-big, _/binary>> = ?R1(Trans, readAll, 8),
+ Val.
+
+readDouble(This) ->
+ Trans = oop:get(This, trans),
+ <<Val:64/float-signed-big, _/binary>> = ?R1(Trans, readAll, 8),
+ Val.
+
+readString(This) ->
+ Trans = oop:get(This, trans),
+ Sz = ?L0(readI32),
+ binary_to_list(?R1(Trans, readAll, Sz)).
diff --git a/lib/erl/src/protocol/tBinaryProtocolFactory.erl b/lib/erl/src/protocol/tBinaryProtocolFactory.erl
new file mode 100644
index 0000000..ff7fa56
--- /dev/null
+++ b/lib/erl/src/protocol/tBinaryProtocolFactory.erl
@@ -0,0 +1,57 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tBinaryProtocolFactory).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("protocol/tBinaryProtocol.hrl").
+-include("protocol/tBinaryProtocolFactory.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, getProtocol/2]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tProtocolFactory.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new() ->
+ Super = (super()):new(),
+ #?MODULE{super=Super}.
+
+%%%
+%%% instance methods
+%%%
+
+getProtocol(_This, Trans) ->
+ oop:start_new(tBinaryProtocol, [Trans]).
+
diff --git a/lib/erl/src/protocol/tProtocol.erl b/lib/erl/src/protocol/tProtocol.erl
new file mode 100644
index 0000000..8900c5d
--- /dev/null
+++ b/lib/erl/src/protocol/tProtocol.erl
@@ -0,0 +1,217 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tProtocol).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("protocol/tProtocol.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+%% -export([interface/1]). %%
+
+-export([
+ new/1,
+ skip/2,
+
+ writeMessageBegin/4, writeMessageEnd/1,
+ writeStructBegin/2, writeStructEnd/1,
+ writeFieldBegin/4, writeFieldEnd/1, writeFieldStop/1,
+ writeMapBegin/4, writeMapEnd/1,
+ writeListBegin/3, writeListEnd/1,
+ writeSetBegin/3, writeSetEnd/1,
+
+ writeBool/2, writeByte/2, writeI16/2, writeI32/2,
+ writeI64/2, writeDouble/2, writeString/2,
+
+ readMessageBegin/1, readMessageEnd/1,
+ readStructBegin/1, readStructEnd/1,
+ readFieldBegin/1, readFieldEnd/1,
+ readMapBegin/1, readMapEnd/1,
+ readListBegin/1, readListEnd/1,
+ readSetBegin/1, readSetEnd/1,
+
+ readBool/1, readByte/1, readI16/1, readI32/1,
+ readI64/1, readDouble/1, readString/1
+]).
+
+%%%
+%%% server interface
+%%%
+
+%% %%% modules we can instantiate from the server %%
+%% interface(subclasses) -> %%
+%% [ %%
+%% tBinaryProtocol %%
+%% ]; %%
+%% %%
+%% %%% synchronous calls to pass %%
+%% interface(call) -> %%
+%% [ %%
+%% skip, %%
+%% %%
+%% writeMessageBegin, writeMessageEnd, %%
+%% writeStructBegin, writeStructEnd, %%
+%% writeFieldBegin, writeFieldEnd, writeFieldStop, %%
+%% writeMapBegin, writeMapEnd, %%
+%% writeListBegin, writeListEnd, %%
+%% writeSetBegin, writeSetEnd, %%
+%% %%
+%% writeBool, writeByte, writeI16, writeI32, %%
+%% writeI64, writeDouble, writeString, %%
+%% %%
+%% readMessageBegin, readMessageEnd, %%
+%% readStructBegin, readStructEnd, %%
+%% readFieldBegin, readFieldEnd, %%
+%% readMapBegin, readMapEnd, %%
+%% readListBegin, readListEnd, %%
+%% readSetBegin, readSetEnd, %%
+%% %%
+%% readBool, readByte, readI16, readI32, %%
+%% readI64, readDouble, readString %%
+%% ]; %%
+%% %%
+%% %%% asynchronous casts to pass %%
+%% interface(cast) -> %%
+%% []. %%
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(trans).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ none.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(trans).
+
+%%%
+%%% class methods
+%%%
+
+new(Trans) ->
+ #?MODULE{trans=Trans}.
+
+%%%
+%%% instance methods
+%%%
+
+writeMessageBegin(_This, _Name, _Type, _Seqid) -> ok.
+writeMessageEnd(_This) -> ok.
+writeStructBegin(_This, _Name) -> ok.
+writeStructEnd(_This) -> ok.
+writeFieldBegin(_This, _Name, _Type, _Id) -> ok.
+writeFieldEnd(_This) -> ok.
+writeFieldStop(_This) -> ok.
+writeMapBegin(_This, _Ktype, _Vtype, _Size) -> ok.
+writeMapEnd(_This) -> ok.
+writeListBegin(_This, _Etype, _Size) -> ok.
+writeListEnd(_This) -> ok.
+writeSetBegin(_This, _Etype, _Size) -> ok.
+writeSetEnd(_This) -> ok.
+
+writeBool(_This, _Value) -> ok.
+writeByte(_This, _Value) -> ok.
+writeI16(_This, _Value) -> ok.
+writeI32(_This, _Value) -> ok.
+writeI64(_This, _Value) -> ok.
+writeDouble(_This, _Value) -> ok.
+writeString(_This, _Value) -> ok.
+
+readMessageBegin(_This) -> ok.
+readMessageEnd(_This) -> ok.
+readStructBegin(_This) -> ok.
+readStructEnd(_This) -> ok.
+readFieldBegin(_This) -> ok.
+readFieldEnd(_This) -> ok.
+readMapBegin(_This) -> ok.
+readMapEnd(_This) -> ok.
+readListBegin(_This) -> ok.
+readListEnd(_This) -> ok.
+readSetBegin(_This) -> ok.
+readSetEnd(_This) -> ok.
+
+readBool(_This) -> ok.
+readByte(_This) -> ok.
+readI16(_This) -> ok.
+readI32(_This) -> ok.
+readI64(_This) -> ok.
+readDouble(_This) -> ok.
+readString(_This) -> ok.
+
+skip(This, Type) ->
+ case Type of
+ ?tType_STOP -> nil; % WATCH
+ ?tType_BOOL -> ?L0(readBool);
+ ?tType_BYTE -> ?L0(readByte);
+ ?tType_I16 -> ?L0(readI16);
+ ?tType_I32 -> ?L0(readI32);
+ ?tType_I64 -> ?L0(readI64);
+ ?tType_DOUBLE -> ?L0(readDouble);
+ ?tType_STRING -> ?L0(readString);
+
+ ?tType_STRUCT ->
+ ?L0(readStructBegin),
+ skip_struct_loop(This),
+
+ %% cpiro: this isn't here in the original tprotocol.rb, but i think it's a bug
+ ?L0(readStructEnd);
+
+ ?tType_MAP ->
+ {Ktype, Vtype, Size} = ?L0(readMapBegin),
+ skip_map_repeat(This, Ktype, Vtype, Size),
+ ?L0(readMapEnd);
+
+ ?tType_SET ->
+ {Etype, Size} = ?L0(readSetBegin),
+ skip_set_repeat(This, Etype, Size),
+ ?L0(readSetEnd);
+
+ ?tType_LIST ->
+ {Etype, Size} = ?L0(readListBegin),
+ skip_set_repeat(This, Etype, Size), % [sic] skipping same as for SET
+ ?L0(readListEnd)
+ end.
+
+skip_struct_loop(This) ->
+ { _Name, Type, _Id } = ?L0(readFieldBegin),
+ if
+ Type == ?tType_STOP ->
+ ok;
+
+ true ->
+ ?L1(skip, Type),
+ ?L0(readFieldEnd),
+
+ %% cpiro: this is here in original tprotocol.rb, but i think it's a bug
+ % ?L0(readStructEnd),
+ skip_struct_loop(This)
+ end.
+
+skip_map_repeat(This, Ktype, Vtype, Times) ->
+ ?L1(skip, Ktype),
+ ?L1(skip, Vtype),
+ skip_map_repeat(This, Ktype, Vtype, Times-1).
+
+skip_set_repeat(This, Etype, Times) ->
+ ?L1(skip, Etype),
+ skip_set_repeat(This, Etype, Times-1).
diff --git a/lib/erl/src/protocol/tProtocolException.erl b/lib/erl/src/protocol/tProtocolException.erl
new file mode 100644
index 0000000..d926aff
--- /dev/null
+++ b/lib/erl/src/protocol/tProtocolException.erl
@@ -0,0 +1,58 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tProtocolException).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("protocol/tProtocolException.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, new/1, new/2]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(type).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tException.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(type).
+
+%%%
+%%% class methods
+%%%
+
+new(Type, Message) ->
+ Super = (super()):new(Message),
+ #?MODULE{super=Super, type=Type}.
+
+new() ->
+ new(?tProtocolException_UNKNOWN, undefined).
+new(Type) ->
+ new(Type, undefined).
+
+%%%
+%%% instance methods
+%%%
diff --git a/lib/erl/src/protocol/tProtocolFactory.erl b/lib/erl/src/protocol/tProtocolFactory.erl
new file mode 100644
index 0000000..d697263
--- /dev/null
+++ b/lib/erl/src/protocol/tProtocolFactory.erl
@@ -0,0 +1,54 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tProtocolFactory).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("protocol/tProtocolFactory.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, getProtocol/2]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?ATTR_DUMMY.
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ none.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new() ->
+ #?MODULE{}.
+
+%%%
+%%% instance methods
+%%%
+
+getProtocol(This, Trans) ->
+ nil.
diff --git a/lib/erl/src/server/tErlServer.erl b/lib/erl/src/server/tErlServer.erl
new file mode 100644
index 0000000..0e61e38
--- /dev/null
+++ b/lib/erl/src/server/tErlServer.erl
@@ -0,0 +1,117 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tErlServer).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("transport/tTransportException.hrl").
+-include("server/tErlServer.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/6, new/5, new/4, effectful_serve/1, effectful_new_acceptor/1, catches/3]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(acceptor);
+?DEFINE_ATTR(listenSocket);
+?DEFINE_ATTR(port).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tServer.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(acceptor) ++ ", " ++
+ ?FORMAT_ATTR(listenSocket) ++ ", " ++
+ ?FORMAT_ATTR(port).
+
+%%%
+%%% class methods
+%%%
+
+new(Port, Handler, Processor, ServerTransport, TransportFactory, ProtocolFactory) ->
+ Super = (super()):new(Handler, Processor, ServerTransport, TransportFactory, ProtocolFactory),
+ #?MODULE{super=Super, port=Port, listenSocket=nil, acceptor=nil}.
+
+new(Port, Handler, Processor, ServerTransport) ->
+ new(Port, Handler, Processor, ServerTransport, nil, nil).
+
+new(Port, Handler, Processor, ServerTransport, TransportFactory) ->
+ new(Port, Handler, Processor, ServerTransport, TransportFactory, nil).
+
+% listenSocket, acceptor, port
+
+effectful_serve(This) ->
+ Port = oop:get(This, port),
+
+ Options = [binary, {packet, 0}, {active, false}],
+
+ %% listen
+ case gen_tcp:listen(Port, Options) of
+ {ok, ListenSocket} ->
+ ?INFO(server_listening, {Port}),
+
+ This1 = oop:set(This, listenSocket, ListenSocket),
+
+ %% spawn acceptor
+ {_Acceptor, This2} = effectful_new_acceptor(This1),
+
+ {ok, This2};
+
+ {error, eaddrinuse} ->
+ error_logger:format("couldn't bind port ~p, address in use", [Port]),
+ {{error, eaddrinuse}, This} %% state before the accept
+ end.
+
+effectful_new_acceptor(This) ->
+ ListenSocket = oop:get(This, listenSocket),
+ Processor = oop:get(This, processor), %% cpiro: generated processor, not the "actual" processor
+ Handler = oop:get(This, handler),
+
+ TF = oop:get(This, transportFactory),
+ PF = oop:get(This, protocolFactory),
+
+ tErlAcceptor = oop:get(This, serverTransport), %% cpiro: only supported ServerTransport
+
+ ServerPid = self(),
+ Acceptor = oop:start_new(tErlAcceptor, [ServerPid, TF, PF]),
+ ?C3(Acceptor, accept, ListenSocket, Processor, Handler),
+
+ This1 = oop:set(This, acceptor, Acceptor),
+
+ {Acceptor, This1}.
+
+catches(_This, _Pid, normal) ->
+ ok.
+
+%% %% The current acceptor has died, wait a little and try again %%
+%% handle_info({'EXIT', Pid, _Abnormal}, #state{acceptor=Pid} = State) -> %%
+%% timer:sleep(2000), %%
+%% iserve_socket:start_link(self(), State#state.listen_socket, State#state.port), %%
+%% {noreply,State}; %%
+
+%% terminate(Reason, State) -> %%
+%% error_logger:info_msg( "Terminating error: ~p~n", [Reason]), % added %%
+%% gen_tcp:close(State#state.listen_socket), %%
+%% ok. %%
+%% %%
diff --git a/lib/erl/src/server/tServer.erl b/lib/erl/src/server/tServer.erl
new file mode 100644
index 0000000..23aef22
--- /dev/null
+++ b/lib/erl/src/server/tServer.erl
@@ -0,0 +1,81 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tServer).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("server/tServer.hrl").
+-include("transport/tTransportFactory.hrl").
+-include("protocol/tBinaryProtocolFactory.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/5, new/4, new/3, serve/1]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(handler);
+?DEFINE_ATTR(processor);
+?DEFINE_ATTR(serverTransport);
+?DEFINE_ATTR(transportFactory);
+?DEFINE_ATTR(protocolFactory).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ none.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(handler) ++ ", " ++
+ ?FORMAT_ATTR(processor) ++ ", " ++
+ ?FORMAT_ATTR(serverTransport) ++ ", " ++
+ ?FORMAT_ATTR(transportFactory) ++ ", " ++
+ ?FORMAT_ATTR(protocolFactory).
+
+%%%
+%%% class methods
+%%%
+
+new(Handler, Processor, ServerTransport, TransportFactory, ProtocolFactory) ->
+ #?MODULE{handler=Handler, processor=Processor, serverTransport=ServerTransport,
+
+ %% much ado about nothing but
+ %% subclasses pass nil too
+ transportFactory =
+ case TransportFactory of
+ nil -> tTransportFactory:new();
+ _ -> TransportFactory
+ end,
+
+ protocolFactory =
+ case ProtocolFactory of
+ nil -> tBinaryProtocolFactory:new();
+ _ -> ProtocolFactory
+ end
+}.
+
+new(Handler, Processor, ServerTransport) ->
+ new(Handler, Processor, ServerTransport, nil, nil).
+
+new(Handler, Processor, ServerTransport, TransportFactory) ->
+ new(Handler, Processor, ServerTransport, TransportFactory, nil).
+
+serve(_This) ->
+ ok.
diff --git a/lib/erl/src/server/tSimpleServer.erl b/lib/erl/src/server/tSimpleServer.erl
new file mode 100644
index 0000000..6dcc723
--- /dev/null
+++ b/lib/erl/src/server/tSimpleServer.erl
@@ -0,0 +1,111 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+%%% NOTE: tSimpleServer's design isn't compatible with our concurrency model.
+%%% It won't work in principle, and certainly not in practice. YMMV.
+
+-module(tSimpleServer).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("transport/tTransportException.hrl").
+-include("server/tSimpleServer.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/5, new/4, new/3, serve/1]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tServer.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new(Handler, Processor, ServerTransport, TransportFactory, ProtocolFactory) ->
+ Super = (super()):new(Handler, Processor, ServerTransport, TransportFactory, ProtocolFactory),
+ error_logger:warning_msg("tSimpleServer has an incompatable design and doesn't work. Promise."),
+ #?MODULE{super=Super}.
+
+new(Handler, Processor, ServerTransport) ->
+ new(Handler, Processor, ServerTransport, nil, nil).
+
+new(Handler, Processor, ServerTransport, TransportFactory) ->
+ new(Handler, Processor, ServerTransport, TransportFactory, nil).
+
+%
+
+serve(This) ->
+ exit(tSimpleServer_doesnt_work),
+ ST = oop:get(This, serverTransport),
+ ?R0(ST, effectful_listen),
+
+ serve_loop(This).
+
+serve_loop(This) ->
+ error_logger:info_msg("ready.", []),
+
+ ST = oop:get(This, serverTransport),
+ Client = ?RT0(ST, accept, infinity),
+
+ TF = oop:get(This, transportFactory),
+ Trans = ?F1(TF, getTransport, Client), %% cpiro: OPAQUE!! Trans = Client
+
+ PF = oop:get(This, protocolFactory),
+ Prot = ?F1(PF, getProtocol, Trans), %% cpiro: OPAQUE!! Prot = start_new(tBinaryProtocol, [Trans])
+
+ error_logger:info_msg("client accept()ed", []),
+
+ serve_loop_loop(This, Prot), % giggle loop?
+
+ ?R0(Trans, effectful_close),
+
+ serve_loop(This).
+
+serve_loop_loop(This, Prot) ->
+ Next =
+ try
+ Handler = oop:get(This, handler),
+ Processor = oop:get(This, processor),
+ Val = apply(Processor, process, [Handler, Prot, Prot]), %% TODO(cpiro): make processor a gen_server instance
+ error_logger:info_msg("request processed: rv=~p", [Val]),
+ loop
+ catch
+ %% TODO(cpiro) case when is_record(...) to pick out our exception
+ %% records vs. normal erlang throws
+ E when is_record(E, tTransportException) ->
+ error_logger:info_msg("tTransportException (normal-ish?)", []),
+ close;
+ F ->
+ error_logger:info_msg("EXCEPTION: ~p", [F]),
+ close
+ end,
+ case Next of
+ loop -> serve_loop_loop(This, Prot);
+ close -> ok
+ end.
diff --git a/lib/erl/src/tApplicationException.erl b/lib/erl/src/tApplicationException.erl
new file mode 100644
index 0000000..568b9c9
--- /dev/null
+++ b/lib/erl/src/tApplicationException.erl
@@ -0,0 +1,115 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tApplicationException).
+
+-include("thrift.hrl").
+-include("tApplicationException.hrl").
+
+-include("oop.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, new/1, new/2, read/2, write/2]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(type).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tException.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(type).
+
+%%%
+%%% class methods
+%%%
+
+new(Type, Message) ->
+ Super = (super()):new(Message),
+ #?MODULE{super=Super, type=Type}.
+
+new() -> new(?tApplicationException_UNKNOWN, undefined).
+new(Type) -> new(Type, undefined).
+
+%%%
+%%% instance methods
+%%%
+
+read(This, Iprot) ->
+ ?R0(Iprot, readStructBegin),
+ read_while_loop(This, Iprot),
+ ?R0(Iprot, readStructEnd),
+ ok.
+
+read_while_loop(This, Iprot) ->
+ {_Fname, Ftype, Fid} = ?R0(Iprot, readFieldBegin),
+
+ if
+ Ftype == ?tType_STOP ->
+ ok;
+
+ (Fid == 1) and (Ftype == ?tType_STRING) ->
+ Message1 = ?R0(Iprot, readString),
+ This1 = oop:set(This, message, Message1),
+ ?R0(Iprot, readFieldEnd),
+ read_while_loop(This1, Iprot);
+
+ Fid == 1 ->
+ ?R0(Iprot, skip),
+ ?R0(Iprot, readFieldEnd),
+ read_while_loop(This, Iprot);
+
+ (Fid == 2) and (Ftype == ?tType_I32) ->
+ Type1 = ?R0(Iprot, readI32),
+ This1 = oop:set(This, type, Type1),
+ ?R0(Iprot, readFieldEnd),
+ read_while_loop(This1, Iprot);
+
+ true ->
+ ?R0(Iprot, skip),
+ ?R0(Iprot, readFieldEnd),
+ read_while_loop(This, Iprot)
+ end.
+
+write(This, Oprot) ->
+ ?R1(Oprot, writeStructBegin, "tApplicationException"),
+ Message = oop:get(This, message),
+ Type = oop:get(This, type),
+
+ if Message /= undefined ->
+ ?R3(Oprot, writeFieldBegin, "message", ?tType_STRING, 1),
+ ?R1(Oprot, writeString, Message),
+ ?R0(Oprot, writeFieldEnd);
+ true -> ok
+ end,
+
+ if Type /= undefined ->
+ ?R3(Oprot, writeFieldBegin, "type", ?tType_I32, 2),
+ ?R1(Oprot, writeI32, Type),
+ ?R0(Oprot, writeFieldEnd);
+ true -> ok
+ end,
+
+ ?R0(Oprot, writeFieldStop),
+ ?R0(Oprot, writeStructEnd),
+ ok.
diff --git a/lib/erl/src/tErlProcessor.erl b/lib/erl/src/tErlProcessor.erl
new file mode 100644
index 0000000..a6d1073
--- /dev/null
+++ b/lib/erl/src/tErlProcessor.erl
@@ -0,0 +1,63 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tErlProcessor).
+
+-include("thrift.hrl").
+-include("oop.hrl").
+-include("tErlProcessor.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/2, process/3]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(generatedProcessor);
+?DEFINE_ATTR(handler).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tProcessor.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(generatedProcessor) ++ ", " ++
+ ?FORMAT_ATTR(handler).
+
+%%%
+%%% class methods
+%%%
+
+new(GP, Handler) ->
+ Super = (super()):new(),
+ #?MODULE{super = Super, generatedProcessor = GP, handler = Handler}.
+
+%% processor is generated code
+%% handler is user code
+
+%%%
+%%% instance methods
+%%%
+
+process(This, Iprot, Oprot) ->
+ GP = oop:get(This, generatedProcessor),
+ Handler = oop:get(This, handler),
+
+ GP:process(Handler, Iprot, Oprot).
diff --git a/lib/erl/src/tException.erl b/lib/erl/src/tException.erl
new file mode 100644
index 0000000..0ec4c94
--- /dev/null
+++ b/lib/erl/src/tException.erl
@@ -0,0 +1,50 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tException).
+
+-include("oop.hrl").
+-include("tException.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/1]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(message).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ none.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(message).
+
+%%%
+%%% class methods
+%%%
+
+new(Message) ->
+ #?MODULE{message=Message}.
+
+%%%
+%%% instance methods
+%%%
+
diff --git a/lib/erl/src/tProcessor.erl b/lib/erl/src/tProcessor.erl
new file mode 100644
index 0000000..003748a
--- /dev/null
+++ b/lib/erl/src/tProcessor.erl
@@ -0,0 +1,50 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tProcessor).
+
+-include("oop.hrl").
+-include("tProcessor.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?ATTR_DUMMY.
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ none.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new() ->
+ #?MODULE{}.
+
+%%%
+%%% instance methods
+%%%
+
diff --git a/lib/erl/src/thrift.app.src b/lib/erl/src/thrift.app.src
new file mode 100755
index 0000000..dc2926a
--- /dev/null
+++ b/lib/erl/src/thrift.app.src
@@ -0,0 +1,41 @@
+%%% -*- mode:erlang -*-
+{application, %APP_NAME%,
+ [
+ % A quick description of the application.
+ {description, "Thrift bindings"},
+
+ % The version of the applicaton
+ {vsn, "%VSN%"},
+
+ % All modules used by the application.
+ {modules,
+ [
+ %MODULES%
+ ]},
+
+ % All of the registered names the application uses. This can be ignored.
+ {registered, []},
+
+ % Applications that are to be started prior to this one. This can be ignored
+ % leave it alone unless you understand it well and let the .rel files in
+ % your release handle this.
+ {applications,
+ [
+ kernel,
+ stdlib
+ ]},
+
+ % OTP application loader will load, but not start, included apps. Again
+ % this can be ignored as well. To load but not start an application it
+ % is easier to include it in the .rel file followed by the atom 'none'
+ {included_applications, []},
+
+ % configuration parameters similar to those in the config file specified
+ % on the command line. can be fetched with gas:get_env
+ {env, []},
+
+ % The Module and Args used to start this application.
+ {mod, {%APP_NAME%, []}}
+ ]
+}.
+
diff --git a/lib/erl/src/thrift.appup.src b/lib/erl/src/thrift.appup.src
new file mode 100755
index 0000000..54a6383
--- /dev/null
+++ b/lib/erl/src/thrift.appup.src
@@ -0,0 +1 @@
+{"%VSN%",[],[]}.
diff --git a/lib/erl/src/thrift.erl b/lib/erl/src/thrift.erl
new file mode 100644
index 0000000..feabb1d
--- /dev/null
+++ b/lib/erl/src/thrift.erl
@@ -0,0 +1,25 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(thrift).
+
+-export([start/2, shutdown/0, stop/1]).
+-behaviour(application).
+
+-include("thrift.hrl").
+
+%%%
+%%% behavior definition
+%%%
+
+start(Type, StartArgs) ->
+ ok.
+
+shutdown() ->
+ application:stop(?MODULE).
+
+stop(_State) ->
+ ok.
diff --git a/lib/erl/src/thrift_logger.erl b/lib/erl/src/thrift_logger.erl
new file mode 100644
index 0000000..12aa059
--- /dev/null
+++ b/lib/erl/src/thrift_logger.erl
@@ -0,0 +1,307 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(thrift_logger).
+
+-behaviour(gen_event).
+
+-include("thrift_logger.hrl").
+
+%% TODO(cpiro): either
+%% make exceptions know whether they need to be displayed
+%% or not exit with tExecptions for non-errors
+%% or "register" tExceptions with the logger (I LIKE!)
+%% ... we shouldn't need to build any specifics in here
+-include("thrift.hrl").
+-include("oop.hrl").
+-include("transport/tTransportException.hrl").
+
+%% gen_event callbacks
+-export([init/1, handle_event/2, handle_call/2,
+ handle_info/2, terminate/2, code_change/3]).
+
+-export([install/0, install/1]).
+
+%% ensure the regular logger is out and ours is in
+install() ->
+ install([]).
+install(Args) ->
+ %% remove loggers
+ lists:foreach(fun(Logger) ->
+ case Logger of
+ _ -> gen_event:delete_handler(error_logger, Logger, normal)
+ end end,
+ gen_event:which_handlers(error_logger)),
+
+ %% TODO(cpiro): sasl someday?
+ %% gen_event:add_handler(error_logger, sasl_report_file_h, {LogFile, all}),
+ gen_event:add_handler(error_logger, ?MODULE, Args).
+
+%% how to output
+format(Format, Data) ->
+ io:format(Format, Data).
+
+%% convenience
+sformat(Format, Data) ->
+ thrift_utils:sformat(Format, Data).
+
+%%====================================================================
+%% gen_event callbacks
+%%====================================================================
+%%--------------------------------------------------------------------
+%% @spec init(Args) -> {ok, State}.
+%%
+%% @doc
+%% Whenever a new event handler is added to an event manager,
+%% this function is called to initialize the event handler.
+%% @end
+%%--------------------------------------------------------------------
+init([]) ->
+ {ok, #thrift_logger_state{
+ term_width = 110,
+ force_one_line = true,
+ omit = [oop_new], % req_processed
+ gen_server_messages = false,
+ lookup = true
+ }};
+
+init([State]) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%% @spec handle_event(Event, State) -> {ok, State} |
+%% {swap_handler, Args1, State1, Mod2, Args2} |
+%% remove_handler.
+%%
+%% @doc
+%% Whenever an event manager receives an event sent using
+%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for
+%% each installed event handler to handle the event.
+%% @end
+%%--------------------------------------------------------------------
+handle_event2(Symbol, Pid, Type, Message, State) -> % Message must be a string
+ {ok, MessageSafe, NL} = regexp:gsub(Message, "[\n]+", " "), % collapse whitespace to one space
+
+ Type1 =
+ case Type of
+ "" -> "";
+ _ -> sformat("~p ", [Type])
+ end,
+
+ Banner = sformat("~s ~p ~s", [Symbol, Pid, Type1]),
+ BannerLen = length(Banner),
+ {Output, OutputSafe} =
+ try %% there's no way to see if Message is a string? just try
+ {sformat("~s", [Message]),
+ sformat("~s", [MessageSafe])}
+ catch X -> why_doesnt_this_work
+ end,
+
+ Length =
+ case (length(OutputSafe) + BannerLen) < State#thrift_logger_state.term_width of
+ true -> short;
+ false -> long
+ end,
+
+ OneLine =
+ case NL == 0 of
+ true -> oneliner;
+ false -> multiline
+ end,
+
+ case { State#thrift_logger_state.force_one_line, Length, OneLine } of
+ %% one line and short ... print as is
+ {_, short, oneliner} ->
+ format("~s~s~n", [Banner, OutputSafe]);
+
+ %% too long ... squash to one
+ {true, long, _} ->
+ O = Banner ++ OutputSafe,
+ Format = sformat("~~~ps >~n", [State#thrift_logger_state.term_width-2]), % e.g. "~80s >~n"
+ format(Format, [O]);
+
+ %% short but multiline... collapse to one
+ {true, short, multiline} ->
+ format("~s~s~n", [Banner, OutputSafe]);
+
+ %% just print it
+ _ ->
+ format("~s~n~s~n~n", [Banner, Output])
+ end.
+
+%%
+handle_event1({What, _Gleader, {Pid, Format, Data}}, State) when is_list(Format) ->
+ Symbol = case What of
+ error -> "!!";
+ warning_msg -> "**";
+ info_msg -> "..";
+ _Else -> "??"
+ end,
+
+ case Format of
+ "** Generic server ~p terminating \n** Last message in was ~p~n** When Server state == ~p~n** Reason for termination == ~n** ~p~n" ->
+ %% v- Pid is a pattern match, not a bind
+ [Pid, LastMessage, Obj, Reason] = Data,
+
+ %% TODO: move as much logic as possible out of thrift_logger
+ Ignore =
+ begin
+ is_tuple(Reason) andalso
+ size(Reason) >= 1 andalso element(1, Reason) == timeout
+ end
+ orelse
+ begin
+ case thrift_utils:unnest_record(Reason, tTransportException) of
+ error -> false;
+ {ok, TTE} ->
+ oop:get(TTE, type) == ?tTransportException_NOT_OPEN andalso
+ oop:get(TTE, message) == "in tSocket:read/2: gen_tcp:recv"
+ end
+ end,
+
+ case Ignore of
+ true ->
+ ok;
+ false ->
+ Format1 = "** gen_server terminating in message ~p~n** State = ~s~n** Reason = ~s~n",
+ Message = sformat(Format1, [LastMessage, oop:inspect(Obj), oop:inspect(Reason)]), %% TODO(cpiro): hope Reason is an object?
+ handle_event2(Symbol, Pid, "", Message, State)
+ end;
+ _ ->
+ Message = sformat(Format, Data),
+ handle_event2(Symbol, Pid, "", Message, State)
+ end,
+ {ok, State};
+
+handle_event1({What, _Gleader, {Pid, Type, Report}}, State) ->
+ Symbol = case What of
+ error_report -> "!!";
+ warning_report -> "**";
+ info_report -> "..";
+ _Else -> "??"
+ end,
+
+ case Type of
+ {thrift_info, TI} ->
+ %% should we show it?
+ case not lists:member(TI, State#thrift_logger_state.omit) of
+ true ->
+ Message = handle_thrift_info(TI, Report, State),
+ handle_event2(Symbol, Pid, "", Message, State);
+ false ->
+ ok
+ end;
+ crash_report ->
+ %% [Cruft|_] = Report, %%
+ %% {value, {_, Reason}} = lists:keysearch(error_info, 1, Cruft), %%
+ %% {value, {_, {_, _, [_,_,_,_, Obj, []]}}} = lists:keysearch(initial_call, 1, Cruft), %%
+ %% sformat("state == ~s~nreason ==~s", [oop:inspect(Obj), oop:inspect(Reason)]), %%
+ ok;
+ progress ->
+ ok;
+
+ _ ->
+ Message = sformat("|| ~s", [oop:inspect(Report)]),
+ handle_event2(Symbol, Pid, Type, Message, State)
+ end,
+ {ok, State};
+
+handle_event1(_Event, State) ->
+ handle_event2("??", "<?.?.?>", "", _Event, State),
+ {ok, State}.
+
+handle_event(Event, State) ->
+ try
+ handle_event1(Event, State)
+ catch
+ _:E ->
+ format("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n error logger error:~n ~p~n Event = ~p~n State = ~p~n ~p~n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n",
+ [E, Event, State, erlang:get_stacktrace()]),
+ {ok, State}
+ end.
+
+%% thrift info handlers
+handle_thrift_info(oop_new, {Args, Class, Object}, State) ->
+ %% arg Class can't come first! Then it'd look like a Class object
+ L = io_lib:format("~p:new(~s) = ~s", [Class, thrift_utils:unbrack(Args), oop:inspect(Object)]),
+ lists:flatten(L);
+
+handle_thrift_info(server_listening, {Port}, State) ->
+ sformat("server listening on port ~p", [Port]);
+
+handle_thrift_info(req_processed, {Value}, State) ->
+ sformat("request: ~p", [Value]);
+
+handle_thrift_info(conn_accepted, {AddrString}, State) ->
+ sformat("connection accepted from ~s", [AddrString]);
+
+handle_thrift_info(conn_timeout, {AddrString}, State) ->
+ sformat("connection timed out from ~s", [AddrString]);
+
+handle_thrift_info(conn_closed, {AddrString}, State) ->
+ sformat("connection closed from ~s", [AddrString]);
+
+handle_thrift_info(Else, Report, State) ->
+ sformat("~p ~s", [Else, oop:inspect(Report)]).
+
+%%--------------------------------------------------------------------
+%% @spec handle_call(Request, State) -> {ok, Reply, State} |
+%% {swap_handler, Reply, Args1, State1,
+%% Mod2, Args2} |
+%% {remove_handler, Reply}.
+%%
+%% @doc
+%% Whenever an event manager receives a request sent using
+%% gen_event:call/3,4, this function is called for the specified event
+%% handler to handle the request.
+%% @end
+%%--------------------------------------------------------------------
+handle_call(_Request, State) ->
+ Reply = ok,
+ {ok, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% @spec handle_info(Info, State) -> {ok, State} |
+%% {swap_handler, Args1, State1, Mod2, Args2} |
+%% remove_handler.
+%%
+%% @doc
+%% This function is called for each installed event handler when
+%% an event manager receives any other message than an event or a synchronous
+%% request (or a system message).
+%% @end
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%% @spec terminate(Reason, State) -> void().
+%%
+%% @doc
+%% Whenever an event handler is deleted from an event manager,
+%% this function is called. It should be the opposite of Module:init/1 and
+%% do any necessary cleaning up.
+%% @end
+%%--------------------------------------------------------------------
+terminate(normal, _State) ->
+ ok;
+terminate(Reason, _State) ->
+ format("*****************~n~n frick, error logger terminating: ~p~n~n*****************~n~n", [Reason]),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}.
+%%
+%% @doc
+%% Convert process state when code is changed
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%====================================================================
+%%% Internal functions
+%%====================================================================
diff --git a/lib/erl/src/thrift_oop_server.erl b/lib/erl/src/thrift_oop_server.erl
new file mode 100644
index 0000000..c21c434
--- /dev/null
+++ b/lib/erl/src/thrift_oop_server.erl
@@ -0,0 +1,216 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+%%%-------------------------------------------------------------------
+%%% @doc
+%%% @end
+%%%-------------------------------------------------------------------
+-module(thrift_oop_server).
+
+-behaviour(gen_server).
+%%--------------------------------------------------------------------
+%% Include files
+%%--------------------------------------------------------------------
+-include("oop.hrl").
+
+-include("thrift.hrl").
+
+%%--------------------------------------------------------------------
+%% External exports
+%%--------------------------------------------------------------------
+-export([
+ start_link/0,
+ stop/0
+ ]).
+
+%%--------------------------------------------------------------------
+%% gen_server callbacks
+%%--------------------------------------------------------------------
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
+
+%%--------------------------------------------------------------------
+%% record definitions
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% macro definitions
+%%--------------------------------------------------------------------
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% External functions
+%%====================================================================
+%%--------------------------------------------------------------------
+%% @doc Starts the server.
+%% @spec start_link() -> {ok, pid()} | {error, Reason}
+%% @end
+%%--------------------------------------------------------------------
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%--------------------------------------------------------------------
+%% @doc Stops the server.
+%% @spec stop() -> ok
+%% @end
+%%--------------------------------------------------------------------
+stop() ->
+ gen_server:cast(?SERVER, stop).
+
+%%====================================================================
+%% Server functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init/1
+%% Description: Initiates the server
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%%--------------------------------------------------------------------
+
+init({Class, Args}) ->
+ process_flag(trap_exit, true),
+ try
+ State = apply(Class, new, Args),
+ ?INFO(oop_new, {Args, Class, State}),
+ {ok, State}
+ catch
+ E -> {stop, {new_failed, E}}
+ end;
+
+init(_) ->
+ {stop, invalid_params}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_call/3
+%% Description: Handling call messages
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+
+handle_call(Request, From, State) ->
+ handle_either(call, Request, From, State).
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast/2
+%% Description: Handling cast messages
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+
+handle_cast(stop, State) ->
+ {stop, normal, State};
+
+handle_cast({Method, Args}, State) ->
+ handle_either(cast, {Method, Args}, undefined, State).
+
+-define(REPLY(Value, State),
+ case Type of
+ call -> {reply, Value, State};
+ cast -> {noreply, State}
+ end
+).
+
+handle_either(Type, Request, From, State) ->
+ %% error_logger:info_msg("~p: ~p", [?SERVER, oop:inspect(State)]),
+ %% error_logger:info_msg("handle_call(Request=~p, From=~p, State)", [Request, From]),
+
+ case Request of
+ {get, [Field]} ->
+ Value = oop:get(State, Field),
+ ?REPLY(Value, State);
+
+ {set, [Field, Value]} ->
+ State1 = oop:set(State, Field, Value),
+ ?REPLY(Value, State1);
+
+ {class, []} ->
+ ?REPLY(?CLASS(State), State);
+
+ {Method, Args} ->
+ handle_method(Type, State, Method, Args);
+
+ _ ->
+ error_logger:format("no match for Request = ~p", [Request]),
+ %% {stop, server_error, State}
+ {reply, server_error, State}
+ end.
+
+handle_method(Type, State, Method, Args) ->
+ %% is an effectful call?
+ Is_effectful = lists:prefix("effectful_", atom_to_list(Method)),
+ Call = oop:call(State, Method, Args),
+
+ %% TODO(cpiro): maybe add error handling here? = catch oop:call?
+
+ case {Is_effectful, Call} of
+ {true, {Retval, State1}} ->
+ ?REPLY(Retval, State1);
+
+ {true, _MalformedReturn} ->
+ %% TODO(cpiro): bad match -- remove when we're done converting
+ error_logger:format("oop:call(effectful_*,..,..) malformed return value ~p",
+ [_MalformedReturn]),
+ %% {stop, server_error, State}
+ {noreply, State};
+
+ {false, Retval} ->
+ ?REPLY(Retval, State)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info/2
+%% Description: Handling all non call/cast messages
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_info({'EXIT', Pid, Except} = All, State) ->
+ Result = try
+ oop:call(State, catches, [Pid, Except])
+ catch
+ exit:{missing_method, _} ->
+ unhandled
+ end,
+
+ case Result of
+ unhandled ->
+ error_logger:format("unhandled exit ~p", [All]),
+ {stop, All, State};
+ _WasHandled ->
+ {noreply, State}
+ end;
+
+handle_info(Info, State) ->
+ error_logger:info_msg("~p", [Info]),
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate/2
+%% Description: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%--------------------------------------------------------------------
+terminate(Reason, State) ->
+ %%error_logger:format("~p terminated!: ~p", [self(), Reason]),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Convert process state when code is changed
+%% Returns: {ok, NewState}
+ %%--------------------------------------------------------------------
+code_change(OldVsn, State, Extra) ->
+ {ok, State}.
+
+%%====================================================================
+%%% Internal functions
+%%====================================================================
diff --git a/lib/erl/src/thrift_utils.erl b/lib/erl/src/thrift_utils.erl
new file mode 100644
index 0000000..f78767c
--- /dev/null
+++ b/lib/erl/src/thrift_utils.erl
@@ -0,0 +1,52 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(thrift_utils).
+
+-include("transport/tTransportException.hrl").
+
+-export([tabulate/2, dict_size/1, sformat/2, unbrack/1, first_item/1, unnest_record/2]).
+
+%% tabulate
+tabulate(N,F) ->
+ tabulate(0, N, F).
+
+tabulate(N,M,_) when N==M ->
+ [];
+tabulate(N,M,F) ->
+ [F(N) | tabulate(N+1, M, F)].
+
+%% makin me sad
+dict_size(Dict) ->
+ dict:fold(fun (_,_,I) -> I+1 end,0,Dict).
+
+%% I CAN HAS EAZIER KTHX
+sformat(Format, Data) when is_list(Data) ->
+ lists:flatten(io_lib:format(Format, Data));
+sformat(Format, Item) ->
+ error_logger:warning_msg("sformat called with non-list Data: (~p, ~p)", [Format, Item]),
+ sformat(Format, [Item]).
+
+%% render a list and pick off the square brackets
+unbrack(List) ->
+ List1 = sformat("~w", [List]),
+ string:substr(List1, 2, length(List1)-2).
+
+first_item(DeepTuple) ->
+ case is_tuple(DeepTuple) of
+ true -> first_item(element(1, DeepTuple));
+ false -> DeepTuple
+ end.
+
+unnest_record(Term, RecordTag) ->
+ case is_record(Term, RecordTag) of
+ true ->
+ {ok, Term};
+ false when is_tuple(Term) ->
+ unnest_record(element(1, Term), RecordTag);
+ _ ->
+ error
+ end.
diff --git a/lib/erl/src/transport/tBufferedTransport.erl b/lib/erl/src/transport/tBufferedTransport.erl
new file mode 100644
index 0000000..48d9fba
--- /dev/null
+++ b/lib/erl/src/transport/tBufferedTransport.erl
@@ -0,0 +1,84 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tBufferedTransport).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("transport/tBufferedTransport.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/1, isOpen/1, open/1, close/1, read/2, effectful_write/2, effectful_flush/1]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(transport);
+?DEFINE_ATTR(wbuf).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tTransport.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(transport) ++
+ ?FORMAT_ATTR(wbuf).
+
+%%%
+%%% class methods
+%%%
+
+new(Transport) ->
+ Super = (super()):new(),
+ #?MODULE{super=Super, transport=Transport, wbuf=""}.
+
+%%%
+%%% instance methods
+%%%
+
+isOpen(This) ->
+ Transport = oop:get(This, transport),
+ ?R0(Transport, isOpen).
+
+open(This) ->
+ Transport = oop:get(This, transport),
+ ?R0(Transport, open).
+
+close(This) ->
+ Transport = oop:get(This, transport),
+ ?R0(Transport, close).
+
+read(This, Sz) ->
+ Transport = oop:get(This, transport),
+ ?R1(Transport, read, Sz).
+
+effectful_write(This, Buf) -> % be sure to rebind This to the retval
+ Wbuf = oop:get(This, wbuf),
+ This1 = oop:set(This, wbuf, Wbuf++Buf), % TODO: ++ efficiency?
+ {ok, This1}.
+
+effectful_flush(This) ->
+ Wbuf = oop:get(This, wbuf),
+ Transport = oop:get(This, transport),
+ ?R1(Transport, effectful_write, Wbuf),
+ ?R0(Transport, effectful_flush),
+ This1 = oop:set(This, wbuf, ""),
+ {ok, This1}.
diff --git a/lib/erl/src/transport/tBufferedTransportFactory.erl b/lib/erl/src/transport/tBufferedTransportFactory.erl
new file mode 100644
index 0000000..9746aba
--- /dev/null
+++ b/lib/erl/src/transport/tBufferedTransportFactory.erl
@@ -0,0 +1,54 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tBufferedTransportFactory).
+
+-include("oop.hrl").
+-include("transport/tBufferedTransport.hrl").
+-include("transport/tBufferedTransportFactory.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, getTransport/2]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?ATTR_DUMMY.
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tTransportFactory.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new() ->
+ Super = (super()):new(),
+ #?MODULE{super=Super}.
+
+%%%
+%%% instance methods
+%%%
+
+getTransport(_This, Trans) ->
+ gen_server:start_link(tBufferedTransport, {new, [Trans]}).
diff --git a/lib/erl/src/transport/tErlAcceptor.erl b/lib/erl/src/transport/tErlAcceptor.erl
new file mode 100644
index 0000000..f3308cf
--- /dev/null
+++ b/lib/erl/src/transport/tErlAcceptor.erl
@@ -0,0 +1,153 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tErlAcceptor).
+
+-include("oop.hrl").
+-include("thrift.hrl").
+-include("tApplicationException.hrl").
+-include("transport/tTransportException.hrl").
+-include("transport/tServerSocket.hrl").
+-include("transport/tErlAcceptor.hrl").
+
+-include_lib("kernel/include/inet.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/3, accept/4]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(serverPid);
+?DEFINE_ATTR(transportFactory);
+?DEFINE_ATTR(protocolFactory).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tServerTransport.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(serverPid) ++ ", " ++
+ ?FORMAT_ATTR(transportFactory) ++ ", " ++
+ ?FORMAT_ATTR(protocolFactory).
+
+%%%
+%%% class methods
+%%%
+
+new(ServerPid, TF, PF) ->
+ Super = (super()):new(),
+ #?MODULE{super = Super,
+ serverPid = ServerPid,
+ transportFactory = TF,
+ protocolFactory = PF
+ }.
+
+%%%
+%%% instance methods
+%%%
+
+accept(This, ListenSocket, GP, Handler) ->
+ ServerPid = oop:get(This, serverPid),
+
+ case catch gen_tcp:accept(ListenSocket) of
+ {ok, Socket} ->
+ ?C0(ServerPid, effectful_new_acceptor), %% cast to create new acceptor
+
+ AddrString = render_addr(Socket),
+ ?INFO(conn_accepted, {AddrString}),
+
+ %% start_new(tSocket, [])
+ Client = oop:start_new(tSocket, []),
+ ?R1(Client, effectful_setHandle, Socket), %% TODO(cpiro): should we just let this be a param to the constructor?
+
+ %% cpiro: OPAQUE!! Trans = Client
+ TF = oop:get(This, transportFactory),
+ Trans = ?F1(TF, getTransport, Client),
+
+ %% cpiro: OPAQUE!! Prot = start_new(tBinaryProtocol, [Trans])
+ PF = oop:get(This, protocolFactory),
+ Prot = ?F1(PF, getProtocol, Trans),
+
+ %% start_new(, ...)
+ Processor = oop:start_new(tErlProcessor, [GP, Handler]), %% TODO
+
+ case receive_loop(This, Processor, Prot, Prot) of
+ conn_timeout ->
+ ?INFO(conn_timeout, {AddrString});
+ conn_closed ->
+ ?INFO(conn_closed, {AddrString});
+ {Class, Else} ->
+ ?ERROR("unhandled ~p in tErlAcceptor: ~p", [Class, Else])
+ end,
+ exit(normal);
+
+ Else ->
+ R = thrift_utils:sformat("accept() failed: ~p", [Else]),
+ exit(tTransportException:new(R))
+ end.
+
+receive_loop(This, Processor, Iprot, Oprot) ->
+ try ?R2(Processor, process, Iprot, Oprot) of
+ {error, TAE} when is_record(TAE, tApplicationException),
+ TAE#tApplicationException.type == ?tApplicationException_HANDLER_ERROR ->
+ ?ERROR("handler returned an error: ~p", [oop:get(TAE, message)]),
+ receive_loop(This, Processor, Iprot, Oprot);
+ Value ->
+ ?INFO(req_processed, {Value}),
+ receive_loop(This, Processor, Iprot, Oprot)
+ catch
+ exit:{timeout, _} ->
+ conn_timeout;
+
+ %% the following clause must be last
+ %% cpiro: would be best to implement an is_a/2 guard BIF
+ %% cpiro: breaks if it's a subclass of tTransportException
+ %% since unnest_record knows nothing about oop
+ Class:Else ->
+ case thrift_utils:unnest_record(Else, tTransportException) of
+ {ok, TTE} when TTE#tTransportException.type == ?tTransportException_NOT_OPEN ->
+ conn_closed;
+ _ ->
+ {Class, Else}
+ end
+ end.
+
+%% helper functions
+
+%% @param Socket the socket in question
+%% TODO(cpiro): there probably needs to be a switch for DoLookup somewhere prominent and outside the lib,
+%% probably at the "application" level
+render_addr(Socket) ->
+ DoLookup = true,
+ {ok, {Peer, Port}} = inet:peername(Socket),
+
+ case Peer of
+ _ when DoLookup ->
+ case catch inet:gethostbyaddr(Peer) of
+ {ok, Hostent} ->
+ thrift_utils:sformat("~s:~p", [Hostent#hostent.h_name, Port]);
+ _ ->
+ "??"
+ end;
+
+ {A,B,C,D} when not DoLookup ->
+ thrift_utils:sformat("~p.~p.~p.~p:~p", [A,B,C,D,Port])
+ end.
diff --git a/lib/erl/src/transport/tServerSocket.erl b/lib/erl/src/transport/tServerSocket.erl
new file mode 100644
index 0000000..d0cbf92
--- /dev/null
+++ b/lib/erl/src/transport/tServerSocket.erl
@@ -0,0 +1,96 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tServerSocket).
+
+-include("oop.hrl").
+-include("thrift.hrl").
+-include("transport/tServerSocket.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/1, effectful_listen/1, accept/1, effectful_close/1]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(port);
+?DEFINE_ATTR(handle).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tServerTransport.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(port) ++ ", " ++
+ ?FORMAT_ATTR(handle).
+
+%%%
+%%% class methods
+%%%
+
+new(Port) ->
+ Super = (super()):new(),
+ #?MODULE{super = Super, port = Port, handle = nil}.
+
+%%%
+%%% instance methods
+%%%
+
+effectful_listen(This) ->
+ Port = oop:get(This, port),
+ Options = [binary, {packet, 0}, {active, false}], % was []
+
+ case gen_tcp:listen(Port, Options) of
+ {ok, ListenSocket} ->
+ This1 = oop:set(This, handle, ListenSocket),
+ {ok, This1}
+
+ % {error, _} ->
+ % TODO: no error handling in Ruby version?
+ end.
+
+accept(This) ->
+ case oop:get(This, handle) of
+ nil ->
+ nil; % cpiro: sic the Ruby code
+
+ Handle ->
+ case gen_tcp:accept(Handle) of
+ {ok, Sock} ->
+ Trans = oop:start_new(tSocket, []),
+ ?R1(Trans, effectful_setHandle, Sock),
+ Trans
+ % {error, _} ->
+ % TODO: no error handling in Ruby version?
+ end
+ end.
+
+effectful_close(This) ->
+ case oop:get(This, handle) of
+ nil ->
+ {nil, This};
+ Handle ->
+ case gen_tcp:close(Handle) of
+ ok ->
+ {ok, This} % cpiro: sic the Ruby version: don't set handle to nil
+ % {error, _} ->
+ % TODO: no error handling in Ruby version?
+ end
+ end.
diff --git a/lib/erl/src/transport/tServerTransport.erl b/lib/erl/src/transport/tServerTransport.erl
new file mode 100644
index 0000000..dfc9ccb
--- /dev/null
+++ b/lib/erl/src/transport/tServerTransport.erl
@@ -0,0 +1,52 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tServerTransport).
+
+-include("oop.hrl").
+-include("transport/tServerTransport.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?ATTR_DUMMY.
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ none.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new() ->
+ #?MODULE{}.
+
+%%%
+%%% instance methods
+%%%
+
+getTransport(_This, Trans) ->
+ Trans.
diff --git a/lib/erl/src/transport/tSocket.erl b/lib/erl/src/transport/tSocket.erl
new file mode 100644
index 0000000..491d86b
--- /dev/null
+++ b/lib/erl/src/transport/tSocket.erl
@@ -0,0 +1,123 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tSocket).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("transport/tTransportException.hrl").
+% -include("transport/tTransport.hrl").
+-include("transport/tSocket.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, new/1, new/2,
+ effectful_setHandle/2, effectful_open/1,
+ isOpen/1, write/2, read/2, effectful_close/1]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(host);
+?DEFINE_ATTR(port);
+?DEFINE_ATTR(handle).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tTransport.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(host) ++ ", " ++
+ ?FORMAT_ATTR(port) ++ ", " ++
+ ?FORMAT_ATTR(handle).
+
+%%%
+%%% class methods
+%%%
+
+new(Host, Port) ->
+ Super = (super()):new(),
+ #?MODULE{super=Super, host=Host, port=Port, handle=nil}.
+
+new(Host) ->
+ new(Host, 9090).
+
+new() ->
+ new("localhost", 9090).
+
+%%%
+%%% instance methods
+%%%
+
+effectful_setHandle(This, Handle) ->
+ {ok, oop:set(This, handle, Handle)}.
+
+effectful_open(This) ->
+ Host = oop:get(This, host),
+ Port = oop:get(This, port),
+ Options = [],
+
+ case gen_tcp:connect(Host, Port, Options) of
+ {error, _} ->
+ exit(tTransportException:new(
+ ?tTransportException_NOT_OPEN,
+ "Could not connect to " ++ Host ++ ":" ++ Port)
+ );
+ {ok, Socket} ->
+ {ok, oop:set(This, handle, Socket)}
+ end.
+
+isOpen(This) ->
+ oop:get(This, handle) /= nil.
+
+write(This, Str) ->
+ Handle = oop:get(This, handle),
+ Val = gen_tcp:send(Handle, Str),
+
+ %% error_logger:info_msg("WRITE |~p| (~p)", [Str,Val]),
+
+ case Val of
+ {error, _} ->
+ throw(tTransportException:new(?tTransportException_NOT_OPEN, "in write"));
+ ok ->
+ ok
+ end.
+
+read(This, Sz) ->
+ Handle = oop:get(This, handle),
+ case gen_tcp:recv(Handle, Sz) of
+ {ok, []} ->
+ Host = oop:get(This, host),
+ Port = oop:get(This, port),
+ throw(tTransportException:new(?tTransportException_UNKNOWN, "TSocket: Could not read " ++ Sz ++ "bytes from " ++ Host ++ ":" ++ Port));
+ {ok, Data} ->
+ Data;
+ {error, Error} ->
+ exit(tTransportException:new(?tTransportException_NOT_OPEN, "in tSocket:read/2: gen_tcp:recv"))
+ end.
+
+effectful_close(This) ->
+ case oop:get(This, handle) of
+ nil ->
+ {ok, This};
+ Handle ->
+ gen_tcp:close(Handle),
+ {ok, oop:set(This, handle, nil)}
+ end.
diff --git a/lib/erl/src/transport/tTransport.erl b/lib/erl/src/transport/tTransport.erl
new file mode 100644
index 0000000..6ec115a
--- /dev/null
+++ b/lib/erl/src/transport/tTransport.erl
@@ -0,0 +1,86 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tTransport).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("transport/tTransport.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, isOpen/1, open/1, close/1, read/2, readAll/2, effectful_write/2, effectful_flush/1]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?ATTR_DUMMY.
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ none.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new() ->
+ #?MODULE{}.
+
+%%%
+%%% instance methods
+%%%
+
+
+
+isOpen(_This) -> nil.
+open(_This) -> nil.
+close(_This) -> nil.
+read(_This, _Sz) -> nil.
+
+readAll(This, Sz) ->
+ readAll_loop(This, Sz, "", 0).
+
+readAll_loop(This, Sz, Buff, Have) ->
+ if
+ Have < Sz ->
+ Chunk = ?L1(read, Sz - Have),
+
+ %% man gen_tcp:
+ %% exactly Length bytes are returned, or an error;
+ %% possibly discarding less than Length bytes of data when
+ %% the socket gets closed from the other side.
+
+ %% error_logger:info_msg("READ |~p|", [Chunk]),
+
+ Have1 = Have + (Sz-Have), % length(Chunk)
+ Buff1 = Buff ++ Chunk, % TODO: ++ efficiency?
+ readAll_loop(This, Sz, Buff1, Have1);
+ true ->
+ Buff
+ end.
+
+effectful_write(This, _Buf) ->
+ {nil, This}.
+
+effectful_flush(This) ->
+ {nil, This}.
diff --git a/lib/erl/src/transport/tTransportException.erl b/lib/erl/src/transport/tTransportException.erl
new file mode 100644
index 0000000..43a7afa
--- /dev/null
+++ b/lib/erl/src/transport/tTransportException.erl
@@ -0,0 +1,58 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tTransportException).
+
+-include("oop.hrl").
+
+-include("thrift.hrl").
+-include("transport/tTransportException.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, new/1, new/2]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?DEFINE_ATTR(super);
+?DEFINE_ATTR(type).
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ tException.
+
+%%% inspect(This) -> string()
+
+inspect(This) ->
+ ?FORMAT_ATTR(type).
+
+%%%
+%%% class methods
+%%%
+
+new(Type, Message) ->
+ Super = (super()):new(Message),
+ #?MODULE{super=Super, type=Type}.
+
+new() ->
+ new(?tTransportException_UNKNOWN, undefined).
+new(Type) ->
+ new(Type, undefined).
+
+%%%
+%%% instance methods
+%%%
diff --git a/lib/erl/src/transport/tTransportFactory.erl b/lib/erl/src/transport/tTransportFactory.erl
new file mode 100644
index 0000000..1c8ca61
--- /dev/null
+++ b/lib/erl/src/transport/tTransportFactory.erl
@@ -0,0 +1,52 @@
+%%% Copyright (c) 2007- Facebook
+%%% Distributed under the Thrift Software License
+%%%
+%%% See accompanying file LICENSE or visit the Thrift site at:
+%%% http://developers.facebook.com/thrift/
+
+-module(tTransportFactory).
+
+-include("oop.hrl").
+-include("transport/tTransportFactory.hrl").
+
+-behavior(oop).
+
+-export([attr/4, super/0, inspect/1]).
+
+-export([new/0, getTransport/2]).
+
+%%%
+%%% define attributes
+%%% 'super' is required unless ?MODULE is a base class
+%%%
+
+?ATTR_DUMMY.
+
+%%%
+%%% behavior callbacks
+%%%
+
+%%% super() -> SuperModule = atom()
+%%% | none
+
+super() ->
+ none.
+
+%%% inspect(This) -> string()
+
+inspect(_This) ->
+ "".
+
+%%%
+%%% class methods
+%%%
+
+new() ->
+ #?MODULE{}.
+
+%%%
+%%% instance methods
+%%%
+
+getTransport(_This, Trans) ->
+ Trans.