[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
-%%====================================================================