blob: ef512bc1e61cf1bd9aaebfc26f25bbaf9354659a [file] [log] [blame]
%%% 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_oop_server).
-behaviour(gen_server).
-include("oop.hrl").
-include("thrift.hrl").
-include("transport/tTransportException.hrl").
-include("protocol/tProtocolException.hrl").
-export([
start_link/0,
stop/0
]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
%%%
%%% api
%%%
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
stop() ->
gen_server:cast(?SERVER, stop).
%%%
%%% init
%%%
init({Class, Args}) ->
process_flag(trap_exit, true),
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}
catch
E -> {stop, {new_failed, E}}
end;
init(_) ->
{stop, invalid_params}.
%%%
%%% call and cast
%%%
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).
reply(call, Value, State) -> {reply, Value, State};
reply(cast, _Value, State) -> {noreply, State}.
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(Type, Value, State);
{set, [Field, Value]} ->
State1 = oop:set(State, Field, Value),
reply(Type, Value, State1);
{class, []} ->
reply(Type, ?CLASS(State), State);
{Method, Args} ->
handle_method(Type, State, Method, Args);
_ ->
?ERROR("thrift no match for Request = ~p", [Request]),
{stop, server_error, State}
%% {reply, server_error, State}
end.
handle_method(Type, State, Method, Args) ->
Is_effectful = lists:prefix("effectful_", atom_to_list(Method)),
try {Is_effectful, oop:call(State, Method, Args)} of
{true, {Retval, State1}} ->
reply(Type, Retval, State1);
{true, _MalformedReturn} ->
%% TODO(cpiro): bad match -- remove when we're done converting
?ERROR("oop:call(effectful_*,..,..) malformed return value ~p",
[_MalformedReturn]),
{stop, server_error, State};
%% {noreply, State};
{false, Retval} ->
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.
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.
%%%
%%% 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}
end;
handle_info(Info, State) ->
?INFO("~p", [Info]),
{noreply, State}.
terminate(Reason, State) ->
ok.
code_change(OldVsn, State, Extra) ->
{ok, State}.