[thrift] gut Erlang exception handling

Summary: * move type field to tException from subclasses
          * add backtrace to tException
          * add oop:is_a
          * on exit, wrap exceptions in {thrift_exception, E} ... otherwise can't distinguish e.g. exit:{{tBinProtException, {tException, ...}}, Stack} vs. exit:{tBinProtException, {tException, ...} -- I hate erlang
          * all throws/exits to tException:throw which does the wrapping described above

Reviewed By: eletuchy

Test Plan: been using this code on my live server ^_^

Revert: OK


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665350 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/erl/src/thrift_oop_server.erl b/lib/erl/src/thrift_oop_server.erl
index 052578c..d3bc720 100644
--- a/lib/erl/src/thrift_oop_server.erl
+++ b/lib/erl/src/thrift_oop_server.erl
@@ -4,77 +4,43 @@
 %%% 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
-%%--------------------------------------------------------------------
+-include("transport/tTransportException.hrl").
+-include("protocol/tProtocolException.hrl").
+
 -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
-%%--------------------------------------------------------------------
+%%%
+%%% api
+%%%
+
 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
+%%%
 
 init({Class, Args}) ->
     process_flag(trap_exit, true),
-    try
+    try %% TODO use apply_if_defined
         State = apply(Class, new, Args),
         ?INFO("thrift ~p:new(~s) = ~s", [Class, thrift_utils:unbrack(Args), oop:inspect(State)]),
         {ok, State}
@@ -85,132 +51,102 @@
 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)
-%%--------------------------------------------------------------------
+%%%
+%%% call and cast
+%%%
 
-handle_call(Request, From, State) ->
-    handle_either(call, Request, From, State).
+handle_call(Request, From, State)  -> handle_call_cast(call, Request, From, State).
+handle_cast(stop, State)           -> {stop, normal, State};
+handle_cast({Method, Args}, State) -> handle_call_cast(cast, {Method, Args}, undefined, State).
 
-%%--------------------------------------------------------------------
-%% Function: handle_cast/2
-%% Description: Handling cast messages
-%% Returns: {noreply, State}          |
-%%          {noreply, State, Timeout} |
-%%          {stop, Reason, State}            (terminate/2 is called)
-%%--------------------------------------------------------------------
+reply(call, Value, State)  -> {reply, Value, State};
+reply(cast, _Value, State) -> {noreply, State}.
 
-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]),
+handle_call_cast(Type, Request, From, State) ->
+    %% ?INFO("~p: ~p", [?SERVER, oop:inspect(State)]),
+    %% ?INFO("handle_call(Request=~p, From=~p, State)", [Request, From]),
 
     case Request of
         {get, [Field]} ->
             Value = oop:get(State, Field),
-            ?REPLY(Value, State);
+            reply(Type, Value, State);
 
         {set, [Field, Value]} ->
             State1 = oop:set(State, Field, Value),
-            ?REPLY(Value, State1);
+            reply(Type, Value, State1);
 
         {class, []} ->
-            ?REPLY(?CLASS(State), State);
+            reply(Type, ?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}
+            ?ERROR("thrift 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
+    try {Is_effectful, oop:call(State, Method, Args)} of
         {true, {Retval, State1}} ->
-            ?REPLY(Retval, State1);
+            reply(Type, 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};
+            ?ERROR("oop:call(effectful_*,..,..) malformed return value ~p",
+                   [_MalformedReturn]),
+            {stop, server_error, State};
+            %% {noreply, State};
 
         {false, Retval} ->
-            ?REPLY(Retval, State)
+            reply(Type, Retval, State)
+
+    catch
+        exit:{thrift_exception, E}          -> handle_exception(E, nothing);
+        exit:{{thrift_exception, E}, Stack} -> handle_exception(E, Stack);
+        exit:normal                         -> exit(normal);
+        exit:(X = {timeout, _})             -> exit(X);
+        exit:Other ->
+            exit(Other)
     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,
+handle_exception(E, Stack) ->
+    %% ?ERROR("texception ~p", [E]),
+    case {oop:is_a(E, tException), Stack} of
+        {true, nothing} -> % good
+            exit({thrift_exception, E});
+        {true, _} -> % good
+            E1 = tException:add_backtrace_element(E, Stack),
+            exit({thrift_exception, E1});
+        false -> % shit
+            ?ERROR("exception wasn't really a tException ~p", [E]),
+            exit(bum)
+    end.
 
-    case Result of
-        unhandled ->
+%%%
+%%% info, terminate, and code_change
+%%%
+
+handle_info({'EXIT', Pid, Except} = All, State) ->
+    case Except of
+        normal ->
+            {noreply, State};
+        {normal, _} ->
+            {noreply, State};
+        _unhandled ->
             error_logger:format("unhandled exit ~p", [All]),
-            {stop, All, State};
-        _WasHandled ->
-            {noreply, State}
+            {stop, All, State}
     end;
 
 handle_info(Info, State) ->
-    error_logger:info_msg("~p", [Info]),
+    ?INFO("~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
-%%====================================================================