blob: c38bc9165411d38772b016af4b59335dc13c0929 [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001%%
2%% Licensed to the Apache Software Foundation (ASF) under one
3%% or more contributor license agreements. See the NOTICE file
4%% distributed with this work for additional information
5%% regarding copyright ownership. The ASF licenses this file
6%% to you under the Apache License, Version 2.0 (the
7%% "License"); you may not use this file except in compliance
8%% with the License. You may obtain a copy of the License at
9%%
10%% http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing,
13%% software distributed under the License is distributed on an
14%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15%% KIND, either express or implied. See the License for the
16%% specific language governing permissions and limitations
17%% under the License.
18%%
19
David Reiss2c534032008-06-11 00:58:00 +000020-module(thrift_client).
21
David Reiss2c534032008-06-11 00:58:00 +000022%% API
David Reiss3f660a42010-08-30 22:05:29 +000023-export([new/2, call/3, send_call/3, close/1]).
David Reiss2c534032008-06-11 00:58:00 +000024
25-include("thrift_constants.hrl").
26-include("thrift_protocol.hrl").
27
David Reiss3f660a42010-08-30 22:05:29 +000028-record(tclient, {service, protocol, seqid}).
David Reissfc427af2008-06-11 01:11:57 +000029
30
David Reiss3f660a42010-08-30 22:05:29 +000031new(Protocol, Service)
32 when is_atom(Service) ->
33 {ok, #tclient{protocol = Protocol,
34 service = Service,
35 seqid = 0}}.
David Reiss5e530af2009-06-04 02:01:24 +000036
David Reiss3f660a42010-08-30 22:05:29 +000037-spec call(#tclient{}, atom(), list()) -> {#tclient{}, {ok, term()} | {error, term()}}.
38call(Client = #tclient{}, Function, Args)
39 when is_atom(Function), is_list(Args) ->
40 case send_function_call(Client, Function, Args) of
41 {Client1, ok} ->
42 receive_function_result(Client1, Function);
43 Else ->
44 Else
David Reisse5a4d0c2008-06-11 01:02:10 +000045 end.
David Reiss2c534032008-06-11 00:58:00 +000046
David Reissa2f45972008-06-11 01:13:33 +000047
David Reiss65cf7202008-06-11 01:12:20 +000048%% Sends a function call but does not read the result. This is useful
David Reissc51986f2009-03-24 20:01:25 +000049%% if you're trying to log non-oneway function calls to write-only
David Reiss65cf7202008-06-11 01:12:20 +000050%% transports like thrift_disk_log_transport.
David Reiss3f660a42010-08-30 22:05:29 +000051-spec send_call(#tclient{}, atom(), list()) -> {#tclient{}, ok}.
52send_call(Client = #tclient{}, Function, Args)
53 when is_atom(Function), is_list(Args) ->
54 send_function_call(Client, Function, Args).
David Reiss65cf7202008-06-11 01:12:20 +000055
David Reiss3f660a42010-08-30 22:05:29 +000056-spec close(#tclient{}) -> ok.
57close(#tclient{protocol=Protocol}) ->
58 thrift_protocol:close_transport(Protocol).
David Reiss464e3002008-06-11 01:00:45 +000059
David Reiss2c534032008-06-11 00:58:00 +000060
61%%--------------------------------------------------------------------
62%%% Internal functions
63%%--------------------------------------------------------------------
David Reiss3f660a42010-08-30 22:05:29 +000064-spec send_function_call(#tclient{}, atom(), list()) -> {#tclient{}, ok | {error, term()}}.
David Reissc4657992010-08-30 22:05:31 +000065send_function_call(Client = #tclient{protocol = Proto0,
David Reiss3f660a42010-08-30 22:05:29 +000066 service = Service,
67 seqid = SeqId},
David Reiss2c534032008-06-11 00:58:00 +000068 Function,
69 Args) ->
70 Params = Service:function_info(Function, params_type),
David Reiss3f660a42010-08-30 22:05:29 +000071 case Params of
72 no_function ->
73 {Client, {error, {no_function, Function}}};
74 {struct, PList} when length(PList) =/= length(Args) ->
75 {Client, {error, {bad_args, Function, Args}}};
76 {struct, _PList} ->
77 Begin = #protocol_message_begin{name = atom_to_list(Function),
78 type = ?tMessageType_CALL,
79 seqid = SeqId},
David Reissc4657992010-08-30 22:05:31 +000080 {Proto1, ok} = thrift_protocol:write(Proto0, Begin),
81 {Proto2, ok} = thrift_protocol:write(Proto1, {Params, list_to_tuple([Function | Args])}),
82 {Proto3, ok} = thrift_protocol:write(Proto2, message_end),
83 {Proto4, ok} = thrift_protocol:flush_transport(Proto3),
84 {Client#tclient{protocol = Proto4}, ok}
David Reiss3f660a42010-08-30 22:05:29 +000085 end.
David Reiss2c534032008-06-11 00:58:00 +000086
David Reiss3f660a42010-08-30 22:05:29 +000087-spec receive_function_result(#tclient{}, atom()) -> {#tclient{}, {ok, term()} | {error, term()}}.
88receive_function_result(Client = #tclient{service = Service}, Function) ->
Bryan Duxburyd3879f82010-08-19 05:06:02 +000089 ResultType = Service:function_info(Function, reply_type),
David Reiss3f660a42010-08-30 22:05:29 +000090 read_result(Client, Function, ResultType).
Bryan Duxburyd3879f82010-08-19 05:06:02 +000091
David Reiss3f660a42010-08-30 22:05:29 +000092read_result(Client, _Function, oneway_void) ->
93 {Client, {ok, ok}};
Bryan Duxburyd3879f82010-08-19 05:06:02 +000094
David Reiss3f660a42010-08-30 22:05:29 +000095read_result(Client = #tclient{protocol = Proto,
96 seqid = SeqId},
Bryan Duxburyd3879f82010-08-19 05:06:02 +000097 Function,
98 ReplyType) ->
David Reissf32d0fb2010-08-30 22:05:00 +000099 case thrift_protocol:read(Proto, message_begin) of
Bryan Duxburyd3879f82010-08-19 05:06:02 +0000100 #protocol_message_begin{seqid = RetSeqId} when RetSeqId =/= SeqId ->
David Reiss3f660a42010-08-30 22:05:29 +0000101 {Client, {error, {bad_seq_id, SeqId}}};
Bryan Duxburyd3879f82010-08-19 05:06:02 +0000102
103 #protocol_message_begin{type = ?tMessageType_EXCEPTION} ->
David Reiss3f660a42010-08-30 22:05:29 +0000104 handle_application_exception(Client);
Bryan Duxburyd3879f82010-08-19 05:06:02 +0000105
106 #protocol_message_begin{type = ?tMessageType_REPLY} ->
David Reiss3f660a42010-08-30 22:05:29 +0000107 handle_reply(Client, Function, ReplyType)
Bryan Duxburyd3879f82010-08-19 05:06:02 +0000108 end.
109
David Reiss3f660a42010-08-30 22:05:29 +0000110
111handle_reply(Client = #tclient{protocol = Proto,
112 service = Service},
David Reiss2c534032008-06-11 00:58:00 +0000113 Function,
114 ReplyType) ->
115 {struct, ExceptionFields} = Service:function_info(Function, exceptions),
116 ReplyStructDef = {struct, [{0, ReplyType}] ++ ExceptionFields},
David Reissf32d0fb2010-08-30 22:05:00 +0000117 {ok, Reply} = thrift_protocol:read(Proto, ReplyStructDef),
David Reiss3f660a42010-08-30 22:05:29 +0000118 ok = thrift_protocol:read(Proto, message_end),
David Reiss2c534032008-06-11 00:58:00 +0000119 ReplyList = tuple_to_list(Reply),
120 true = length(ReplyList) == length(ExceptionFields) + 1,
121 ExceptionVals = tl(ReplyList),
122 Thrown = [X || X <- ExceptionVals,
123 X =/= undefined],
David Reiss3f660a42010-08-30 22:05:29 +0000124 case Thrown of
125 [] when ReplyType == {struct, []} ->
126 {Client, {ok, ok}};
127 [] ->
128 {Client, {ok, hd(ReplyList)}};
129 [Exception] ->
130 throw({Client, {exception, Exception}})
131 end.
David Reiss6b3e40f2008-06-11 00:59:03 +0000132
David Reiss3f660a42010-08-30 22:05:29 +0000133handle_application_exception(Client = #tclient{protocol = Proto}) ->
134 {ok, Exception} =
135 thrift_protocol:read(Proto, ?TApplicationException_Structure),
David Reissf32d0fb2010-08-30 22:05:00 +0000136 ok = thrift_protocol:read(Proto, message_end),
David Reiss55ff70f2008-06-11 00:58:25 +0000137 XRecord = list_to_tuple(
138 ['TApplicationException' | tuple_to_list(Exception)]),
David Reiss1af18682008-06-11 01:01:36 +0000139 error_logger:error_msg("X: ~p~n", [XRecord]),
David Reiss55ff70f2008-06-11 00:58:25 +0000140 true = is_record(XRecord, 'TApplicationException'),
David Reiss3f660a42010-08-30 22:05:29 +0000141 throw({Client, {exception, XRecord}}).