| %%% 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("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}. |
| |
| %%-------------------------------------------------------------------- |
| %% 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 |
| %%==================================================================== |