David Reiss | ea2cba8 | 2009-03-30 21:35:00 +0000 | [diff] [blame] | 1 | %% |
| 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 Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 20 | -module(thrift_client). |
| 21 | |
David Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 22 | %% API |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 23 | -export([new/2, call/3, send_call/3, close/1]). |
David Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 24 | |
| 25 | -include("thrift_constants.hrl"). |
| 26 | -include("thrift_protocol.hrl"). |
| 27 | |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 28 | -record(tclient, {service, protocol, seqid}). |
David Reiss | fc427af | 2008-06-11 01:11:57 +0000 | [diff] [blame] | 29 | |
| 30 | |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 31 | new(Protocol, Service) |
| 32 | when is_atom(Service) -> |
| 33 | {ok, #tclient{protocol = Protocol, |
| 34 | service = Service, |
| 35 | seqid = 0}}. |
David Reiss | 5e530af | 2009-06-04 02:01:24 +0000 | [diff] [blame] | 36 | |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 37 | -spec call(#tclient{}, atom(), list()) -> {#tclient{}, {ok, term()} | {error, term()}}. |
| 38 | call(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 Reiss | e5a4d0c | 2008-06-11 01:02:10 +0000 | [diff] [blame] | 45 | end. |
David Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 46 | |
David Reiss | a2f4597 | 2008-06-11 01:13:33 +0000 | [diff] [blame] | 47 | |
David Reiss | 65cf720 | 2008-06-11 01:12:20 +0000 | [diff] [blame] | 48 | %% Sends a function call but does not read the result. This is useful |
David Reiss | c51986f | 2009-03-24 20:01:25 +0000 | [diff] [blame] | 49 | %% if you're trying to log non-oneway function calls to write-only |
David Reiss | 65cf720 | 2008-06-11 01:12:20 +0000 | [diff] [blame] | 50 | %% transports like thrift_disk_log_transport. |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 51 | -spec send_call(#tclient{}, atom(), list()) -> {#tclient{}, ok}. |
| 52 | send_call(Client = #tclient{}, Function, Args) |
| 53 | when is_atom(Function), is_list(Args) -> |
| 54 | send_function_call(Client, Function, Args). |
David Reiss | 65cf720 | 2008-06-11 01:12:20 +0000 | [diff] [blame] | 55 | |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 56 | -spec close(#tclient{}) -> ok. |
| 57 | close(#tclient{protocol=Protocol}) -> |
| 58 | thrift_protocol:close_transport(Protocol). |
David Reiss | 464e300 | 2008-06-11 01:00:45 +0000 | [diff] [blame] | 59 | |
David Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 60 | |
| 61 | %%-------------------------------------------------------------------- |
| 62 | %%% Internal functions |
| 63 | %%-------------------------------------------------------------------- |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 64 | -spec send_function_call(#tclient{}, atom(), list()) -> {#tclient{}, ok | {error, term()}}. |
David Reiss | c465799 | 2010-08-30 22:05:31 +0000 | [diff] [blame] | 65 | send_function_call(Client = #tclient{protocol = Proto0, |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 66 | service = Service, |
| 67 | seqid = SeqId}, |
David Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 68 | Function, |
| 69 | Args) -> |
| 70 | Params = Service:function_info(Function, params_type), |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 71 | 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 Reiss | c465799 | 2010-08-30 22:05:31 +0000 | [diff] [blame] | 80 | {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 Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 85 | end. |
David Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 86 | |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 87 | -spec receive_function_result(#tclient{}, atom()) -> {#tclient{}, {ok, term()} | {error, term()}}. |
| 88 | receive_function_result(Client = #tclient{service = Service}, Function) -> |
Bryan Duxbury | d3879f8 | 2010-08-19 05:06:02 +0000 | [diff] [blame] | 89 | ResultType = Service:function_info(Function, reply_type), |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 90 | read_result(Client, Function, ResultType). |
Bryan Duxbury | d3879f8 | 2010-08-19 05:06:02 +0000 | [diff] [blame] | 91 | |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 92 | read_result(Client, _Function, oneway_void) -> |
| 93 | {Client, {ok, ok}}; |
Bryan Duxbury | d3879f8 | 2010-08-19 05:06:02 +0000 | [diff] [blame] | 94 | |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 95 | read_result(Client = #tclient{protocol = Proto0, |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 96 | seqid = SeqId}, |
Bryan Duxbury | d3879f8 | 2010-08-19 05:06:02 +0000 | [diff] [blame] | 97 | Function, |
| 98 | ReplyType) -> |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 99 | {Proto1, MessageBegin} = thrift_protocol:read(Proto0, message_begin), |
| 100 | NewClient = Client#tclient{protocol = Proto1}, |
| 101 | case MessageBegin of |
Bryan Duxbury | d3879f8 | 2010-08-19 05:06:02 +0000 | [diff] [blame] | 102 | #protocol_message_begin{seqid = RetSeqId} when RetSeqId =/= SeqId -> |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 103 | {NewClient, {error, {bad_seq_id, SeqId}}}; |
Bryan Duxbury | d3879f8 | 2010-08-19 05:06:02 +0000 | [diff] [blame] | 104 | |
| 105 | #protocol_message_begin{type = ?tMessageType_EXCEPTION} -> |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 106 | handle_application_exception(NewClient); |
Bryan Duxbury | d3879f8 | 2010-08-19 05:06:02 +0000 | [diff] [blame] | 107 | |
| 108 | #protocol_message_begin{type = ?tMessageType_REPLY} -> |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 109 | handle_reply(NewClient, Function, ReplyType) |
Bryan Duxbury | d3879f8 | 2010-08-19 05:06:02 +0000 | [diff] [blame] | 110 | end. |
| 111 | |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 112 | |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 113 | handle_reply(Client = #tclient{protocol = Proto0, |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 114 | service = Service}, |
David Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 115 | Function, |
| 116 | ReplyType) -> |
| 117 | {struct, ExceptionFields} = Service:function_info(Function, exceptions), |
| 118 | ReplyStructDef = {struct, [{0, ReplyType}] ++ ExceptionFields}, |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 119 | {Proto1, {ok, Reply}} = thrift_protocol:read(Proto0, ReplyStructDef), |
| 120 | {Proto2, ok} = thrift_protocol:read(Proto1, message_end), |
| 121 | NewClient = Client#tclient{protocol = Proto2}, |
David Reiss | 2c53403 | 2008-06-11 00:58:00 +0000 | [diff] [blame] | 122 | ReplyList = tuple_to_list(Reply), |
| 123 | true = length(ReplyList) == length(ExceptionFields) + 1, |
| 124 | ExceptionVals = tl(ReplyList), |
| 125 | Thrown = [X || X <- ExceptionVals, |
| 126 | X =/= undefined], |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 127 | case Thrown of |
| 128 | [] when ReplyType == {struct, []} -> |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 129 | {NewClient, {ok, ok}}; |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 130 | [] -> |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 131 | {NewClient, {ok, hd(ReplyList)}}; |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 132 | [Exception] -> |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 133 | throw({NewClient, {exception, Exception}}) |
David Reiss | 3f660a4 | 2010-08-30 22:05:29 +0000 | [diff] [blame] | 134 | end. |
David Reiss | 6b3e40f | 2008-06-11 00:59:03 +0000 | [diff] [blame] | 135 | |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 136 | handle_application_exception(Client = #tclient{protocol = Proto0}) -> |
| 137 | {Proto1, {ok, Exception}} = |
| 138 | thrift_protocol:read(Proto0, ?TApplicationException_Structure), |
| 139 | {Proto2, ok} = thrift_protocol:read(Proto1, message_end), |
David Reiss | 55ff70f | 2008-06-11 00:58:25 +0000 | [diff] [blame] | 140 | XRecord = list_to_tuple( |
| 141 | ['TApplicationException' | tuple_to_list(Exception)]), |
David Reiss | 1af1868 | 2008-06-11 01:01:36 +0000 | [diff] [blame] | 142 | error_logger:error_msg("X: ~p~n", [XRecord]), |
David Reiss | 55ff70f | 2008-06-11 00:58:25 +0000 | [diff] [blame] | 143 | true = is_record(XRecord, 'TApplicationException'), |
David Reiss | 8286295 | 2010-08-30 22:05:32 +0000 | [diff] [blame^] | 144 | NewClient = Client#tclient{protocol = Proto2}, |
| 145 | throw({NewClient, {exception, XRecord}}). |