first commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5717c99
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+_*
+.eunit
+*.o
+*.beam
+*.plt
+*.swp
+*.swo
+.erlang.cookie
+ebin
+log
+erl_crash.dump
+.rebar
+logs
+_build
+.idea
+.rebar
+target
+*.iml
diff --git a/Developer.md b/Developer.md
new file mode 100644
index 0000000..7fb83d3
--- /dev/null
+++ b/Developer.md
@@ -0,0 +1,45 @@
+## 开发
+
+```
+ ./rebar3 eunit -m dubbo_zookeeper_tests
+
+ dubbo_zookeeper:register_consumer(<<"com.ifcoder.demo.facade.User">>,[]).
+
+ iProcessData:call_object().
+
+
+ scherdule:call_object().
+
+
+ ./rebar3 shell --apps "testdubboerl"
+ dubboerl:init().
+
+```
+### eunit Test
+ ./rebar3 eunit -m dubbo_zookeeper_tests
+ ./rebar3 eunit -m de_codec_tests
+
+## 服务注册流程
+
+### 消费者注册
+
+
+
+### 生产者注册
+
+
+
+### 消费者调用
+
+```
+ Option=[
+ sync,
+ {timeout,5000}
+ {attachments,[{<<"a">>,<<"b">>}]}
+ ]
+```
+
+
+
+
+## Develop Log
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..63002bc
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Dlive
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6099b6b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,67 @@
+dubboerl
+=====
+A Erlang framework for dubbo.
+
+Feature
+-----
+
+* 支持Zookeeper注册中心
+* 支持Hession serialize
+* 支持Consumer
+* 支持Provider
+* sync invoker
+* async invoker
+
+Start
+-----
+
+参考demo [dubboerl_demo](https://github.com/DLive/dubboerl_demo)
+
+#### Step1
+Transfer java facede t
+o erlang lib and add to you project dir.
+
+#### Step2
+
+configure dubbo refernce
+
+```
+{dubboerl,[
+ {zookeeper_list,[{"127.0.0.1",2181}]},
+ {application,<<"testdubboerl">>},
+ {consumer,[
+ {<<"me.dlive.dubboservice.service.IProcessData">>,[]}
+ ]},
+ {provider,[
+ {scherdule_impl,scherdule_behaviour,<<"me.dlive.dubboservice.service.Scherdule">>,[]}
+ ]}
+
+]}
+```
+#### Step4
+Init dubboerl application context
+
+ dubboerl:init().
+
+#### Step5
+Do your interface method invoker.
+
+```
+RequestPara = #testReq{name = <<"nameinfo">>,nick = <<"nickinfo">>,age = 10},
+iProcessData:queryinfo(Info,[]).
+```
+
+
+
+Build
+-----
+
+ $ rebar3 compile
+
+
+release
+
+ $ ./rebar3 as dubboerl release -n dubboerl
+
+
+
diff --git a/config/sys.config b/config/sys.config
new file mode 100644
index 0000000..8d62088
--- /dev/null
+++ b/config/sys.config
@@ -0,0 +1,34 @@
+[
+ %% SASL config {file, "log/sasl-error.log"}
+ {sasl, [
+ {sasl_error_logger, false},
+ {errlog_type, error},
+ {error_logger_mf_dir, "log/sasl-error.log"}, % Log directory
+ {error_logger_mf_maxbytes, 104857600}, % 100 MB max file size
+ {error_logger_mf_maxfiles, 5} % 5 files max
+ ]},
+ {emysql,[
+ {lock_timeout,900000},
+ {default_timeout, 900000}
+
+ ]},
+ {lager, [
+ {log_root, "./logs"},
+ {handlers, [
+ {lager_console_backend, debug},
+ {lager_file_backend, [{file, "error.log"}, {level, error}]},
+ {lager_file_backend, [{file, "console.log"}, {level, info}]}
+ ]}
+ ]},
+ {dubboerl,[
+ {zookeeper_list,[{"127.0.0.1",2181}]},
+ {application,<<"testdubboerl">>},
+ {consumer,[
+ {<<"com.ifcoder.demo.facade.User">>,[]}
+ ]},
+ {provider,[
+ {user_impl,user_behaviour,<<"com.ifcoder.demo.facade.User">>,[]}
+ ]}
+
+ ]}
+].
diff --git a/config/vm.args b/config/vm.args
new file mode 100644
index 0000000..7134fa4
--- /dev/null
+++ b/config/vm.args
@@ -0,0 +1,36 @@
+## Name of the node
+##需要定义 -name android_connecion_1@logic1.mpush.paf
+-name dubboerl@127.0.0.1
+
+## Cookie for distributed erlang
+-setcookie fb65338cf25a2d68a2da5207f718cdfa
+
+## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
+## (Disabled by default..use with caution!)
+##-heart
+
+## Enable kernel poll and a few async threads
++K true
+##+A 5
+
+## Increase number of concurrent ports/sockets
+-env ERL_MAX_PORTS 10240000
+
+## too many db tables
+-env ERL_MAX_ETS_TABLES 125535
+
++P 3000000
+
+## Tweak GC to run more often
+-env ERL_FULLSWEEP_AFTER 30000
+
+
+## +S 2
+
+## erlang 端口上退 默认65535
++Q 2000000
+
+-kernel inet_dist_listen_min 4370 inet_dist_listen_max 4388
+
+## 调度类型
+-sub true
\ No newline at end of file
diff --git a/include/common.hrl b/include/common.hrl
new file mode 100644
index 0000000..70768b7
--- /dev/null
+++ b/include/common.hrl
@@ -0,0 +1,15 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 14. 十月 2016 下午4:29
+%%%-------------------------------------------------------------------
+-author("dlive").
+
+-ifdef(DEBUG).
+-define(RELOADER,true).
+-else.
+-define(RELOADER,false).
+-endif.
\ No newline at end of file
diff --git a/include/dubbo.hrl b/include/dubbo.hrl
new file mode 100644
index 0000000..016a7a0
--- /dev/null
+++ b/include/dubbo.hrl
@@ -0,0 +1,98 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 16. 十月 2016 下午11:38
+%%%-------------------------------------------------------------------
+-author("dlive").
+
+-include("hessian.hrl").
+
+-define(DUBBO_VERSION,<<"2.6.0">>).
+-define(DUBBO_MEGIC, -9541). %% new version 16#dabb
+-define(DUBBO_MEGIC_HIGH, 16#da). %% new version 16#da
+-define(DUBBO_MEGIC_LOW, 16#bb). %% new version 16#bb
+
+%% 序列化类型
+-define(SERIALIZATION_HESSIAN,2).
+-define(SERIALIZATION_FASTJSON,6).
+-define(SERIALIZATION_KRYO,8).
+
+-define(RESPONSE_WITH_EXCEPTION,0).
+-define(RESPONSE_VALUE,1).
+-define(RESPONSE_NULL_VALUE,2).
+
+
+-define(RESPONSE_STATE_OK,20).
+
+-define(REQUEST_TIME_OUT,5000).
+
+-define(LINE_SEPERATOR,<<"\n"/utf8>>).
+
+-record(dubbo_request,{
+ serialize_type = 2 ::integer(),
+ is_event = true ::boolean(),
+ is_twoway = false ::boolean(),
+ data ::null|dubbo_rpc_invocation,
+ mid ::integer(),
+ mversion ::string(),
+ error_msg ::string(),
+ state ::byte(),
+ decode_state
+}).
+
+-record(dubbo_response,{
+ serialize_type = 2 ::integer(),
+ is_event = true ::boolean(),
+ is_twoway = false ::boolean(),
+ data ::null|dubbo_rpc_invocation,
+ mid ::integer(),
+ mversion ::string(),
+ error_msg ::string(),
+ state ::byte(),
+ decode_state
+}).
+
+-record(dubbo_rpc_invocation,{
+ serialVersionUID = -4355285085441097045,
+ className ::string(),
+ classVersion ::string(),
+ methodName ::string(),
+ parameterDesc ::string(),
+ parameterTypes=[] ::[#type_def{}],
+ parameters=[] ::[term()],
+ attachments=[] ::[term()]
+}).
+
+-record(consumer_config,{
+ interface,
+ application = <<"NoName">> ::binary(),
+ category = <<"consumers">> ::binary(),
+ check=false ::boolean(),
+ default_timeout=500 ::integer(),
+ dubbo_version= <<"2.5.3">> ::binary(),
+ methods=[] ::list(),
+ revision= <<"">> ::binary(),
+ side= <<"consumers">> ::binary()
+}).
+
+-record(provider_config,{
+ protocol,
+ host,
+ port,
+ interface,
+ anyhost=true,
+ executes=1,
+ application,
+ dubbo= <<"2.5.3">>,
+ methods=[],
+ side= <<"provider">>
+}).
+
+
+
+-record(interface_list,{interface,pid,connection_info}).
+-record(provider_node_list,{host_flag,connection_info}).
+-record(connection_info,{connection_id,pid,weight,host_flag}).
\ No newline at end of file
diff --git a/include/dubbo_type.hrl b/include/dubbo_type.hrl
new file mode 100644
index 0000000..f01d969
--- /dev/null
+++ b/include/dubbo_type.hrl
@@ -0,0 +1,12 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 15. Apr 2018 9:10 PM
+%%%-------------------------------------------------------------------
+-author("dlive").
+
+
+-type response_content() :: binary().
\ No newline at end of file
diff --git a/include/dubboerl.hrl b/include/dubboerl.hrl
new file mode 100644
index 0000000..baf4c25
--- /dev/null
+++ b/include/dubboerl.hrl
@@ -0,0 +1,16 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 22. Mar 2018 6:53 PM
+%%%-------------------------------------------------------------------
+-author("dlive").
+
+
+-define(PROVIDER_IMPL_TABLE,provider_impl_table).
+
+-define(PROVIDER_WORKER,provider_worker).
+
+-define(TRAFFIC_CONTROL,traffic_control).
\ No newline at end of file
diff --git a/include/hessian.hrl b/include/hessian.hrl
new file mode 100755
index 0000000..46ea79e
--- /dev/null
+++ b/include/hessian.hrl
@@ -0,0 +1,46 @@
+-ifndef(HESSIAN_H).
+-define(HESSIAN_H,1).
+
+-define(M, 2).
+-define(m, 0).
+
+-define(CHUNK_SIZE, 1024).
+
+-define(MegaSeconds, 1000000000).
+-define(Seconds, 1000).
+-define(MicroSeconds, 1000).
+-define(UnixEpoch, 62167219200).
+
+%% Equivalents: type_def and class
+-record(type_def,{defineNo=-1,native_type, foreign_type, fieldnames}).
+-record(class, {typeNo=-1, encoded=false, name=[], fields=[]}).
+
+-record(list, {refNo=-1, len=-1, type=untyped, values=[]}).
+-record(map, {refNo=-1, type=untyped, dict=dict:new()}).
+-record(object, {refNo=-1, typeRef=-1, class=auto, values=[]}).
+
+-record(set, {ref=-1, value=[]}).
+
+-ifdef(DEBUG).
+% Application Logging
+-define(LOG(Msg), io:format("{~p:~p}: ~p~n", [?MODULE, ?LINE, Msg])).
+-define(ERROR(Error,Reason), io:format("Error @ ~p:~p: ~p (Reason: ~p)~n", [?MODULE, ?LINE,Error,Reason])).
+% Protocol Logging
+-define(START(Msg,Value), io:format("~s -> ~p~n", [Msg,Value])).
+-define(METHOD(Value), io:format("\tMethod -> ~p~n", [ binary_to_list(Value) ] )).
+-define(VALUE(Value), io:format("\t\tValue -> ~p~n", [ Value ] )).
+-define(TYPEDEF(Value), io:format("\t\tTypeDef -> ~p~n", [ Value ] )).
+-define(INSTANCE(Value), io:format("\t\tInstance -> ~p~n", [ Value ] )).
+-else.
+% Application Logging
+-define(LOG(Msg), true).
+-define(ERROR(Msg), true).
+% Protocol Logging
+-define(START(Msg,Value), true).
+-define(METHOD(Value), true).
+-define(VALUE(Value), true).
+-define(TYPEDEF(Value), true).
+-define(INSTANCE(Value), true).
+-endif.
+
+-endif.
\ No newline at end of file
diff --git a/include/hessian_test.hrl b/include/hessian_test.hrl
new file mode 100644
index 0000000..ed6b8cb
--- /dev/null
+++ b/include/hessian_test.hrl
@@ -0,0 +1,34 @@
+% ---------------------------------------------------------------------------
+% Copyright (C) 2008 0x6e6562
+%
+% Licensed under the Apache License, Version 2.0 (the "License");
+% you may not use this file except in compliance with the License.
+% You may obtain a copy of the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS,
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+% See the License for the specific language governing permissions and
+% limitations under the License.
+% ---------------------------------------------------------------------------
+
+% This is for simple tests
+-record(pair, {first, second}).
+
+% These are used in the kitchen sink tests
+-record(kitchen_sink, {color,
+ plug_hole,
+ delivery_date,
+ cost,
+ type,
+ deluxe}).
+-record(plug_hole, {diameter}).
+-record(specification, {customer_name,
+ plug_hole,
+ color,
+ total,
+ type,
+ order_date,
+ deluxe}).
\ No newline at end of file
diff --git a/include/java_type.hrl b/include/java_type.hrl
new file mode 100644
index 0000000..e85e3a7
--- /dev/null
+++ b/include/java_type.hrl
@@ -0,0 +1,13 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 09. Mar 2018 11:10 PM
+%%%-------------------------------------------------------------------
+-author("dlive").
+
+-record(null_pointer_exception,{detailMessage,cause,stackTrace,suppressedExceptions}).
+
+-record(stack_stack_trace_element,{declaringClass,methodName,fileName,lineNumber}).
\ No newline at end of file
diff --git a/rebar.config b/rebar.config
new file mode 100644
index 0000000..81b3d5f
--- /dev/null
+++ b/rebar.config
@@ -0,0 +1,19 @@
+{erl_opts, [debug_info,
+ {parse_transform, lager_transform}
+ ]}.
+
+{lib_dirs, []}.
+{base_dir, "_build"}.
+{root_dir, "."}.
+{project_app_dirs, ["."]}.
+{extra_src_dirs, ["test"]}.
+{ct_opts, []}.
+
+
+{deps, [
+ {erlzk, ".*", {git, "git@github.com:huaban/erlzk.git", {tag, "v0.6.2"}}},
+ {lager, ".*", {git, "git@github.com:basho/lager.git", {tag, "3.2.4"}}},
+ {ranch, ".*", {git, "https://github.com/ninenines/ranch.git", {tag, "1.4.0"}}},
+ {poolboy, ".*", {git, "https://github.com/devinus/poolboy.git", {tag, "1.5.1"}}},
+ {jiffy, "0.15.1"}
+]}.
\ No newline at end of file
diff --git a/rebar3 b/rebar3
new file mode 100755
index 0000000..9463ef6
--- /dev/null
+++ b/rebar3
Binary files differ
diff --git a/src/de_codec.erl b/src/de_codec.erl
new file mode 100644
index 0000000..8cc4136
--- /dev/null
+++ b/src/de_codec.erl
@@ -0,0 +1,284 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 20. 十月 2016 下午6:21
+%%%-------------------------------------------------------------------
+-module(de_codec).
+-author("dlive").
+
+-include("dubbo.hrl").
+-include("hessian.hrl").
+
+-ifdef(TEST).
+-compile(export_all).
+-endif.
+%% API
+-export([encode_request/1,encode_response/1]).
+
+-export([decode_header/1]).
+-export([decode_response/2]).
+-export([decode_request/2]).
+
+
+
+-spec encode_request(#dubbo_request{})->{ok,binary()} | {error,term()}.
+encode_request(Request)->
+%% lager:debug("encode request ~p",[Request]),
+ {ok,RequestData} = encode_request_data(Request#dubbo_request.serialize_type,Request),
+ Size = byte_size(RequestData),
+ Header = encode_header(Request,Size,0),
+ RequestContent = <<Header/binary,RequestData/binary>>,
+ {ok,RequestContent}.
+
+encode_header(Request,DataLen,RequestState)->
+ Header2=-128 bor Request#dubbo_request.serialize_type,
+ Header21=case Request#dubbo_request.is_twoway of
+ true -> Header2 bor 64;
+ false-> Header2
+ end,
+ Header22=case Request#dubbo_request.is_event of
+ true -> Header21 bor 32;
+ false-> Header21
+ end,
+ RequestId = Request#dubbo_request.mid,
+ Header = << ?DUBBO_MEGIC:16,Header22:8,RequestState:8,RequestId:64,DataLen:32>>,
+ Header.
+encode_request_data(?SERIALIZATION_FASTJSON,Request)->
+ dubbo_serializa_fastjson:encode_request_data(Request);
+
+encode_request_data(?SERIALIZATION_HESSIAN,Request)->
+ State=type_encoding:init(),
+ DataType =case Request#dubbo_request.is_event of
+ true->
+ dubbo_event;
+ false->
+ case Request#dubbo_request.data of
+ #dubbo_rpc_invocation{} ->
+ dubbo_rpc_invocation;
+ _ ->
+ unknow
+ end
+ end,
+ {ok,Bin} = encode_request_data(DataType,Request,Request#dubbo_request.data,State),
+ {ok,Bin}.
+
+encode_request_data(dubbo_event,Request,Data,State) ->
+ Bin = hessianEncode:encode(Data,State),
+ {ok,Bin};
+encode_request_data(dubbo_rpc_invocation,Request,Data,State) ->
+ METHOD_NAME = Data#dubbo_rpc_invocation.methodName,
+ METHOD_ARGS_TYPES = Data#dubbo_rpc_invocation.parameterDesc,
+ RequestList = [
+ hessianEncode:encode(?DUBBO_VERSION, State), %% dubbo version
+ hessianEncode:encode(Data#dubbo_rpc_invocation.className, State),
+ hessianEncode:encode(Data#dubbo_rpc_invocation.classVersion, State),
+ hessianEncode:encode(METHOD_NAME, State),
+ hessianEncode:encode(METHOD_ARGS_TYPES, State)
+ ],
+ {ArgsBin,State2} = encode_arguments(Data,State),
+ AttachDict = dict:from_list(Data#dubbo_rpc_invocation.attachments),
+ AttachMaps = #map{dict = AttachDict },
+ {AttachBinay,_} = hessianEncode:encode(AttachMaps, State2),
+ RequestData = erlang:iolist_to_binary(RequestList ++ [ArgsBin,AttachBinay]),
+ {ok,RequestData}.
+
+-spec encode_response(#dubbo_response{})-> {ok,term()}.
+encode_response(Response)->
+ {ok,ResponseData} = encode_response_data(Response),
+ Size = byte_size(ResponseData),
+ Header = encode_response_header(Response,Size,?RESPONSE_STATE_OK),
+ ResponseContent = <<Header/binary,ResponseData/binary>>,
+ {ok, ResponseContent}.
+
+encode_response_data(Response)->
+ State=type_encoding:init(),
+ DataType =case Response#dubbo_response.is_event of
+ true->
+ dubbo_event;
+ false->
+ case Response#dubbo_response.data of
+ #dubbo_rpc_invocation{} ->
+ dubbo_rpc_invocation;
+ _ ->
+ unknow
+ end
+ end,
+ {ok,Bin} = encode_response_data(DataType,Response,Response#dubbo_response.data,State),
+ {ok,Bin}.
+encode_response_data(dubbo_event,Response,Data,State) ->
+ Bin = hessianEncode:encode(Data,State),
+ {ok,Bin};
+encode_response_data(dubbo_rpc_invocation,Response,Data,State) ->
+ Result = case Data of
+ null ->
+ [
+ hessianEncode:encode(?RESPONSE_NULL_VALUE, State)
+ ];
+ _ ->
+ {ArgsBin,_State2} = encode_arguments(Data,State),
+ [
+ hessianEncode:encode(?RESPONSE_VALUE, State),
+ ArgsBin
+ ]
+ end,
+ ResponseData = erlang:iolist_to_binary(Result),
+ {ok,ResponseData}.
+
+encode_response_header(Response,DataLen, ResponseState)->
+ Header2= Response#dubbo_response.serialize_type,
+ Header21=case Response#dubbo_response.is_twoway of
+ true -> Header2 bor 64;
+ false-> Header2
+ end,
+ Header22=case Response#dubbo_response.is_event of
+ true -> Header21 bor 32;
+ false-> Header21
+ end,
+ RequestId = Response#dubbo_response.mid,
+ Header = << ?DUBBO_MEGIC:16,Header22:8, ResponseState:8,RequestId:64,DataLen:32>>,
+ Header.
+encode_arguments(Data,State)->
+ {StateNew} = lists:foldl(fun(X,{StateTmp})->
+ StateTmpNew = type_encoding:enlist(X,StateTmp),
+ {StateTmpNew} end,
+ {State},Data#dubbo_rpc_invocation.parameterTypes),
+ {Bin,State2} = lists:foldl(fun(X,{BinTmp,StateTmp2})->
+ case hessianEncode:encode(X, StateTmp2) of
+ {ArgsBin,StateTmpNew} ->
+ {<<BinTmp/binary,ArgsBin/binary>>, StateTmpNew};
+ ArgsBin2 ->
+ {<<BinTmp/binary,ArgsBin2/binary>>, StateTmp2}
+ end end,
+ {<<>>,StateNew},Data#dubbo_rpc_invocation.parameters),
+ {Bin,State2}.
+
+-spec decode_header(binary())-> {State::ok|error,Type::request|response,Data::#dubbo_response{}|#dubbo_request{}}.
+decode_header(Header)->
+ <<?DUBBO_MEGIC_HIGH,?DUBBO_MEGIC_LOW,Flag:8,State:8,Mid:64,DataLen:32>> = Header,
+ if
+ (Flag band 16#80) == 0 ->
+ {DecodeState,Res} = decode_header(response,Flag,State,Mid,DataLen),
+ {DecodeState,response,Res};
+ true ->
+ {DecodeState,Req} = decode_header(request,Flag,State,Mid,DataLen),
+ {DecodeState,request,Req}
+ end.
+decode_header(request,Flag,State,Mid,DataLen)->
+ SerializeType = Flag band 16#1f,
+ IsTwoWay = if
+ (Flag band 16#40) /=0 -> true;
+ true -> false
+ end,
+ IsEvent = if
+ (Flag band 16#20) /=0 -> true;
+ true -> false
+ end,
+ Req = #dubbo_request{
+ is_event = IsEvent,
+ is_twoway = IsTwoWay,
+ mid = Mid,
+ mversion = <<"2.0.0">>,
+ serialize_type = SerializeType
+ },
+ {ok,Req};
+decode_header(response,Flag,State,Mid,DataLen)->
+ SerializeType = Flag band 16#1f,
+ IsEvent = if
+ (Flag band 16#20) /= 0 -> true;
+ true -> false
+ end,
+ Res = #dubbo_response{is_event = IsEvent,
+ serialize_type = SerializeType,
+ state = State,
+ mid = Mid
+ },
+ {ok,Res}.
+
+-spec decode_response(#dubbo_response{},binary())-> {ok,#dubbo_response{}}.
+decode_response(Res,Data)->
+ case Res#dubbo_response.serialize_type of
+ ?SERIALIZATION_FASTJSON ->
+ dubbo_serializa_fastjson:decode_response(Res,Data);
+ ?SERIALIZATION_HESSIAN ->
+ dubbo_serializa_hession:decode_response(Res,Data)
+ end.
+
+%%decode_response(?SERIALIZATION_FASTJSON,dubbo_rpc_invocation,Res,Data)->
+%% dubbo_serializa_fastjson:decode_response(dubbo_rpc_invocation,Res,Data);
+%%
+%%decode_response(?SERIALIZATION_HESSIAN,dubbo_rpc_invocation,Res,Data)->
+%% {Rest,Type,State} = hessianDecode2:decode(Data,hessianDecode2:init()),
+%% case Type of
+%% 1 ->
+%% {_,Object,DecodeState} = hessianDecode2:decode(Rest,State),
+%% {ok,Res#dubbo_response{data = Object,decode_state = DecodeState}};
+%% 2 ->
+%% {ok,Res#dubbo_response{data = null,decode_state = State}};
+%% _->
+%% lager:warning("decode unkonw type ~p ~p",[Type,Rest]),
+%% {Rest2,Object2,DecodeState2} = hessianDecode2:decode(Rest,State),
+%% lager:warning("decode unkonw type2 ~p ~p",[Object2,Rest2]),
+%% {ok,Res#dubbo_response{data = Object2,decode_state = DecodeState2}}
+%% end;
+%%decode_response(?SERIALIZATION_HESSIAN,dubbo_event,Res,Data)->
+%% {_Rest,undefined,_NewState} = hessianDecode2:decode(Data,hessianDecode2:init()),
+%% {ok,Res#dubbo_response{data = undefined}}.
+
+-spec decode_request(#dubbo_request{},binary())-> {ok,#dubbo_request{}}.
+decode_request(Req,Data)->
+ case Req#dubbo_request.serialize_type of
+ ?SERIALIZATION_FASTJSON ->
+ dubbo_serializa_fastjson:decode_request(Req,Data);
+ ?SERIALIZATION_HESSIAN ->
+ dubbo_serializa_hession:decode_request(Req,Data)
+ end.
+
+%%decode_request(dubbo_rpc_invocation,Req,Data)->
+%% {ResultList,NewState,RestData} = decode_request_body(Data,hessianDecode2:init(),[dubbo,path,version,method_name,desc_and_args,attachments]),
+%% [DubboVersion,Path,Version,MethodName,Desc,ArgsObj,Attachments]=ResultList,
+%% RpcData = #dubbo_rpc_invocation{className = Path,classVersion = Version,methodName = MethodName,parameterDesc = Data,parameters = ArgsObj,attachments = Attachments},
+%% Req2 = Req#dubbo_request{data = RpcData},
+%% {ok,Req2};
+%%
+%%decode_request(dubbo_event,Req,Data)->
+%% {_Rest,undefined,_NewState} = hessianDecode2:decode(Data,hessianDecode2:init()),
+%% {ok,Req#dubbo_request{data = undefined}}.
+%%
+%%decode_request_body(Data,State,List)->
+%% {ResultList,NewState,RestData} = decode_request_body(List,Data,State,[]),
+%% {lists:reverse(ResultList),NewState,RestData}.
+%%decode_request_body([ParseType|List],Data,State,ResultList)
+%% when ParseType==dubbo;ParseType==path;ParseType==version;ParseType==method_name ->
+%% {Rest,Result,NewState } = hessianDecode2:decode(Data,State),
+%% decode_request_body(List,Rest,NewState, [Result] ++ ResultList);
+%%decode_request_body([desc_and_args| List],Data,State,ResultList)->
+%% {Rest,ParameterDesc,State1 } = hessianDecode2:decode(Data,State),
+%% if
+%% size(ParameterDesc) == 0 ->
+%% decode_request_body(List,Rest,State1, [ [],[] ]++ ResultList);
+%% true ->
+%% ParameterDescArray = binary:split(ParameterDesc,<<";">>),
+%% {ArgsObjList,NewState,RestData} = decode_request_body_args(ParameterDescArray,Rest,State1,[]),
+%% decode_request_body(List,RestData,NewState, [ArgsObjList,ParameterDesc]++ ResultList)
+%% end;
+%%decode_request_body([attachments|List],Data,State,ResultList)->
+%% {Rest,Attachments,State1 } = hessianDecode2:decode(Data,State),
+%% AttachmentsList = dict:to_list(Attachments#map.dict),
+%% decode_request_body(List,Rest,State1,[AttachmentsList] ++ ResultList);
+%%decode_request_body([_Type1|List],Data,State,ResultList)->
+%% lager:warning("decode_request_body unknow type"),
+%% decode_request_body(List,Data,State, ResultList);
+%%decode_request_body([],Data,State,ResultList)->
+%% {ResultList,State,Data}.
+%%
+%%decode_request_body_args([],Data,State,ArgsObjList)->
+%% {ArgsObjList,State,Data};
+%%decode_request_body_args([ArgsType|RestList],Data,State,ArgsObjList) when ArgsType== <<>> ->
+%% decode_request_body_args(RestList,Data,State,ArgsObjList);
+%%decode_request_body_args([_ArgsType|RestList],Data,State,ArgsObjList) ->
+%% {Rest,ArgObj,NewState } = hessianDecode2:decode(Data,State),
+%% ArgObj2 = de_type_transfer:classobj_to_native(ArgObj,NewState),
+%% decode_request_body_args(RestList,Rest,NewState,ArgsObjList++[ArgObj2]).
\ No newline at end of file
diff --git a/src/de_common_fun.erl b/src/de_common_fun.erl
new file mode 100644
index 0000000..31eb143
--- /dev/null
+++ b/src/de_common_fun.erl
@@ -0,0 +1,24 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 27. Dec 2017 3:01 PM
+%%%-------------------------------------------------------------------
+-module(de_common_fun).
+-author("dlive").
+
+%% API
+-export([local_ip_v4/0,local_ip_v4_str/0]).
+
+local_ip_v4() ->
+ {ok, Addrs} = inet:getifaddrs(),
+ hd([
+ Addr || {_, Opts} <- Addrs, {addr, Addr} <- Opts,
+ size(Addr) == 4, Addr =/= {127,0,0,1}
+ ]).
+
+local_ip_v4_str()->
+ {V1,V2,V3,V4} =local_ip_v4(),
+ list_to_binary(io_lib:format("~p.~p.~p.~p",[V1,V2,V3,V4])).
diff --git a/src/de_heartbeat.erl b/src/de_heartbeat.erl
new file mode 100644
index 0000000..b6da7cb
--- /dev/null
+++ b/src/de_heartbeat.erl
@@ -0,0 +1,21 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 20. 十月 2016 下午5:55
+%%%-------------------------------------------------------------------
+-module(de_heartbeat).
+-author("dlive").
+
+-include("dubbo.hrl").
+%% API
+-export([generate_request/2]).
+generate_request(undefined,NeedResponse)->
+ RequestId = de_id_count:gen_id(),
+ generate_request(RequestId,NeedResponse);
+generate_request(RequestId,NeedResponse)->
+ Req = #dubbo_request{is_event = true,is_twoway = true,mid = RequestId,data = undefined,mversion= <<"2.0.0">>},
+ {ok,Bin} = de_codec:encode_request(Req),
+ {ok,Bin}.
\ No newline at end of file
diff --git a/src/de_id_count.erl b/src/de_id_count.erl
new file mode 100644
index 0000000..be07d1b
--- /dev/null
+++ b/src/de_id_count.erl
@@ -0,0 +1,177 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 17. 十月 2016 上午11:12
+%%%-------------------------------------------------------------------
+-module(de_id_count).
+-author("dlive").
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+-export([gen_id/0,get_index/2]).
+
+-define(SERVER, ?MODULE).
+-define(INDEX_ETS_TABLE,de_id_count_table).
+-define(MAX_NUM,9223372036854775807).
+-record(state, {}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(start_link() ->
+ {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+-spec(init(Args :: term()) ->
+ {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term()} | ignore).
+init([]) ->
+ init_table(),
+ {ok, #state{}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
+ State :: #state{}) ->
+ {reply, Reply :: term(), NewState :: #state{}} |
+ {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_cast(Request :: term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
+ State :: #state{}) -> term()).
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
+ Extra :: term()) ->
+ {ok, NewState :: #state{}} | {error, Reason :: term()}).
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+gen_id()->
+ try ets:update_counter(?INDEX_ETS_TABLE,cur_index,{2,1,?MAX_NUM,1}) of
+ NewCount ->
+ NewCount
+ catch
+ _Type:_Reason ->
+ (catch ets:insert(?INDEX_ETS_TABLE,{cur_index,1})),
+ ets:update_counter(?INDEX_ETS_TABLE,cur_index,{2,1,?MAX_NUM,1})
+ end.
+
+get_index(Type,Max)->
+ try ets:update_counter(?INDEX_ETS_TABLE,Type,{2,1,Max,1}) of
+ NewCount ->
+ NewCount
+ catch
+ _Type:_Reason ->
+ (catch ets:insert(?INDEX_ETS_TABLE,{Type,1})),
+ ets:update_counter(?INDEX_ETS_TABLE,Type,{2,1,Max,1})
+ end.
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+init_table()->
+ try ets:new(de_id_count_table, [public,named_table]) of
+ de_id_count_table ->
+ ets:insert(?INDEX_ETS_TABLE,{cur_index,1}),
+ ok
+ catch
+ _Type:Reason ->
+ io:format("error init auto inc table reason:~p~n",[Reason])
+ end.
\ No newline at end of file
diff --git a/src/de_network_tools.erl b/src/de_network_tools.erl
new file mode 100644
index 0000000..7527885
--- /dev/null
+++ b/src/de_network_tools.erl
@@ -0,0 +1,28 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 21. Mar 2018 4:07 PM
+%%%-------------------------------------------------------------------
+-module(de_network_tools).
+-author("dlive").
+
+%% API
+-export([local_ip_v4/0,local_ipv4/0,local_ipv4_binary/0]).
+
+
+local_ip_v4() ->
+ {ok, Addrs} = inet:getifaddrs(),
+ hd([
+ Addr || {_, Opts} <- Addrs, {addr, Addr} <- Opts,
+ size(Addr) == 4, Addr =/= {127,0,0,1}
+ ]).
+
+local_ipv4_binary()->
+ {I1,I2,I3,I4}=local_ip_v4(),
+ list_to_binary(io_lib:format("~p.~p.~p.~p",[I1,I2,I3,I4])).
+local_ipv4()->
+ {I1,I2,I3,I4}=local_ip_v4(),
+ lists:flatten(io_lib:format("~p.~p.~p.~p",[I1,I2,I3,I4])).
diff --git a/src/de_type_transfer.erl b/src/de_type_transfer.erl
new file mode 100644
index 0000000..5d89dcc
--- /dev/null
+++ b/src/de_type_transfer.erl
@@ -0,0 +1,104 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 27. 十月 2016 下午8:28
+%%%-------------------------------------------------------------------
+-module(de_type_transfer).
+-author("dlive").
+
+-include("hessian.hrl").
+-include("dubbo.hrl").
+
+%% API
+-export([java_to_native/2,pre_process_typedef/3,response_to_native/1,classobj_to_native/2]).
+
+
+response_to_native(Response)->
+ java_to_native(Response#dubbo_response.data,Response#dubbo_response.decode_state).
+
+
+classobj_to_native(Data,DecodeState)->
+ java_to_native(Data,DecodeState).
+
+java_to_native(#object{values = ForeignData}=Data,State)->
+ ForeignDataNew = [java_to_native(ValueItem,State) || ValueItem <-ForeignData ],
+
+ case hessianDecode2:get_deftype(Data#object.typeRef,State) of
+ #type_def{fieldnames = ObjectFields,foreign_type = ForeignType } ->
+ case get_deftype(ForeignType) of
+ false->
+ error;
+ #type_def{fieldnames = NativeFields,native_type = NativeTupeName}->
+ AsDict = dict:from_list(lists:zip(ObjectFields,ForeignDataNew)),
+ NativeData = [dict:fetch(atom_to_binary(Key,utf8),AsDict) || Key <- NativeFields],
+ list_to_tuple( [NativeTupeName] ++ NativeData)
+ end;
+ Info ->
+ lager:warning("java_to_native error:~p",[Info]),
+ error
+ end;
+java_to_native(#list{values = ForeignData}=Data,State)->
+ ForeignDataNew = [java_to_native(ValueItem,State) || ValueItem <-ForeignData ],
+ ForeignDataNew;
+%% case hessianDecode2:get_deftype(Data#list.refNo,State) of
+%% #type_def{fieldnames = ObjectFields,foreign_type = ForeignType } ->
+%% case get_deftype(ForeignType) of
+%% false->
+%% error;
+%% #type_def{fieldnames = NativeFields,native_type = NativeTupeName}->
+%% lager:debug("test ForeignType ~p NativeTupeName ~p",[ForeignType,NativeTupeName]),
+%%%% AsDict = dict:from_list(lists:zip(ObjectFields,ForeignDataNew)),
+%%%% NativeData = [dict:fetch(atom_to_binary(Key,utf8),AsDict) || Key <- NativeFields],
+%%%% list_to_tuple( [NativeTupeName] ++ NativeData)
+%% ForeignDataNew
+%% end;
+%% Info ->
+%% lager:warning("java_to_native list error:~p",[Info]),
+%% error
+%% end;
+java_to_native(Data,_)->
+ lager:debug("java_to_native unkonw type ~p",[Data]),
+ Data.
+
+%%get_deftype([Item |DefTypeList],ForeignType)->
+%% if
+%% Item#type_def.foreign_type == ForeignType -> Item;
+%% true ->
+%% get_deftype(DefTypeList,ForeignType)
+%% end;
+%%get_deftype([],_ForeignType)->
+%% false.
+
+get_deftype(ForeignType)->
+
+ case type_register:lookup_foreign_type(ForeignType) of
+ undefined->
+ lager:debug("get deftype undefined ~p",[ForeignType]),
+ false;
+ #type_def{}=TypeDef->
+ lager:debug("get deftype success ~p",[ForeignType]),
+ TypeDef;
+ _->
+ lager:debug("get deftype undefined ~p",[ForeignType]),
+ false
+ end.
+
+pre_process_typedef(NativeType,ForeignType,FieldsNames)->
+ Type = #type_def{native_type = NativeType,foreign_type = ForeignType,fieldnames = FieldsNames},
+%% Type2=type_decoding:hash_store(Type),
+ type_register:regiest_foreign_native(Type),
+ lager:debug("pre_process_typedef ~p,~p",[NativeType,ForeignType]).
+%% case type_decoding:resolve_native_type(ForeignType) of
+%% undefined ->
+%%%% Type = #type_def{native_type = NativeType, foreign_type = ForeignType, fieldnames = record_info(fields,NativeType)},
+%% Type = #type_def{native_type = NativeType,foreign_type = ForeignType,fieldnames = FieldsNames},
+%% Type2=type_decoding:hash_store(Type),
+%%
+%% lager:debug("pre_process_typedef ~p,~p",[NativeType,ForeignType]);
+%% type_decoding:store_typepool(Type2);
+%% _->
+%% ok
+%% end.
\ No newline at end of file
diff --git a/src/dubbo_agent.erl b/src/dubbo_agent.erl
new file mode 100644
index 0000000..15e557a
--- /dev/null
+++ b/src/dubbo_agent.erl
@@ -0,0 +1,20 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 11. May 2018 12:15 AM
+%%%-------------------------------------------------------------------
+-module(dubbo_agent).
+-author("dlive").
+
+%% API
+-export([register_consumer/1]).
+
+register_consumer(Interface)->
+ ProviderList=[
+ "dubbo%3a%2f%2f127.0.0.1%3a20880%2fcom.alibaba.dubbo.performance.demo.provider.IHelloService%3fanyhost%3dtrue%26application%3dmesh-provider%26dubbo%3d2.6%26executes%3d30%26interface%3dcom.alibaba.dubbo.performance.demo.provider.IHelloService%26methods%3dhash%26pid%3d26029%26side%3dprovider%26timestamp%3d1514615143388"
+ ],
+ dubbo_consumer_pool:start_consumer(Interface,ProviderList),
+ ok.
diff --git a/src/dubbo_common.erl b/src/dubbo_common.erl
new file mode 100644
index 0000000..11abb42
--- /dev/null
+++ b/src/dubbo_common.erl
@@ -0,0 +1,13 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 17. 十月 2016 上午12:23
+%%%-------------------------------------------------------------------
+-module(dubbo_common).
+-author("dlive").
+
+%% API
+-export([]).
diff --git a/src/dubbo_config_util.erl b/src/dubbo_config_util.erl
new file mode 100644
index 0000000..c27f694
--- /dev/null
+++ b/src/dubbo_config_util.erl
@@ -0,0 +1,43 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 05. Jan 2018 12:21 AM
+%%%-------------------------------------------------------------------
+-module(dubbo_config_util).
+-author("dlive").
+
+-include("dubbo.hrl").
+%% API
+-export([gen_consumer/3,gen_provider/5]).
+
+
+gen_consumer(Application,Interface,Option)->
+ #consumer_config{
+ interface = Interface,
+ application = Application,
+ category = <<"consumers">> ,
+ check=false,
+ default_timeout = proplists:get_value(default_timeout,Option,500),
+ dubbo_version= proplists:get_value(dubbo_version,Option,?DUBBO_VERSION),
+ methods=[],
+ revision= <<"">>,
+ side= <<"consumers">>
+ }.
+
+gen_provider(Application,Port,Interface,MethodList,Option)->
+ Host = de_network_tools:local_ipv4_binary(),
+ MethodList2= [atom_to_binary(Item,utf8) || Item <- MethodList ],
+ #provider_config{
+ protocol= <<"dubbo">>,
+ host= Host,
+ port = Port,
+ interface=Interface,
+ anyhost=true,
+ executes=10,
+ application=Application,
+ methods = MethodList2,
+ side= <<"provider">>
+ }.
\ No newline at end of file
diff --git a/src/dubbo_consumer_pool.erl b/src/dubbo_consumer_pool.erl
new file mode 100644
index 0000000..70e4538
--- /dev/null
+++ b/src/dubbo_consumer_pool.erl
@@ -0,0 +1,287 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 28. Dec 2017 7:34 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_consumer_pool).
+-author("dlive").
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0, start_consumer/2]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+-export([select_connection/1,select_connection/2]).
+
+-include("dubbo.hrl").
+-define(SERVER, ?MODULE).
+
+-define(INTERFCE_LIST_TABLE,interface_list).
+-define(PROVIDER_NODE_LIST_TABLE,provider_node_list).
+
+-record(state, {}).
+
+
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(start_link() ->
+ {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+-spec(init(Args :: term()) ->
+ {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term()} | ignore).
+init([]) ->
+ init_ets_table(),
+ {ok, #state{}}.
+init_ets_table()->
+ try ets:new(?INTERFCE_LIST_TABLE, [bag,public,named_table,{keypos,2}]) of
+ ?INTERFCE_LIST_TABLE ->
+ ok
+ catch
+ _Type:Reason ->
+ lager:error("new ets table error ~p",[Reason]),
+ error
+ end,
+ try ets:new(?PROVIDER_NODE_LIST_TABLE, [bag,public,named_table,{keypos,2}]) of
+ ?PROVIDER_NODE_LIST_TABLE ->
+ ok
+ catch
+ _Type1:Reason1 ->
+ lager:error("new ets table error ~p",[Reason1]),
+ error
+ end,
+ ok.
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
+ State :: #state{}) ->
+ {reply, Reply :: term(), NewState :: #state{}} |
+ {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+
+handle_call({add_consumer,Interface,ProviderNodeList}, _From, State) ->
+
+ OldProviderList = get_interface_provider_node(Interface),
+ NewProviderList = add_consumer(ProviderNodeList,[]),
+ DeleteProverList = OldProviderList -- NewProviderList,
+ clean_invalid_provider(DeleteProverList),
+ {reply, ok, State};
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_cast(Request :: term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_se rver when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
+ State :: #state{}) -> term()).
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
+ Extra :: term()) ->
+ {ok, NewState :: #state{}} | {error, Reason :: term()}).
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+start_consumer(Interface,ProviderNodeInfo)->
+ gen_server:call(?SERVER,{add_consumer,Interface,ProviderNodeInfo}).
+
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+add_consumer([],RegisterList)->
+ RegisterList;
+add_consumer([ProviderNodeInfo|ProviderList],RegisterList)->
+ case dubbo_node_config_util:parse_provider_info(ProviderNodeInfo) of
+ {ok,ProviderConfig} ->
+ HostFlag=get_host_flag(ProviderConfig),
+ case ets:lookup(?PROVIDER_NODE_LIST_TABLE,HostFlag) of
+ []->
+ ConnectionList = start_provider_process(HostFlag,30,ProviderConfig),
+ ok = update_connection_info(ProviderConfig#provider_config.interface,HostFlag,ConnectionList,true),
+ ok;
+ List ->
+ List2 = lists:map(fun(#provider_node_list{connection_info = ConnectionItem })->
+ ConnectionItem
+ end,List),
+ ok = update_connection_info(ProviderConfig#provider_config.interface,HostFlag,List2,false),
+ ok
+ end,
+ add_consumer(ProviderList,[HostFlag]++RegisterList);
+ {error,R1} ->
+ lager:error("parse provider info error reason ~p",[R1]),
+ add_consumer(ProviderList,RegisterList)
+ end.
+
+start_provider_process(HostFlag,Weight,ProviderConfig) ->
+ ExecutesList=lists:seq(1,ProviderConfig#provider_config.executes),
+ ConnectionList= lists:map(fun(Item) ->
+ ConnectionFlag= << HostFlag/binary,(integer_to_binary(Item))/binary >>,
+ ConnectionFlagTerm= binary_to_atom(ConnectionFlag,utf8),
+ AChild = {ConnectionFlagTerm,{dubbo_netty_client, start_link, [ConnectionFlagTerm,HostFlag,ProviderConfig,Item]}, permanent, 2000, worker, [dubbo_netty_client]},
+ {ok,Pid} = dubbo_consumer_pool_sup:add_children(AChild),
+ lager:info("start provider ~p pid info ~p~n",[HostFlag,Pid]),
+ #connection_info{connection_id = ConnectionFlagTerm,pid = Pid,weight = Weight,host_flag = HostFlag}
+ end,ExecutesList),
+ ConnectionList.
+get_host_flag(ProviderConfig)->
+ HostFlag= << (list_to_binary(ProviderConfig#provider_config.host))/binary,<<"_">>/binary,(integer_to_binary(ProviderConfig#provider_config.port))/binary>>,
+ HostFlag.
+
+update_connection_info(Interface,HostFlag,ConnectionList,IsUpdateProvideNode)->
+ lists:map(fun(Item) ->
+ I1 = ets:insert(?INTERFCE_LIST_TABLE,#interface_list{interface = Interface,connection_info = Item}),
+ lager:debug("save INTERFCE_LIST_TABLE ~p info:~p",[Interface,I1]),
+ case IsUpdateProvideNode of
+ true->
+ I2 = ets:insert(?PROVIDER_NODE_LIST_TABLE,#provider_node_list{host_flag = HostFlag,connection_info = Item }),
+ lager:debug("save PROVIDER_NODE_LIST_TABLE ~p info:~p",[HostFlag,I2]);
+ false->
+ ok
+ end,
+ ok
+ end,ConnectionList),
+ ok.
+
+get_interface_provider_node(Interface)->
+ case ets:lookup(?INTERFCE_LIST_TABLE,Interface) of
+ []->
+ [];
+ List->
+ ListRet = [Item#interface_list.connection_info#connection_info.host_flag || Item <- List],
+ lists_util:del_duplicate(ListRet)
+ end.
+
+select_connection(Interface)->
+ RandNum = rand:uniform(2048),
+ select_connection(Interface,RandNum).
+select_connection(Interface,RandNum)->
+ case ets:lookup(?INTERFCE_LIST_TABLE,Interface) of
+ []->
+ {error,none};
+ List->
+ Len = length(List),
+ RemNum = (RandNum rem Len)+1,
+%% RandNum2 = if
+%% RandNum==Len -> RandNum-1;
+%% true->RandNum
+%% end,
+ InterfaceListItem = lists:nth(RemNum,List),
+ {ok,InterfaceListItem#interface_list.connection_info}
+ end.
+
+clean_invalid_provider([])->
+ ok;
+clean_invalid_provider([HostFlag | DeleteProverList])->
+ case ets:lookup(?PROVIDER_NODE_LIST_TABLE,HostFlag) of
+ []->
+ ok;
+ ProviderNodeList->
+ io:format("ConnectionList ~p~n",[ProviderNodeList]),
+ ProviderNodeList1 = lists_util:del_duplicate(ProviderNodeList),
+ clean_connection_info(ProviderNodeList1)
+ end,
+ clean_invalid_provider(DeleteProverList).
+
+clean_connection_info(ProviderNodeList)->
+ lists:map(fun(Item)->
+ Pid=Item#provider_node_list.connection_info#connection_info.pid,
+ ConnectionId=Item#provider_node_list.connection_info#connection_info.connection_id,
+ Pattern=#interface_list{pid=Pid,_='_'},
+ ets:delete_object(?INTERFCE_LIST_TABLE,Pattern),
+ dubbo_consumer_pool_sup:stop_children(ConnectionId)
+ end,ProviderNodeList),
+ ok.
\ No newline at end of file
diff --git a/src/dubbo_consumer_pool_sup.erl b/src/dubbo_consumer_pool_sup.erl
new file mode 100644
index 0000000..a1ce87e
--- /dev/null
+++ b/src/dubbo_consumer_pool_sup.erl
@@ -0,0 +1,80 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 30. Dec 2017 9:58 AM
+%%%-------------------------------------------------------------------
+-module(dubbo_consumer_pool_sup).
+-author("dlive").
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0,add_children/1,stop_children/1]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%%===================================================================
+%%% API functions
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the supervisor
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(start_link() ->
+ {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%%===================================================================
+%%% Supervisor callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Whenever a supervisor is started using supervisor:start_link/[2,3],
+%% this function is called by the new process to find out about
+%% restart strategy, maximum restart frequency and child
+%% specifications.
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(init(Args :: term()) ->
+ {ok, {SupFlags :: {RestartStrategy :: supervisor:strategy(),
+ MaxR :: non_neg_integer(), MaxT :: non_neg_integer()},
+ [ChildSpec :: supervisor:child_spec()]
+ }} |
+ ignore |
+ {error, Reason :: term()}).
+init([]) ->
+ RestartStrategy = one_for_one,
+ MaxRestarts = 1000,
+ MaxSecondsBetweenRestarts = 3600,
+
+ SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
+
+ Restart = permanent,
+ Shutdown = 2000,
+ Type = worker,
+
+%% AChild = {'AName', {'AModule', start_link, []}, Restart, Shutdown, Type, ['AModule']},
+
+ {ok, {SupFlags, []}}.
+
+
+add_children(ChildSpec)->
+ supervisor:start_child(?SERVER,ChildSpec).
+stop_children(ChildID)->
+ supervisor:terminate_child(?SERVER,ChildID).
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/dubbo_directory.erl b/src/dubbo_directory.erl
new file mode 100644
index 0000000..7551d2f
--- /dev/null
+++ b/src/dubbo_directory.erl
@@ -0,0 +1,146 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 26. Feb 2017 10:27 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_directory).
+-author("dlive").
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(start_link() ->
+ {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+-spec(init(Args :: term()) ->
+ {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term()} | ignore).
+init([]) ->
+ {ok, #state{}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
+ State :: #state{}) ->
+ {reply, Reply :: term(), NewState :: #state{}} |
+ {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_cast(Request :: term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
+ State :: #state{}) -> term()).
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
+ Extra :: term()) ->
+ {ok, NewState :: #state{}} | {error, Reason :: term()}).
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/dubbo_invoker.erl b/src/dubbo_invoker.erl
new file mode 100644
index 0000000..fa4821a
--- /dev/null
+++ b/src/dubbo_invoker.erl
@@ -0,0 +1,96 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 06. Mar 2018 12:17 AM
+%%%-------------------------------------------------------------------
+-module(dubbo_invoker).
+-author("dlive").
+
+-include("dubbo.hrl").
+%% API
+-export([invoke_request/2,invoke_request/5]).
+
+-spec invoke_request(Interface::binary(),Request::#dubbo_request{})->
+ {ok,reference()}|
+ {ok,reference(),Data::any(),RpcContent::list()}|
+ {error,Reason::timeout|no_provider|any()}.
+invoke_request(Interface,Request)->
+ invoke_request(Interface,Request,[],#{},self()).
+
+-spec invoke_request(Interface::binary(),Request::#dubbo_request{},RpcContext::list(),RequestState::map(),CallBackPid::pid())->
+ {ok,reference()}|
+ {ok,reference(),Data::any(),RpcContent::list()}|
+ {error,Reason::timeout|no_provider|request_full|any()}.
+invoke_request(Interface,Request,RpcContext,RequestState,CallBackPid)->
+ case dubbo_consumer_pool:select_connection(Interface,Request#dubbo_request.mid) of
+ {ok,#connection_info{pid=Pid,host_flag = HostFlag}} ->
+ case dubbo_traffic_control:check_goon(HostFlag,199) of
+ ok ->
+ Request2 = merge_attachments(Request,RpcContext),
+ {ok,RequestData} = de_codec:encode_request(Request2),
+ Ref=get_ref(RequestState),
+ RequestState2 = request_context:update(<<"t_agent_e">>,RequestState),
+ gen_server:cast(Pid,{send_request,Ref,Request2,RequestData,CallBackPid,RequestState2}),
+ case is_sync(RequestState) of
+ true->
+ sync_receive(Ref,get_timeout(RequestState));
+ false-> {ok, Ref}
+ end;
+ full ->
+ {error,request_full}
+ end;
+ {error,none}->
+ lager:error("[INVOKE] ~p error Reason no_provider",[Interface]),
+ {error,no_provider};
+ {error,R1}->
+ lager:error("[INVOKE] ~p error Reason ~p",[Interface,R1]),
+ {error,R1}
+ end.
+
+
+is_sync(Option)->
+ maps:is_key(sync,Option).
+%% lists:member(sync,Option).
+get_ref(Option)->
+ maps:get(ref,Option,make_ref()).
+%% case maps:is_key(ref,Option) of
+%% true->
+%%
+%% end,
+%% case proplists:get_value(ref,Option) of
+%% undefined->
+%% make_ref();
+%% Ref->
+%% Ref
+%% end.
+
+get_timeout(Option)->
+ maps:get(timeout,Option,?REQUEST_TIME_OUT).
+
+
+sync_receive(Ref,TimeOut)->
+ receive
+ {'$gen_cast',{msg_back,Ref,Response,RpcContent}}->
+ {ok,Ref,Response,RpcContent}
+ after
+ TimeOut ->
+ {error,timeout}
+ end.
+
+merge_attachments(Request,Option)->
+ Attachements= Request#dubbo_request.data#dubbo_rpc_invocation.attachments,
+ case lists:keyfind(attachments,1,Option) of
+ false->OptionAttachments=[];
+ {attachments,OptionAttachments}->
+ OptionAttachments
+ end,
+ List=[
+ {<<"version">>, <<"0.0.0">>},
+ {<<"timeout">>, <<"500">>}
+ ],
+ Attachements2= lists:merge3(Attachements,OptionAttachments,List),
+ Data2=Request#dubbo_request.data#dubbo_rpc_invocation{attachments = Attachements2},
+ Request#dubbo_request{data = Data2}.
diff --git a/src/dubbo_netty_client.erl b/src/dubbo_netty_client.erl
new file mode 100644
index 0000000..d5ba464
--- /dev/null
+++ b/src/dubbo_netty_client.erl
@@ -0,0 +1,452 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 14. 十月 2016 下午3:31
+%%%-------------------------------------------------------------------
+-module(dubbo_netty_client).
+-author("dlive").
+
+-behaviour(gen_server).
+
+-include("dubbo.hrl").
+%% API
+-export([start_link/4]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+-export([check_recv_data/2]).
+
+-define(SERVER, ?MODULE).
+
+-record(heartbeat,{last_write=0,last_read=0,timeout=60000,max_timeout=61000}).
+-record(state, {provider_config,socket =undefined,
+ heartbeat=#heartbeat{},
+ recv_buffer= <<>> , %%从服务端接收的数据
+ host_flag
+}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(start_link(Name::binary(),HostFlag::binary(),ProviderConfig::#provider_config{},integer()) ->
+ {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
+start_link(Name,HostFlag,ProviderConfig,Index) ->
+ gen_server:start_link({local, Name}, ?MODULE, [HostFlag,ProviderConfig,Index], []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+-spec(init(Args :: term()) ->
+ {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term()} | ignore).
+init([HostFlag,ProviderConfig,Index]) ->
+ erlang:process_flag(min_bin_vheap_size, 1024*1024),
+%% erlang:process_flag(min_heap_size, 1024*1024),
+%% BindScheduler = (Index rem erlang:system_info(schedulers_online))+1,
+%% lager:info("will bind to scheduler ~p",[BindScheduler]),
+%% erlang:process_flag(scheduler, BindScheduler),
+%% erlang:process_flag(priority, high),
+
+ #provider_config{host = Host,port = Port}=ProviderConfig,
+ State = case open(Host,Port) of
+ {ok,Socket} ->
+ #state{socket = Socket};
+ {error}->
+ #state{}
+ end,
+ NowStamp = time_util:timestamp_ms(),
+ HeartBeatInfo = #heartbeat{last_read = NowStamp,last_write = NowStamp},
+ lager:info("netty client start ~p",[HostFlag]),
+%% start_heartbeat_timer(HeartBeatInfo),
+ {ok, State#state{provider_config=ProviderConfig,heartbeat=HeartBeatInfo,host_flag = HostFlag}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
+ State :: #state{}) ->
+ {reply, Reply :: term(), NewState :: #state{}} |
+ {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_cast(Request :: term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+
+handle_cast({send_request,Ref,Request,Data,SourcePid,RequestState}, State) ->
+ RequestState2 = request_context:update(<<"t_net_b">>,RequestState),
+ lager:debug("[send_request begin] send data to provider consumer mid ~p pid ~p sourcePid ~p",[Request#dubbo_request.mid,self(),SourcePid]),
+ NewState = case send_msg(Data,State) of
+ ok->
+ save_request_info(Request,SourcePid,Ref,RequestState2),
+ lager:debug("[send_request end] send data to provider consumer pid ~p state ok",[self()]),
+ State;
+ {error,closed}->
+ State2 = reconnect(State),
+ State2;
+ {error,R1}->
+ lager:error("[send_request end] send data to provider consumer pid error ~p ~p",[self(),R1]),
+ State
+ end,
+ HeartbeatInfo =update_heartbeat(write,NewState#state.heartbeat),
+ {noreply, NewState#state{heartbeat = HeartbeatInfo}};
+
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+
+
+handle_info({tcp,_Port,Data}, #state{recv_buffer = RecvBuffer} = State) ->
+%% inet:setopts(State#state.socket, [{active, once}]),
+%% lager:debug("[INFO] recv one data ~w",[Data]),
+ {ok,NextBuffer,NewState} = case check_recv_data(<< RecvBuffer/binary,Data/binary >>,State) of
+ {next_buffer,NextBuffer2,State3}->
+ lager:debug("[INFO] recv one data state wait next_buffer"),
+ {ok,NextBuffer2,State3}
+ end,
+%% HeartbeatInfo =update_heartbeat(write,NewState#state.heartbeat),
+ {noreply, NewState#state{recv_buffer = NextBuffer}};
+handle_info({tcp_closed,Port},State)->
+ NewState=reconnect(State),
+ {noreply, NewState};
+handle_info({timeout, _TimerRef, {reconnect}},State)->
+ NewState=reconnect(State),
+ {noreply, NewState};
+handle_info({timeout, _TimerRef, {heartbeat_timer}},State) ->
+ {ok,NewState} = case check_heartbeat_state(State) of
+ {normal}-> {ok,State};
+ {send_heart}->
+%% @todo send_heartbeat_msg(undefined,true,State);
+ {ok,State};
+ {reconnect} ->
+ %% @todo reconnect
+ {ok,State}
+ end,
+ HeartbeatInfo = update_heartbeat(write,NewState#state.heartbeat),
+ start_heartbeat_timer(HeartbeatInfo),
+ {noreply,NewState#state{heartbeat = HeartbeatInfo}};
+handle_info(_Info,State) ->
+ lager:warning("[INFO] get one info:~p",[_Info]),
+%% inet:setopts(State#state.socket, [{active, once}]),
+%% case State#state.tmp_pid of
+%% undefined ->ok;
+%% Pid ->
+%% gen_server:cast(Pid,{msg_back})
+%% end,
+ HeartbeatInfo =update_heartbeat(write,State#state.heartbeat),
+ {noreply, State#state{heartbeat = HeartbeatInfo}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
+ State :: #state{}) -> term()).
+terminate(_Reason, _State) ->
+ lager:warning("terminate reason:~p",[_Reason]),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
+ Extra :: term()) ->
+ {ok, NewState :: #state{}} | {error, Reason :: term()}).
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+open(Host,Port)->
+ lager:debug("will connect to provider ~p ~p",[Host,Port]),
+ %
+ case gen_tcp:connect(Host,Port,[
+ binary,
+ {packet,0},{active, false},
+ {reuseaddr,true},
+ {delay_send, true},
+ {nodelay, true},
+ {high_watermark, 512 * 1024},
+ {low_watermark, 256 * 1024},
+%% {high_msgq_watermark,128 * 1024},
+%% {low_msgq_watermark,64 * 1024},
+ {sndbuf, 512 * 1024},
+ {recbuf, 512 * 1024}
+ ]) of
+ {ok,Sockets} ->
+%% inet:setopts(Sockets, [{active, once}]),
+ inet:setopts(Sockets, [{active, true}]),
+ {ok,Sockets};
+ Info ->
+ lager:error("start netty client ~p~n",[Info]),
+ {error,Info}
+ end.
+
+reconnect(State)->
+ #provider_config{host = Host,port = Port} = State#state.provider_config,
+ case State#state.socket of
+ undefined ->ok;
+ Socket->
+ gen_tcp:close(Socket)
+ end,
+ case open(Host,Port) of
+ {ok,Socket2}->
+ State#state{socket = Socket2,recv_buffer = <<>>};
+ {error,_Info}->
+ erlang:start_timer(2000,self(),{reconnect}),
+ State#state{socket = undefined}
+ end.
+
+send_msg(Msg,State) ->
+ case State#state.socket of
+ undefined->
+ {error,closed};
+ Socket ->
+ case gen_tcp:send(Socket,Msg) of
+ ok->
+ ok;
+ {error,Reason}->
+ lager:error("send to server error,reason:~p",[Reason]),
+ {error,Reason}
+ end
+ end.
+
+%%%=================================================================
+%%% 心跳检测
+%%%=================================================================
+start_heartbeat_timer(HeartbeatInfo)->
+ erlang:start_timer(HeartbeatInfo#heartbeat.timeout, self() , {heartbeat_timer}),
+ ok.
+update_heartbeat(write,Info)->
+ Info#heartbeat{last_write = time_util:timestamp_ms()};
+update_heartbeat(read,Info)->
+ Info#heartbeat{last_read = time_util:timestamp_ms()}.
+
+
+check_heartbeat_state(#state{heartbeat = HeartBeatInfo}=_State)->
+ Now = time_util:timestamp_ms(),
+ #heartbeat{last_read = LastRead,last_write = LastWrite,timeout = Timeout,max_timeout = MaxTimeout} = HeartBeatInfo,
+ if
+ (Now - LastRead) > Timeout ->
+ {send_heart};
+ (Now - LastWrite) > Timeout ->
+ {send_heart};
+ (Now - LastRead) > MaxTimeout ->
+ {reconnect};
+ true->
+ {normal}
+ end.
+
+
+send_heartbeat_msg(Mid,NeedResponse,State)->
+ {ok,Bin} = de_heartbeat:generate_request(Mid,NeedResponse),
+ NewState = case send_msg(Bin,State) of
+ ok ->
+ lager:info("send one heartbeat msg to server"),
+ State;
+ {error,_Reason} ->
+ State2 = reconnect(State),
+ State2
+ end,
+ {ok,NewState}.
+
+%%%=================================================================
+%%% 接收数据处理
+%%%=================================================================
+-spec check_recv_data(Data::binary(),State::#state{})->{ready,ReadyData::binary()} | {ready,ReadyData::binary(),NextBuffer::binary()}.
+check_recv_data(<<?DUBBO_MEGIC_HIGH,?DUBBO_MEGIC_LOW,Rest/binary>> = Data,State) when byte_size(Rest)<14 ->
+ {next_buffer,Data,State};
+check_recv_data(<<?DUBBO_MEGIC_HIGH,?DUBBO_MEGIC_LOW,_OtherFlag:80,DataLen:32,Rest/binary>> = Data,State) ->
+ RestSize = byte_size(Rest),
+ if
+ DataLen==RestSize ->
+ {ok,State2} = process_data(Data,State),
+ {next_buffer,<<>>,State2};
+ DataLen>RestSize ->
+ lager:warning("need wait next buffer data ~p",[Data]),
+ {next_buffer,Data,State};
+ DataLen<RestSize ->
+ <<ReadyData:DataLen/binary,NextBuffer/binary>> = Rest,
+ OneData = <<?DUBBO_MEGIC_HIGH:8,?DUBBO_MEGIC_LOW:8,_OtherFlag:80,DataLen:32,ReadyData/binary>>,
+ {ok,State3} = process_data(OneData,State),
+%% lager:warning("recevi more data ~w ",[NextBuffer]),
+ check_recv_data(NextBuffer,State3)
+ end;
+check_recv_data(<<Error/integer,Data/binary>>,State)->
+ lager:error("recv bad header data,Begin Byte:~p",[Error]),
+ check_recv_data(Data,State);
+check_recv_data(<<>>,State)->
+ {next_buffer,<<>>,State}.
+
+
+process_data(Data,State)->
+ TmpTime = time_util:timestamp_ms(),
+ <<Header:16/binary,RestData/binary>> = Data,
+ case de_codec:decode_header(Header) of
+ {ok,response,ResponseInfo}->
+ %%心跳包的回应,是否会造成错误
+ dubbo_traffic_control:decr_count(State#state.host_flag),
+ case get_earse_request_info(ResponseInfo#dubbo_response.mid) of
+ undefined->
+ lager:error("dubbo response can't find request data,response ~p",[ResponseInfo]);
+ {SourcePid,Ref,RequestState} ->
+%% RequestState2 = request_context:update(<<"t_net_b">>,TmpTime,RequestState),
+ RequestState3 = request_context:update(<<"t_net_e">>,RequestState),
+
+ {ok,Res} = de_codec:decode_response(ResponseInfo,RestData),
+
+ %%从另一条路返回
+%% case Res#dubbo_response.is_event of
+%% false ->
+%% mesh_agent_invoker:response(Ref,Res#dubbo_response.data,RequestState3);
+%% _->
+%% ok
+%% end
+
+ %% 从原路返回
+ case Res#dubbo_response.is_event of
+ false ->
+ gen_server:cast(SourcePid,{response_process,Ref,RequestState3,Res#dubbo_response.data});
+ _->
+ ok
+ end
+%% gen_server:cast(SourcePid,{response_process,Ref,ResponseInfo,RestData,RequestState3})
+
+%% lager:debug("will cast mid ~p to source process SourcePid ~p",[Response#dubbo_response.mid,SourcePid]),
+%% RpcContent=[],
+%% ResponseData = de_type_transfer:response_to_native(Response),
+%% lager:debug("one response ~p",[Response]),
+%% gen_server:cast(SourcePid,{msg_back,Ref,Response,RpcContent,RequestState3})
+ end,
+
+
+
+%% {ok,Res} = de_codec:decode_response(ResponseInfo,RestData),
+%% lager:info("get one response mid ~p, is_event ~p state ~p",[Res#dubbo_response.mid,Res#dubbo_response.is_event,Res#dubbo_response.state]),
+%% {ok,State3} =process_response(Res#dubbo_response.is_event,Res,State,TmpTime),
+ {ok,State};
+ {ok,request,RequestInfo}->
+ {ok,Req} = de_codec:decode_request(RequestInfo,RestData),
+ lager:info("get one request mid ~p, is_event ~p",[Req#dubbo_request.mid,Req#dubbo_request.is_event]),
+ {ok,State2} = process_request(Req#dubbo_request.is_event,Req,State),
+ {ok,State2};
+ {error,Type,RelData}->
+ lager:error("process_data error type ~p RelData ~p",[Type,RelData]),
+ {ok,State}
+ end.
+
+
+%% @doc process event
+-spec process_response(IsEvent::boolean(),#dubbo_response{},#state{},term())->ok.
+process_response(false,Response,State,TmpTime)->
+ dubbo_traffic_control:decr_count(State#state.host_flag),
+ case get_earse_request_info(Response#dubbo_response.mid) of
+ undefined->
+ lager:error("dubbo response can't find request data,response ~p",[Response]);
+ {SourcePid,Ref,RequestState} ->
+%% RequestState2 = request_context:update(<<"t_net_b">>,TmpTime,RequestState),
+ RequestState3 = request_context:update(<<"t_net_e">>,RequestState),
+%% lager:debug("will cast mid ~p to source process SourcePid ~p",[Response#dubbo_response.mid,SourcePid]),
+ RpcContent=[],
+%% ResponseData = de_type_transfer:response_to_native(Response),
+%% lager:debug("one response ~p",[Response]),
+ gen_server:cast(SourcePid,{msg_back,Ref,Response,RpcContent,RequestState3})
+ end,
+ {ok,State};
+process_response(true,Response,State,TmpTime)->
+
+ {ok,State}.
+
+process_request(true,Request,State)->
+ {ok,NewState} = send_heartbeat_msg(Request#dubbo_request.mid,false,State),
+ {ok,NewState};
+process_request(false,Request,State)->
+ {ok,State}.
+
+
+save_request_info(Request,SourcePid,Ref,RequestState)->
+%% SaveFlag = get_request_flag(),
+
+ put(Request#dubbo_request.mid,{SourcePid,Ref,RequestState}).
+get_earse_request_info(Mid)->
+%% Flag=get_request_flag(Mid),
+ erase(Mid).
+
+
+
+
+get_request_flag(Mid)->
+ Mid.
+%% list_to_binary(io_lib:format(<<"request_~p">>,[Mid])).
\ No newline at end of file
diff --git a/src/dubbo_node_config_util.erl b/src/dubbo_node_config_util.erl
new file mode 100644
index 0000000..f0636e2
--- /dev/null
+++ b/src/dubbo_node_config_util.erl
@@ -0,0 +1,102 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 30. Dec 2017 2:27 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_node_config_util).
+-author("dlive").
+
+-include("dubbo.hrl").
+%% API
+-export([parse_provider_info/1,gen_provider_info/1]).
+
+%%
+parse_provider_info(ProviderStr)->
+ case http_uri:parse(http_uri:decode(ProviderStr),[{scheme_defaults,[{dubbo,20880}]}]) of
+ {ok, {Scheme, UserInfo, Host, Port, Path, Query}} ->
+ QueryStr = case lists:prefix("?",Query) of
+ true ->
+ [_|Query2] = Query,
+ Query2;
+ false->
+ Query
+ end,
+ QueryListTmp = string:tokens(QueryStr,"&"),
+ ProviderConfig = parse_parameter(QueryListTmp,#provider_config{protocol=Scheme,host=Host,port = Port}),
+ lager:debug("parse provider info string ~p,result: ~p",[ProviderStr,ProviderConfig]),
+ {ok,ProviderConfig};
+ {error,R1} ->
+ lager:debug("parse provider error string ~p, error ~p",[ProviderStr,R1]),
+ {error,R1}
+ end.
+%% PrivoderInfo =list_to_binary(http_uri:decode(binary_to_list(PrivoderStr))),
+%% [{ProtocolEndPos,3},{HostEndPos,1},{InterfaceEndPos,1} ] = binary:matches(PrivoderInfo,[<<"://">>,<<"/">>,<<"?">>],[]),
+%% HostLength=HostEndPos - ProtocolEndPos-3,
+%% InterfaceLenth=InterfaceEndPos-HostEndPos-1,
+%% << Protocol:5/binary,_:3/binary,Host:HostLength/binary,_:1/binary,Interface:InterfaceLenth/binary,_:1/binary,Parameter/binary>> = PrivoderInfo,
+%% [HostName,Port]=binary:split(Host,<<":">>),
+%% ParameterList = binary:split(Parameter,<<"&">>),
+
+%% io:format(user,"protocol end Pos ~p~n protocol:~p~n Host:~p~n Interface:~p~n",[ProtocolEndPos,Protocol,Host,Interface]),
+
+parse_parameter([],Config)->
+ Config;
+parse_parameter([Item|Rest],Config)->
+ case string:tokens(Item,"=") of
+ KeyPair when length(KeyPair) ==2 ->
+ [Key,Value] = KeyPair,
+ ConfigNew = parse_parameter(Key,Value,Config),
+ parse_parameter(Rest,ConfigNew);
+ KeyPair2 ->
+ lager:error("parse parameter error, keypair ~p",[KeyPair2]),
+ parse_parameter(Rest,Config)
+ end.
+parse_parameter("anyhost",Value,Config)->
+ Config#provider_config{anyhost = list_to_atom(Value)};
+parse_parameter("application",Value,Config)->
+ Config#provider_config{application = list_to_binary(Value)};
+parse_parameter("dubbo",Value,Config)->
+ Config#provider_config{dubbo = list_to_binary(Value)};
+parse_parameter("executes",Value,Config)->
+ Config#provider_config{executes = list_to_integer(Value)};
+parse_parameter("interface",Value,Config)->
+ Config#provider_config{interface = list_to_binary(Value)};
+parse_parameter("methods",Value,Config)->
+ MethodList= string:tokens(Value,","),
+ MethodList2 = [list_to_binary(Item) || Item <- MethodList],
+ Config#provider_config{methods = MethodList2};
+parse_parameter("side",Value,Config)->
+ Config#provider_config{side = list_to_binary(Value)};
+parse_parameter("interface",Value,Config)->
+ Config#provider_config{interface = list_to_binary(Value)};
+parse_parameter(_,_,Config)->
+ Config.
+
+gen_provider_info(ProviderConfig)->
+ Parameter=gen_provider_parameter(ProviderConfig),
+ Info = io_lib:format("dubbo://~s:~p/~s?~s",[
+ ProviderConfig#provider_config.host,
+ ProviderConfig#provider_config.port,
+ ProviderConfig#provider_config.interface,
+ Parameter
+ ]),
+ list_to_binary(http_uri:encode(Info)).
+
+gen_provider_parameter(Providerconfig) ->
+ Method= [binary_to_list(Item) || Item <- Providerconfig#provider_config.methods],
+ Method2= list_to_binary(string:join(Method,",")),
+ List=[
+ {<<"interface">>,Providerconfig#provider_config.interface},
+ {<<"application">>,Providerconfig#provider_config.application},
+ {<<"anyhost">>, <<"true">>},
+ {<<"dubbo">>,Providerconfig#provider_config.dubbo},
+ {<<"executes">>,integer_to_binary(Providerconfig#provider_config.executes)},
+ {<<"methods">>,Method2},
+ {<<"side">>,Providerconfig#provider_config.side},
+ {<<"timestamp">>,integer_to_binary(time_util:timestamp_ms())}
+ ],
+ List2 = [io_lib:format("~ts=~ts",[Key,Value]) || {Key,Value} <- List],
+ lists:flatten(string:join(List2,"&")).
\ No newline at end of file
diff --git a/src/dubbo_provider_protocol.erl b/src/dubbo_provider_protocol.erl
new file mode 100644
index 0000000..ad6074d
--- /dev/null
+++ b/src/dubbo_provider_protocol.erl
@@ -0,0 +1,200 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 20. Mar 2018 7:19 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_provider_protocol).
+-author("dlive").
+
+-behaviour(gen_server).
+-behaviour(ranch_protocol).
+-include("dubboerl.hrl").
+-include("dubbo.hrl").
+
+
+%% API
+-export([start_link/4,register_impl_provider/3, select_impl_provider/1]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-define(TIMEOUT, 5000).
+
+-record(heartbeat,{last_write=0,last_read=0,timeout=50000,max_timeout=9000}).
+
+-record(state, {transport,provider_config,socket =undefined,
+ heartbeat=#heartbeat{},
+ recv_buffer= <<>> %%从客户端接收的数据
+}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+
+start_link(Ref, Socket, Transport, Opts) ->
+ {ok, proc_lib:spawn_link(?MODULE, init, [{Ref, Socket, Transport, Opts}])}.
+
+%% gen_server.
+
+%% This function is never called. We only define it so that
+%% we can use the -behaviour(gen_server) attribute.
+%init([]) -> {ok, undefined}.
+
+init({Ref, Socket, Transport, _Opts = []}) ->
+ lager:info("provider connected"),
+ ok = ranch:accept_ack(Ref),
+ ok = Transport:setopts(Socket, [{active, once}]),
+ gen_server:enter_loop(?MODULE, [],
+ #state{socket=Socket, transport=Transport},
+ ?TIMEOUT).
+
+handle_info({tcp,_Port,Data}, #state{recv_buffer = RecvBuffer,socket=Socket, transport=Transport} = State) ->
+ Transport:setopts(Socket, [{active, once}]),
+ NowBuffer = << RecvBuffer/binary,Data/binary >>,
+
+ {ok,NextBuffer,NewState} = case check_recv_data(NowBuffer,State) of
+ {next_buffer,<<>>,State2} ->
+ {ok,<<>>,State2};
+ {next_buffer,NextBuffer2,State3}->
+ lager:debug("[INFO] recv one data state wait next_buffer"),
+ {ok,NextBuffer2,State3}
+ end,
+%% HeartbeatInfo =update_heartbeat(write,NewState#state.heartbeat),
+ {noreply, NewState#state{recv_buffer = NextBuffer}};
+handle_info({tcp_closed,Port},State)->
+%% NewState=reconnect(State),
+ {noreply, State};
+%%handle_info({timeout, _TimerRef, {reconnect}},State)->
+%% NewState=reconnect(State),
+%% {noreply, NewState};
+
+handle_info({tcp_closed, _Socket}, State) ->
+ {stop, normal, State};
+handle_info({tcp_error, _, Reason}, State) ->
+ {stop, Reason, State};
+handle_info(timeout, State) ->
+ {stop, normal, State};
+handle_info(_Info, State) ->
+ {stop, normal, State}.
+
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+handle_cast({send_response,Data}, #state{socket = Socket} = State) ->
+ case gen_tcp:send(Socket,Data) of
+ ok ->
+ ok;
+ Other ->
+ lager:warning("response error ~p",[Other])
+ end,
+ {noreply, State};
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+register_impl_provider(Interface,ImplModuleName,ModuleName)->
+ ets:insert(?PROVIDER_IMPL_TABLE,{Interface,ImplModuleName,ModuleName}),
+ ok.
+
+-spec select_impl_provider(Interface::binary()) ->{ok,binary()} | {error,term()}.
+select_impl_provider(Interface) ->
+ case ets:lookup(?PROVIDER_IMPL_TABLE,Interface) of
+ []->
+ {error,no_provider};
+ [{Interface,ImplModuleName,ModuleName}] ->
+ {ok,ImplModuleName}
+ end.
+
+
+
+%%%=================================================================
+%%% 接收数据处理
+%%%=================================================================
+-spec check_recv_data(Data::binary(),State::#state{})->{ready,ReadyData::binary()} | {ready,ReadyData::binary(),NextBuffer::binary()}.
+check_recv_data(<<?DUBBO_MEGIC_HIGH,?DUBBO_MEGIC_LOW,Rest/binary>> = Data,State) when byte_size(Rest)<14 ->
+ {next_buffer,Data,State};
+check_recv_data(<<?DUBBO_MEGIC_HIGH,?DUBBO_MEGIC_LOW,_OtherFlag:80,DataLen:32,Rest/binary>> = Data,State) ->
+ RestSize = byte_size(Rest),
+ if
+ DataLen==RestSize ->
+ {ok,State2} = process_data(Data,State),
+ {next_buffer,<<>>,State2};
+ DataLen>RestSize ->
+ lager:warning("need wait next buffer data ~p",[Data]),
+ {next_buffer,Data,State};
+ DataLen<RestSize ->
+ <<ReadyData:DataLen/binary,NextBuffer/binary>> = Rest,
+ OneData = <<?DUBBO_MEGIC_HIGH:8,?DUBBO_MEGIC_LOW:8,_OtherFlag:80,DataLen:32,ReadyData/binary>>,
+ {ok,State3} = process_data(OneData,State),
+ lager:warning("recevi more data ~w ",[NextBuffer]),
+ check_recv_data(NextBuffer,State3)
+ end;
+check_recv_data(<<Error/integer,Data/binary>>,State)->
+ lager:error("recv bad header data,Begin Byte:~p",[Error]),
+ check_recv_data(Data,State);
+check_recv_data(<<>>,State)->
+ {next_buffer,<<>>,State}.
+
+
+process_data(Data,State)->
+ <<Header:16/binary,RestData/binary>> = Data,
+ case de_codec:decode_header(Header) of
+ {ok,request,RequestInfo}->
+ {ok,Req} = de_codec:decode_request(RequestInfo,RestData),
+ lager:info("get one request mid ~p, is_event ~p",[Req#dubbo_request.mid,Req#dubbo_request.is_event]),
+ {ok,State2} = process_request(Req#dubbo_request.is_event,Req,State),
+ {ok,State2};
+ {ok,response,ResponseInfo}->
+ {ok,Res} = de_codec:decode_response(ResponseInfo,RestData),
+ lager:info("get one response mid ~p, is_event ~p state ~p",[Res#dubbo_response.mid,Res#dubbo_response.is_event,Res#dubbo_response.state]),
+ {ok,State3} =process_response(Res#dubbo_response.is_event,Res,State),
+ {ok,State3};
+ {error,Type,RelData}->
+ lager:error("process_data error type ~p RelData ~p",[Type,RelData]),
+ {ok,State}
+ end.
+
+
+%% @doc process event
+-spec process_response(IsEvent::boolean(),#dubbo_response{},#state{})->ok.
+process_response(true,Response,State)->
+
+ {ok,State};
+
+process_response(false,Response,State)->
+%% case get_earse_request_info(Response#dubbo_response.mid) of
+%% undefined->
+%% lager:error("dubbo response can't find request data,response ~p",[Response]);
+%% {SourcePid,Ref,Request} ->
+%% lager:debug("will cast mid ~p to source process SourcePid ~p",[Response#dubbo_response.mid,SourcePid]),
+%% RpcContent=[],
+%% ResponseData = de_type_transfer:response_to_native(Response),
+%% gen_server:cast(SourcePid,{msg_back,Ref,ResponseData,RpcContent})
+%% end,
+ {ok,State}.
+
+process_request(true,Request,State)->
+%% {ok,NewState} = send_heartbeat_msg(Request#dubbo_request.mid,State),
+ lager:info("process request event ~p",[Request]),
+ {ok,State};
+process_request(false,Request,State)->
+ lager:info("process request ~p",[Request]),
+ dubbo_provider_worker:process_request(Request,self()),
+ {ok,State}.
\ No newline at end of file
diff --git a/src/dubbo_provider_worker.erl b/src/dubbo_provider_worker.erl
new file mode 100644
index 0000000..00d299e
--- /dev/null
+++ b/src/dubbo_provider_worker.erl
@@ -0,0 +1,198 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 11. Mar 2018 8:08 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_provider_worker).
+-author("dlive").
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/1,process_request/2]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+-define(SERVER, ?MODULE).
+-include("dubbo.hrl").
+-include("dubboerl.hrl").
+-include("dubbo_type.hrl").
+
+-record(heartbeat,{last_write=0,last_read=0,timeout=50000,max_timeout=9000}).
+-record(state, {provider_config,socket =undefined,
+ heartbeat=#heartbeat{},
+ recv_buffer= <<>> %%从客户端接收的数据
+}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(start_link(term()) ->
+ {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
+start_link(Args) ->
+ gen_server:start_link(?MODULE, Args, []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+-spec(init(Args :: term()) ->
+ {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term()} | ignore).
+init(Args) ->
+ process_flag(trap_exit, true),
+ {ok, #state{}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
+ State :: #state{}) ->
+ {reply, Reply :: term(), NewState :: #state{}} |
+ {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_cast(Request :: term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_cast({request,Request,SourcePid},State)->
+%% #dubbo_request{mid=Mid} = Request,
+
+%% Data = #databaseOperateResponse{databaseOperateRsp = "ha-ha"},
+%% Data2 =#dubbo_rpc_invocation{parameters = [Data]},
+%% {ok,Content }= de_codec:encode_response(#dubbo_response{mid=Mid,is_event = false,data= Data2}),
+ {ok,Content} = invoker_implement(Request),
+ gen_server:cast(SourcePid,{send_response,Content}),
+
+ {noreply, State};
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
+ State :: #state{}) -> term()).
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
+ Extra :: term()) ->
+ {ok, NewState :: #state{}} | {error, Reason :: term()}).
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+process_request(Request,SourcePid)->
+ Worker = poolboy:checkout(?PROVIDER_WORKER),
+ try
+ gen_server:cast(Worker,{request,Request,SourcePid})
+ after
+ ok = poolboy:checkin(?PROVIDER_WORKER, Worker)
+ end.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+-spec invoker_implement(#dubbo_request{})-> {ok,response_content()}.
+invoker_implement(Request)->
+ #dubbo_rpc_invocation{className = Interface,methodName = MethodName,parameters = Parameters} = Request#dubbo_request.data,
+ case dubbo_provider_protocol:select_impl_provider(Interface) of
+ {ok,ImplModule}->
+ case apply(ImplModule,binary_to_atom(MethodName,latin1),Parameters) of
+ {error}->
+ ok;
+ #dubbo_rpc_invocation{}=ResultInvoca ->
+ #dubbo_request{mid = Mid} = Request,
+ {ok,Content }= de_codec:encode_response(#dubbo_response{mid=Mid,is_event = false,data= ResultInvoca}),
+ {ok,Content};
+ ResultObj->
+%% Data = #databaseOperateResponse{databaseOperateRsp = "ha-ha"},
+ #dubbo_request{mid = Mid} = Request,
+ Data2 =#dubbo_rpc_invocation{parameters = [ResultObj]},
+ {ok,Content }= de_codec:encode_response(#dubbo_response{mid=Mid,is_event = false,data= Data2}),
+ {ok,Content}
+ end;
+ {error,Reason} ->
+ {error,Reason}
+ end.
\ No newline at end of file
diff --git a/src/dubbo_provider_worker_sup.erl b/src/dubbo_provider_worker_sup.erl
new file mode 100644
index 0000000..0b5708f
--- /dev/null
+++ b/src/dubbo_provider_worker_sup.erl
@@ -0,0 +1,85 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 11. Mar 2018 8:04 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_provider_worker_sup).
+-author("dlive").
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+-define(SERVER, ?MODULE).
+
+%%%===================================================================
+%%% API functions
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the supervisor
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(start_link() ->
+ {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%%===================================================================
+%%% Supervisor callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Whenever a supervisor is started using supervisor:start_link/[2,3],
+%% this function is called by the new process to find out about
+%% restart strategy, maximum restart frequency and child
+%% specifications.
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(init(Args :: term()) ->
+ {ok, {SupFlags :: {RestartStrategy :: supervisor:strategy(),
+ MaxR :: non_neg_integer(), MaxT :: non_neg_integer()},
+ [ChildSpec :: supervisor:child_spec()]
+ }} |
+ ignore |
+ {error, Reason :: term()}).
+init([]) ->
+ RestartStrategy = one_for_one,
+ MaxRestarts = 1000,
+ MaxSecondsBetweenRestarts = 3600,
+
+ SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
+
+ Restart = permanent,
+ Shutdown = 2000,
+ Type = worker,
+
+ AChild = {'AName', {'AModule', start_link, []},
+ Restart, Shutdown, Type, ['AModule']},
+
+ PoolArgs = [{name, {local, provider_worker}},
+ {worker_module, dubbo_provider_worker},
+ {size, 5},
+ {max_overflow, 100}
+ ],
+ WorkerPool = poolboy:child_spec(provider_worker, PoolArgs, []),
+ {ok, {SupFlags, [WorkerPool]}}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+%%start_child(InterfaceList)->
+%% ChildSpec = {dubbo_provider_server,{dubbo_provider_server,start_link,[InterfaceList]},permanent,2000,worker},
+%% supervisor:start_child(?SERVER,ChildSpec),
+%% ok.
\ No newline at end of file
diff --git a/src/dubbo_serializa_fastjson.erl b/src/dubbo_serializa_fastjson.erl
new file mode 100644
index 0000000..819a118
--- /dev/null
+++ b/src/dubbo_serializa_fastjson.erl
@@ -0,0 +1,295 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 11. May 2018 10:07 AM
+%%%-------------------------------------------------------------------
+-module(dubbo_serializa_fastjson).
+-author("dlive").
+
+-include("dubbo.hrl").
+%% API
+-export([encode_request_data/1,decode_response/2,decode_request/2]).
+
+-export([decode_header/1]).
+-export([decode_request/2]).
+
+encode_request_data(Request)->
+ DataType =case Request#dubbo_request.is_event of
+ false->
+ case Request#dubbo_request.data of
+ #dubbo_rpc_invocation{} ->
+ dubbo_rpc_invocation;
+ _ ->
+ unknow
+ end;
+ true->
+ dubbo_event
+ end,
+ {ok,Bin} = encode_request_data(DataType,Request,Request#dubbo_request.data,[]),
+ {ok,Bin}.
+
+
+encode_request_data(dubbo_rpc_invocation,Request,Data,State) ->
+%% METHOD_NAME = Data#dubbo_rpc_invocation.methodName,
+%% METHOD_ARGS_TYPES = Data#dubbo_rpc_invocation.parameterDesc,
+ RequestList = [
+%% jiffy:encode(?DUBBO_VERSION,[]),
+ string_encode(?DUBBO_VERSION),
+ ?LINE_SEPERATOR,
+%% jiffy:encode(Data#dubbo_rpc_invocation.className,[]),
+ string_encode(Data#dubbo_rpc_invocation.className),
+ ?LINE_SEPERATOR,
+%% jiffy:encode(Data#dubbo_rpc_invocation.classVersion,[]),
+ string_encode(Data#dubbo_rpc_invocation.classVersion),
+ ?LINE_SEPERATOR,
+%% jiffy:encode(Data#dubbo_rpc_invocation.methodName,[]),
+ string_encode(Data#dubbo_rpc_invocation.methodName),
+ ?LINE_SEPERATOR,
+%% jiffy:encode(Data#dubbo_rpc_invocation.parameterDesc,[]),
+ string_encode(Data#dubbo_rpc_invocation.parameterDesc),
+ ?LINE_SEPERATOR
+%%
+%% hessianEncode:encode(?DUBBO_VERSION, State), %% dubbo version
+%% hessianEncode:encode(Data#dubbo_rpc_invocation.className, State),
+%% hessianEncode:encode(Data#dubbo_rpc_invocation.classVersion, State),
+%% hessianEncode:encode(METHOD_NAME, State),
+%% hessianEncode:encode(METHOD_ARGS_TYPES, State)
+ ],
+ {ArgsBin,_} = encode_arguments(Data,State),
+%% AttachDict = dict:from_list(Data#dubbo_rpc_invocation.attachments),
+%% AttachMaps = #map{dict = AttachDict },
+%% AttachBinay = hessianEncode:encode(AttachMaps, State2),
+ AttachBinay = jiffy:encode({Data#dubbo_rpc_invocation.attachments},[]),
+ RequestData = erlang:iolist_to_binary(RequestList ++ [ArgsBin,AttachBinay,?LINE_SEPERATOR]),
+ {ok,RequestData};
+encode_request_data(dubbo_event,Request,Data,State) ->
+ %% @todo 确认该数据类型
+ Bin = jiffy:encode(Data),
+%% Bin = hessianEncode:encode(Data,State),
+ {ok,Bin}.
+
+
+-spec encode_response(#dubbo_response{})-> {ok,term()}.
+encode_response(Response)->
+ {ok,ResponseData} = encode_response_data(Response),
+ Size = byte_size(ResponseData),
+ Header = encode_response_header(Response,Size,?RESPONSE_STATE_OK),
+ ResponseContent = <<Header/binary,ResponseData/binary>>,
+ {ok, ResponseContent}.
+
+encode_response_data(Response)->
+ State=type_encoding:init(),
+ DataType =case Response#dubbo_response.is_event of
+ true->
+ dubbo_event;
+ false->
+ case Response#dubbo_response.data of
+ #dubbo_rpc_invocation{} ->
+ dubbo_rpc_invocation;
+ _ ->
+ unknow
+ end
+ end,
+ {ok,Bin} = encode_response_data(DataType,Response,Response#dubbo_response.data,State),
+ {ok,Bin}.
+encode_response_data(dubbo_event,Response,Data,State) ->
+ Bin = hessianEncode:encode(Data,State),
+ {ok,Bin};
+encode_response_data(dubbo_rpc_invocation,Response,Data,State) ->
+ Result = case Data of
+ null ->
+ [
+ hessianEncode:encode(?RESPONSE_NULL_VALUE, State)
+ ];
+ _ ->
+ {ArgsBin,_State2} = encode_arguments(Data,State),
+ [
+ hessianEncode:encode(?RESPONSE_VALUE, State),
+ ArgsBin
+ ]
+ end,
+ ResponseData = erlang:iolist_to_binary(Result),
+ {ok,ResponseData}.
+
+encode_response_header(Response,DataLen, ResponseState)->
+ Header2= Response#dubbo_response.serialize_type,
+ Header21=case Response#dubbo_response.is_twoway of
+ true -> Header2 bor 64;
+ false-> Header2
+ end,
+ Header22=case Response#dubbo_response.is_event of
+ true -> Header21 bor 32;
+ false-> Header21
+ end,
+ RequestId = Response#dubbo_response.mid,
+ Header = << ?DUBBO_MEGIC:16,Header22:8, ResponseState:8,RequestId:64,DataLen:32>>,
+ Header.
+encode_arguments(Data,State)->
+ {Bin} = lists:foldl(
+ fun(X,{BinTmp})->
+ ArgsBin = string_encode(X),
+ {<<BinTmp/binary,ArgsBin/binary ,?LINE_SEPERATOR/binary>>}
+ end,
+ {<<>>},Data#dubbo_rpc_invocation.parameters),
+ {Bin,State}.
+
+-spec decode_header(binary())-> {State::ok|error,Type::request|response,Data::#dubbo_response{}|#dubbo_request{}}.
+decode_header(Header)->
+ <<?DUBBO_MEGIC_HIGH,?DUBBO_MEGIC_LOW,Flag:8,State:8,Mid:64,DataLen:32>> = Header,
+ if
+ (Flag band 16#80) == 0 ->
+ {DecodeState,Res} = decode_header(response,Flag,State,Mid,DataLen),
+ {DecodeState,response,Res};
+ true ->
+ {DecodeState,Req} = decode_header(request,Flag,State,Mid,DataLen),
+ {DecodeState,request,Req}
+ end.
+decode_header(request,Flag,State,Mid,DataLen)->
+ SerializeType = Flag band 16#1f,
+ IsTwoWay = if
+ (Flag band 16#40) /=0 -> true;
+ true -> false
+ end,
+ IsEvent = if
+ (Flag band 16#20) /=0 -> true;
+ true -> false
+ end,
+ Req = #dubbo_request{
+ is_event = IsEvent,
+ is_twoway = IsTwoWay,
+ mid = Mid,
+ mversion = <<"2.0.0">>,
+ serialize_type = SerializeType
+ },
+ {ok,Req};
+decode_header(response,Flag,State,Mid,DataLen)->
+ SerializeType = Flag band 16#1f,
+ IsEvent = if
+ (Flag band 16#20) /= 0 -> true;
+ true -> false
+ end,
+ Res = #dubbo_response{is_event = IsEvent,
+ serialize_type = SerializeType,
+ state = State,
+ mid = Mid
+ },
+ {ok,Res}.
+
+-spec decode_response(#dubbo_response{},binary())-> {ok,#dubbo_response{}}.
+decode_response(Res,Data)->
+ if
+ Res#dubbo_response.is_event == true ->
+ decode_response(dubbo_event,Res,Data);
+ true ->
+ decode_response(dubbo_rpc_invocation,Res,Data)
+ end.
+decode_response(dubbo_rpc_invocation,Res,Data)->
+ DataList = binary:split(Data,<<"\n">>),
+ [TypeBin | DataList1] = DataList,
+%% {Rest,Type,State} = hessianDecode2:decode(Data,hessianDecode2:init()),
+ Type = jiffy:decode(TypeBin),
+
+ case Type of
+ ?RESPONSE_VALUE ->
+%% {_,Object,DecodeState} = hessianDecode2:decode(Rest,State),
+ [Value | _] = DataList1,
+ Object = jiffy:decode(Value),
+ {ok,Res#dubbo_response{data = Object}};
+ ?RESPONSE_NULL_VALUE ->
+ {ok,Res#dubbo_response{data = null}};
+ ?RESPONSE_WITH_EXCEPTION ->
+ [ExceptionValue | _] = DataList1,
+ ExceptionObject = jiffy:decode(ExceptionValue),
+%% lager:warning("decode unkonw type ~p ~p",[Type,Rest]),
+%% {Rest2,Object2,DecodeState2} = hessianDecode2:decode(Rest,State),
+%% lager:warning("decode unkonw type2 ~p ~p",[Object2,Rest2]),
+ {ok,Res#dubbo_response{data = ExceptionObject}};
+ Other ->
+ lager:error("server response unkonw info ~p",[Other]),
+ {ok,Res#dubbo_response{data = <<"server pool exhausted">>}}
+
+ end;
+decode_response(dubbo_event,Res,Data)->
+%% {_Rest,undefined,_NewState} = hessianDecode2:decode(Data,hessianDecode2:init()),
+ {ok,Res#dubbo_response{data = null}}.
+
+-spec decode_request(#dubbo_request{},binary())-> {ok,#dubbo_request{}}.
+decode_request(Req,Data)->
+ if
+ Req#dubbo_request.is_event == true ->
+ decode_request(dubbo_event,Req,Data);
+ true ->
+ decode_request(dubbo_rpc_invocation,Req,Data)
+ end.
+
+decode_request(dubbo_rpc_invocation,Req,Data)->
+ {ResultList,NewState,RestData} = decode_request_body(Data,hessianDecode2:init(),[dubbo,path,version,method_name,desc_and_args,attachments]),
+ [DubboVersion,Path,Version,MethodName,Desc,ArgsObj,Attachments]=ResultList,
+ RpcData = #dubbo_rpc_invocation{className = Path,classVersion = Version,methodName = MethodName,parameterDesc = Data,parameters = ArgsObj,attachments = Attachments},
+ Req2 = Req#dubbo_request{data = RpcData},
+ {ok,Req2};
+%% {Rest,Dubbo,State} = hessianDecode2:decode(Data,hessianDecode2:init()),
+%% {Rest1,ClassName,State1} = hessianDecode2:decode(Data,State),
+%% {Rest2,ClassName,State2} = hessianDecode2:decode(Rest1,State1),
+%% case Type of
+%% 1 ->
+%% {_,Object,DecodeState} = hessianDecode2:decode(Rest,State),
+%% {ok,Req#dubbo_request{data = Object,decode_state = DecodeState}};
+%% 2 ->
+%% {ok,Req#dubbo_request{data = null,decode_state = State}};
+%% _->
+%% lager:warning("decode unkonw type ~p ~p",[Type,Rest]),
+%% {Rest2,Object2,DecodeState2} = hessianDecode2:decode(Rest,State),
+%% lager:warning("decode unkonw type2 ~p ~p",[Object2,Rest2]),
+%% {ok,Req#dubbo_request{data = Object2,decode_state = DecodeState2}}
+%% end;
+decode_request(dubbo_event,Req,Data)->
+%% DataList = binary:split(Data,<<"\n">>),
+ lager:debug("dubbo_event datalist ~w",[Data]),
+ Result = jiffy:decode(Data,[]),
+%% {_Rest,undefined,_NewState} = hessianDecode2:decode(Data,hessianDecode2:init()),
+ {ok,Req#dubbo_request{data = Result}}.
+
+decode_request_body(Data,State,List)->
+ {ResultList,NewState,RestData} = decode_request_body(List,Data,State,[]),
+ {lists:reverse(ResultList),NewState,RestData}.
+decode_request_body([ParseType|List],Data,State,ResultList)
+ when ParseType==dubbo;ParseType==path;ParseType==version;ParseType==method_name ->
+ {Rest,Result,NewState } = hessianDecode2:decode(Data,State),
+ decode_request_body(List,Rest,NewState, [Result] ++ ResultList);
+decode_request_body([desc_and_args| List],Data,State,ResultList)->
+ {Rest,ParameterDesc,State1 } = hessianDecode2:decode(Data,State),
+ if
+ size(ParameterDesc) == 0 ->
+ decode_request_body(List,Rest,State1, [ [],[] ]++ ResultList);
+ true ->
+ ParameterDescArray = binary:split(ParameterDesc,<<";">>),
+ {ArgsObjList,NewState,RestData} = decode_request_body_args(ParameterDescArray,Rest,State1,[]),
+ decode_request_body(List,RestData,NewState, [ArgsObjList,ParameterDesc]++ ResultList)
+ end;
+decode_request_body([attachments|List],Data,State,ResultList)->
+ {Rest,Attachments,State1 } = hessianDecode2:decode(Data,State),
+ AttachmentsList = dict:to_list(Attachments#map.dict),
+ decode_request_body(List,Rest,State1,[AttachmentsList] ++ ResultList);
+decode_request_body([_Type1|List],Data,State,ResultList)->
+ lager:warning("decode_request_body unknow type"),
+ decode_request_body(List,Data,State, ResultList);
+decode_request_body([],Data,State,ResultList)->
+ {ResultList,State,Data}.
+
+decode_request_body_args([],Data,State,ArgsObjList)->
+ {ArgsObjList,State,Data};
+decode_request_body_args([ArgsType|RestList],Data,State,ArgsObjList) when ArgsType== <<>> ->
+ decode_request_body_args(RestList,Data,State,ArgsObjList);
+decode_request_body_args([_ArgsType|RestList],Data,State,ArgsObjList) ->
+ {Rest,ArgObj,NewState } = hessianDecode2:decode(Data,State),
+ ArgObj2 = de_type_transfer:classobj_to_native(ArgObj,NewState),
+ decode_request_body_args(RestList,Rest,NewState,ArgsObjList++[ArgObj2]).
+
+string_encode(Data) when is_binary(Data)->
+ << <<"\"">>/binary,Data/binary,<<"\"">>/binary >>;
+string_encode(Data)->
+ jiffy:encode(Data).
diff --git a/src/dubbo_serializa_hession.erl b/src/dubbo_serializa_hession.erl
new file mode 100644
index 0000000..1e622fb
--- /dev/null
+++ b/src/dubbo_serializa_hession.erl
@@ -0,0 +1,246 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 11. May 2018 4:12 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_serializa_hession).
+-author("dlive").
+
+-include("dubbo.hrl").
+%% API
+-export([decode_header/1]).
+-export([decode_response/2]).
+-export([decode_request/2]).
+
+
+encode_request_data(dubbo_event,Request,Data,State) ->
+ Bin = hessianEncode:encode(Data,State),
+ {ok,Bin};
+encode_request_data(dubbo_rpc_invocation,Request,Data,State) ->
+ METHOD_NAME = Data#dubbo_rpc_invocation.methodName,
+ METHOD_ARGS_TYPES = Data#dubbo_rpc_invocation.parameterDesc,
+ RequestList = [
+ hessianEncode:encode(?DUBBO_VERSION, State), %% dubbo version
+ hessianEncode:encode(Data#dubbo_rpc_invocation.className, State),
+ hessianEncode:encode(Data#dubbo_rpc_invocation.classVersion, State),
+ hessianEncode:encode(METHOD_NAME, State),
+ hessianEncode:encode(METHOD_ARGS_TYPES, State)
+ ],
+ {ArgsBin,State2} = encode_arguments(Data,State),
+ AttachDict = dict:from_list(Data#dubbo_rpc_invocation.attachments),
+ AttachMaps = #map{dict = AttachDict },
+ {AttachBinay,_} = hessianEncode:encode(AttachMaps, State2),
+ RequestData = erlang:iolist_to_binary(RequestList ++ [ArgsBin,AttachBinay]),
+ {ok,RequestData}.
+
+-spec encode_response(#dubbo_response{})-> {ok,term()}.
+encode_response(Response)->
+ {ok,ResponseData} = encode_response_data(Response),
+ Size = byte_size(ResponseData),
+ Header = encode_response_header(Response,Size,?RESPONSE_STATE_OK),
+ ResponseContent = <<Header/binary,ResponseData/binary>>,
+ {ok, ResponseContent}.
+
+encode_response_data(Response)->
+ State=type_encoding:init(),
+ DataType =case Response#dubbo_response.is_event of
+ true->
+ dubbo_event;
+ false->
+ case Response#dubbo_response.data of
+ #dubbo_rpc_invocation{} ->
+ dubbo_rpc_invocation;
+ _ ->
+ unknow
+ end
+ end,
+ {ok,Bin} = encode_response_data(DataType,Response,Response#dubbo_response.data,State),
+ {ok,Bin}.
+encode_response_data(dubbo_event,Response,Data,State) ->
+ Bin = hessianEncode:encode(Data,State),
+ {ok,Bin};
+encode_response_data(dubbo_rpc_invocation,Response,Data,State) ->
+ Result = case Data of
+ null ->
+ [
+ hessianEncode:encode(?RESPONSE_NULL_VALUE, State)
+ ];
+ _ ->
+ {ArgsBin,_State2} = encode_arguments(Data,State),
+ [
+ hessianEncode:encode(?RESPONSE_VALUE, State),
+ ArgsBin
+ ]
+ end,
+ ResponseData = erlang:iolist_to_binary(Result),
+ {ok,ResponseData}.
+
+encode_response_header(Response,DataLen, ResponseState)->
+ Header2= Response#dubbo_response.serialize_type,
+ Header21=case Response#dubbo_response.is_twoway of
+ true -> Header2 bor 64;
+ false-> Header2
+ end,
+ Header22=case Response#dubbo_response.is_event of
+ true -> Header21 bor 32;
+ false-> Header21
+ end,
+ RequestId = Response#dubbo_response.mid,
+ Header = << ?DUBBO_MEGIC:16,Header22:8, ResponseState:8,RequestId:64,DataLen:32>>,
+ Header.
+encode_arguments(Data,State)->
+ {StateNew} = lists:foldl(fun(X,{StateTmp})->
+ StateTmpNew = type_encoding:enlist(X,StateTmp),
+ {StateTmpNew} end,
+ {State},Data#dubbo_rpc_invocation.parameterTypes),
+ {Bin,State2} = lists:foldl(fun(X,{BinTmp,StateTmp2})->
+ case hessianEncode:encode(X, StateTmp2) of
+ {ArgsBin,StateTmpNew} ->
+ {<<BinTmp/binary,ArgsBin/binary>>, StateTmpNew};
+ ArgsBin2 ->
+ {<<BinTmp/binary,ArgsBin2/binary>>, StateTmp2}
+ end end,
+ {<<>>,StateNew},Data#dubbo_rpc_invocation.parameters),
+ {Bin,State2}.
+
+-spec decode_header(binary())-> {State::ok|error,Type::request|response,Data::#dubbo_response{}|#dubbo_request{}}.
+decode_header(Header)->
+ <<?DUBBO_MEGIC_HIGH,?DUBBO_MEGIC_LOW,Flag:8,State:8,Mid:64,DataLen:32>> = Header,
+ if
+ (Flag band 16#80) == 0 ->
+ {DecodeState,Res} = decode_header(response,Flag,State,Mid,DataLen),
+ {DecodeState,response,Res};
+ true ->
+ {DecodeState,Req} = decode_header(request,Flag,State,Mid,DataLen),
+ {DecodeState,request,Req}
+ end.
+decode_header(request,Flag,State,Mid,DataLen)->
+ SerializeType = Flag band 16#1f,
+ IsTwoWay = if
+ (Flag band 16#40) /=0 -> true;
+ true -> false
+ end,
+ IsEvent = if
+ (Flag band 16#20) /=0 -> true;
+ true -> false
+ end,
+ Req = #dubbo_request{
+ is_event = IsEvent,
+ is_twoway = IsTwoWay,
+ mid = Mid,
+ mversion = <<"2.0.0">>,
+ serialize_type = SerializeType
+ },
+ {ok,Req};
+decode_header(response,Flag,State,Mid,DataLen)->
+ SerializeType = Flag band 16#1f,
+ IsEvent = if
+ (Flag band 16#20) /= 0 -> true;
+ true -> false
+ end,
+ Res = #dubbo_response{is_event = IsEvent,
+ serialize_type = SerializeType,
+ state = State,
+ mid = Mid
+ },
+ {ok,Res}.
+
+-spec decode_response(#dubbo_response{},binary())-> {ok,#dubbo_response{}}.
+decode_response(Res,Data)->
+ if
+ Res#dubbo_response.is_event == true ->
+ decode_response(dubbo_event,Res,Data);
+ true ->
+ decode_response(dubbo_rpc_invocation,Res,Data)
+ end.
+
+decode_response(dubbo_rpc_invocation,Res,Data)->
+ {Rest,Type,State} = hessianDecode2:decode(Data,hessianDecode2:init()),
+ case Type of
+ 1 ->
+ {_,Object,DecodeState} = hessianDecode2:decode(Rest,State),
+ {ok,Res#dubbo_response{data = Object,decode_state = DecodeState}};
+ 2 ->
+ {ok,Res#dubbo_response{data = null,decode_state = State}};
+ _->
+ lager:warning("decode unkonw type ~p ~p",[Type,Rest]),
+ {Rest2,Object2,DecodeState2} = hessianDecode2:decode(Rest,State),
+ lager:warning("decode unkonw type2 ~p ~p",[Object2,Rest2]),
+ {ok,Res#dubbo_response{data = Object2,decode_state = DecodeState2}}
+ end;
+decode_response(dubbo_event,Res,Data)->
+ {_Rest,undefined,_NewState} = hessianDecode2:decode(Data,hessianDecode2:init()),
+ {ok,Res#dubbo_response{data = undefined}}.
+
+-spec decode_request(#dubbo_request{},binary())-> {ok,#dubbo_request{}}.
+decode_request(Req,Data)->
+ if
+ Req#dubbo_request.is_event == true ->
+ decode_request(dubbo_event,Req,Data);
+ true ->
+ decode_request(dubbo_rpc_invocation,Req,Data)
+ end.
+
+decode_request(dubbo_rpc_invocation,Req,Data)->
+ {ResultList,NewState,RestData} = decode_request_body(Data,hessianDecode2:init(),[dubbo,path,version,method_name,desc_and_args,attachments]),
+ [DubboVersion,Path,Version,MethodName,Desc,ArgsObj,Attachments]=ResultList,
+ RpcData = #dubbo_rpc_invocation{className = Path,classVersion = Version,methodName = MethodName,parameterDesc = Data,parameters = ArgsObj,attachments = Attachments},
+ Req2 = Req#dubbo_request{data = RpcData},
+ {ok,Req2};
+%% {Rest,Dubbo,State} = hessianDecode2:decode(Data,hessianDecode2:init()),
+%% {Rest1,ClassName,State1} = hessianDecode2:decode(Data,State),
+%% {Rest2,ClassName,State2} = hessianDecode2:decode(Rest1,State1),
+%% case Type of
+%% 1 ->
+%% {_,Object,DecodeState} = hessianDecode2:decode(Rest,State),
+%% {ok,Req#dubbo_request{data = Object,decode_state = DecodeState}};
+%% 2 ->
+%% {ok,Req#dubbo_request{data = null,decode_state = State}};
+%% _->
+%% lager:warning("decode unkonw type ~p ~p",[Type,Rest]),
+%% {Rest2,Object2,DecodeState2} = hessianDecode2:decode(Rest,State),
+%% lager:warning("decode unkonw type2 ~p ~p",[Object2,Rest2]),
+%% {ok,Req#dubbo_request{data = Object2,decode_state = DecodeState2}}
+%% end;
+decode_request(dubbo_event,Req,Data)->
+ {_Rest,undefined,_NewState} = hessianDecode2:decode(Data,hessianDecode2:init()),
+ {ok,Req#dubbo_request{data = undefined}}.
+
+decode_request_body(Data,State,List)->
+ {ResultList,NewState,RestData} = decode_request_body(List,Data,State,[]),
+ {lists:reverse(ResultList),NewState,RestData}.
+decode_request_body([ParseType|List],Data,State,ResultList)
+ when ParseType==dubbo;ParseType==path;ParseType==version;ParseType==method_name ->
+ {Rest,Result,NewState } = hessianDecode2:decode(Data,State),
+ decode_request_body(List,Rest,NewState, [Result] ++ ResultList);
+decode_request_body([desc_and_args| List],Data,State,ResultList)->
+ {Rest,ParameterDesc,State1 } = hessianDecode2:decode(Data,State),
+ if
+ size(ParameterDesc) == 0 ->
+ decode_request_body(List,Rest,State1, [ [],[] ]++ ResultList);
+ true ->
+ ParameterDescArray = binary:split(ParameterDesc,<<";">>),
+ {ArgsObjList,NewState,RestData} = decode_request_body_args(ParameterDescArray,Rest,State1,[]),
+ decode_request_body(List,RestData,NewState, [ArgsObjList,ParameterDesc]++ ResultList)
+ end;
+decode_request_body([attachments|List],Data,State,ResultList)->
+ {Rest,Attachments,State1 } = hessianDecode2:decode(Data,State),
+ AttachmentsList = dict:to_list(Attachments#map.dict),
+ decode_request_body(List,Rest,State1,[AttachmentsList] ++ ResultList);
+decode_request_body([_Type1|List],Data,State,ResultList)->
+ lager:warning("decode_request_body unknow type"),
+ decode_request_body(List,Data,State, ResultList);
+decode_request_body([],Data,State,ResultList)->
+ {ResultList,State,Data}.
+
+decode_request_body_args([],Data,State,ArgsObjList)->
+ {ArgsObjList,State,Data};
+decode_request_body_args([ArgsType|RestList],Data,State,ArgsObjList) when ArgsType== <<>> ->
+ decode_request_body_args(RestList,Data,State,ArgsObjList);
+decode_request_body_args([_ArgsType|RestList],Data,State,ArgsObjList) ->
+ {Rest,ArgObj,NewState } = hessianDecode2:decode(Data,State),
+ ArgObj2 = de_type_transfer:classobj_to_native(ArgObj,NewState),
+ decode_request_body_args(RestList,Rest,NewState,ArgsObjList++[ArgObj2]).
\ No newline at end of file
diff --git a/src/dubbo_traffic_control.erl b/src/dubbo_traffic_control.erl
new file mode 100644
index 0000000..d7b6fcb
--- /dev/null
+++ b/src/dubbo_traffic_control.erl
@@ -0,0 +1,52 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 22. May 2018 1:58 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_traffic_control).
+-author("dlive").
+-include("dubboerl.hrl").
+%% API
+-export([init/0,check_goon/2,decr_count/1]).
+
+
+init()->
+ case ets:info(?TRAFFIC_CONTROL) of
+ undefined ->
+ io:format("init decoding TRAFFIC_CONTROL table pid ~p~n",[self()]),
+ ets:new(?TRAFFIC_CONTROL,[public,named_table, {write_concurrency, true}]); %% public
+ _ ->
+ ets:delete(?TRAFFIC_CONTROL),
+ ets:new(?TRAFFIC_CONTROL,[public,named_table, {write_concurrency, true}])
+ end,
+ ok.
+
+
+check_goon(Key,Max)->
+ try ets:update_counter(?TRAFFIC_CONTROL,Key,1) of
+ Value when Value > Max ->
+ ets:update_counter(?TRAFFIC_CONTROL,Key,-1),
+ full;
+ _V ->
+%% lager:debug("check traffic incr value ~p",[V]),
+ ok
+
+ catch
+ _T:_R->
+ ets:insert(?TRAFFIC_CONTROL,{Key,1}),
+ ok
+ end.
+
+decr_count(Key)->
+ try ets:update_counter(?TRAFFIC_CONTROL,Key,-1) of
+ _V ->
+%% lager:debug("check traffic decr value ~p",[V]),
+ ok
+ catch
+ _T:_R->
+ ets:insert(?TRAFFIC_CONTROL,{Key,0}),
+ ok
+ end.
\ No newline at end of file
diff --git a/src/dubbo_zookeeper.erl b/src/dubbo_zookeeper.erl
new file mode 100644
index 0000000..f0016a8
--- /dev/null
+++ b/src/dubbo_zookeeper.erl
@@ -0,0 +1,270 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 29. 十月 2016 上午9:56
+%%%-------------------------------------------------------------------
+-module(dubbo_zookeeper).
+-author("dlive").
+
+-behaviour(gen_server).
+
+-include("dubbo.hrl").
+%% API
+-export([start_link/0,register_consumer/1,register_consumer/2,gen_consumer_node_info/1,register_provider/1,provider_watcher/1]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {zk_pid}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(start_link() ->
+ {ok, Pid :: pid()} | ignore | {error, Reason :: term()}).
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+-spec(init(Args :: term()) ->
+ {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term()} | ignore).
+init([]) ->
+ {ok,Pid} = connection(),
+ {ok, #state{zk_pid = Pid}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_call(Request :: term(), From :: {pid(), Tag :: term()},
+ State :: #state{}) ->
+ {reply, Reply :: term(), NewState :: #state{}} |
+ {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+
+handle_call({add_consumer,Consumer}, _From, State) ->
+ add_consumer(Consumer,State),
+ {reply, ok, State};
+handle_call({add_provider,Provider}, _From, State) ->
+ register_provider_path(Provider,State),
+ {reply, ok, State};
+handle_call(_Request, _From, State) ->
+ {reply, ok, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_cast(Request :: term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_cast({provider_node_change,Interface,Path}, #state{zk_pid = Pid}=State) ->
+ get_provider_and_start(Pid,Interface,Path),
+ {noreply, State};
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+-spec(handle_info(Info :: timeout() | term(), State :: #state{}) ->
+ {noreply, NewState :: #state{}} |
+ {noreply, NewState :: #state{}, timeout() | hibernate} |
+ {stop, Reason :: term(), NewState :: #state{}}).
+handle_info(_Info, State) ->
+ lager:info("zk server recv msg:~p",[_Info]),
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+-spec(terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
+ State :: #state{}) -> term()).
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+-spec(code_change(OldVsn :: term() | {down, term()}, State :: #state{},
+ Extra :: term()) ->
+ {ok, NewState :: #state{}} | {error, Reason :: term()}).
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+register_consumer(Consumer) ->
+ gen_server:call(?SERVER,{add_consumer,Consumer}),
+ ok.
+register_consumer(Name,Option)->
+ Consumer=#consumer_config{interface = Name,methods = [<<"testa">>,<<"testb">>]},
+ register_consumer(Consumer),
+ ok.
+register_provider(Provider)->
+ gen_server:call(?SERVER,{add_provider,Provider}),
+ ok.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+connection()->
+ {ok,List} = application:get_env(dubboerl,zookeeper_list),
+ {ok, Pid} = erlzk:connect(List, 30000, [
+ {chroot, "/"},
+ {monitor, self()}]),
+ {ok,Pid}.
+
+add_consumer(Consumer,State)->
+ Pid= State#state.zk_pid,
+%% InterfacePath= << <<"/dubbo/">>/binary,Name/binary ,<<"consumers">>/binary >>,
+ ConsumerNode = gen_consumer_node_info(Consumer),
+ ConsumerNode2= list_to_binary(edoc_lib:escape_uri(binary_to_list(ConsumerNode))),
+ check_and_create_path(Pid,<<"">>,[{<<"dubbo">>,p},{Consumer#consumer_config.interface,p},{<<"consumers">>,p},{ConsumerNode2,e} ]),
+ get_provider_list(Consumer,State),
+ ok.
+register_provider_path(Provider,State)->
+ #state{zk_pid = Pid}=State,
+ ProviderNode = dubbo_node_config_util:gen_provider_info(Provider),
+ check_and_create_path(Pid,<<"">>,[{<<"dubbo">>,p},{Provider#provider_config.interface,p},{<<"providers">>,p},{ProviderNode,e}]),
+ ok.
+
+
+get_provider_list(Consumer,State)->
+ Pid= State#state.zk_pid,
+ InterfacePath= << <<"/dubbo/">>/binary,(Consumer#consumer_config.interface)/binary ,<<"/providers">>/binary >>,
+ get_provider_and_start(Pid,Consumer#consumer_config.interface,InterfacePath),
+ ok.
+get_provider_and_start(Pid,Interface,Path)->
+ case erlzk:get_children(Pid,Path,spawn(dubbo_zookeeper,provider_watcher,[Interface])) of
+ {ok,ChildList} ->
+ lager:debug("get provider list ~p",[ChildList]),
+ start_provider_process(Interface,ChildList),
+ ok;
+ {error,R1} ->
+ lager:debug("[add_consumer] get_provider_list error ~p ~p",[R1]),
+ ok
+ end.
+
+provider_watcher(Interface)->
+ receive
+ {node_children_changed,Path} ->
+ gen_server:cast(?SERVER,{provider_node_change,Interface,Path}),
+ lager:debug("provider_watcher get event ~p ~p",[node_children_changed,Path]);
+ {Event, Path} ->
+%% Path = "/a",
+%% Event = node_created
+ lager:debug("provider_watcher get event ~p ~p",[Event,Path])
+ end,
+ ok.
+
+
+create_path(Pid,Path,CreateType)->
+ case erlzk:create(Pid,Path,CreateType) of
+ {ok,ActualPath}->
+ lager:debug("[add_consumer] create zk path success ~p",[ActualPath]),
+ ok;
+ {error,R1}->
+ lager:debug("[add_consumer] create zk path error ~p ~p",[Path,R1])
+ end,
+ ok.
+check_and_create_path(_Pid,_RootPath,[]) ->
+ ok;
+check_and_create_path(Pid,RootPath,[{Item,CreateType}|Rst])->
+ CheckPath= << RootPath/binary,<<"/">>/binary,Item/binary >>,
+ case erlzk:exists(Pid,CheckPath) of
+ {ok,Stat} ->
+ check_and_create_path(Pid,CheckPath,Rst);
+ {error,no_node}->
+ lager:debug("[add_consumer] check_and_create_path unexist no_node ~p",[CheckPath]),
+ create_path(Pid,CheckPath,CreateType),
+ check_and_create_path(Pid,CheckPath,Rst);
+ {error,R1} ->
+ lager:debug("[add_consumer] check_and_create_path unexist ~p",[R1]),
+ check_and_create_path(Pid,CheckPath,Rst)
+ end.
+
+gen_consumer_node_info(Consumer)->
+ %% revision参数字段的作用是什么? 暂时不添加
+ Methods=lists_util:join(Consumer#consumer_config.methods,<<",">>),
+ Value=io_lib:format(<<"consumer://~s/~s?application=~s&category=~s&check=~p&default.timeout=~p&dubbo=~s&interface=~s&methods=~s&side=~s×tamp=~p">>,
+ [de_common_fun:local_ip_v4_str(),
+ Consumer#consumer_config.interface,
+ Consumer#consumer_config.application,
+ Consumer#consumer_config.category,
+ Consumer#consumer_config.check,
+ Consumer#consumer_config.default_timeout,
+ Consumer#consumer_config.dubbo_version,
+ Consumer#consumer_config.interface,
+ Methods,
+ Consumer#consumer_config.side,
+ time_util:timestamp_ms()
+ ]),
+ list_to_binary(Value).
+
+%%dubbo_zookeeper:register_consumer(<<"com.ifcoder.abcd">>,[]).
+start_provider_process(Interface,ProviderList)->
+ dubbo_consumer_pool:start_consumer(Interface,ProviderList).
+
diff --git a/src/dubboerl.app.src b/src/dubboerl.app.src
new file mode 100644
index 0000000..4998bf0
--- /dev/null
+++ b/src/dubboerl.app.src
@@ -0,0 +1,16 @@
+{application, dubboerl,
+ [{description, "An OTP application"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {mod, { dubboerl_app, []}},
+ {applications,
+ [kernel,
+ stdlib,xmerl,eunit,erlzk,lager,poolboy,inets
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {maintainers, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/src/dubboerl.erl b/src/dubboerl.erl
new file mode 100644
index 0000000..8d841bd
--- /dev/null
+++ b/src/dubboerl.erl
@@ -0,0 +1,54 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 22. Dec 2017 11:54 PM
+%%%-------------------------------------------------------------------
+-module(dubboerl).
+-author("dlive").
+
+-include("dubboerl.hrl").
+%% API
+-export([init/0,start_consumer/0,start_provider/0]).
+
+init()->
+ start_consumer(),
+ start_provider(),
+ ok.
+
+
+start_consumer()->
+ ConsumerList = application:get_env(dubboerl,consumer,[]),
+ ApplicationName = application:get_env(dubboerl,application,<<"defaultApplication">>),
+ lists:map(fun({Interface,Option})->
+ ConsumerInfo = dubbo_config_util:gen_consumer(ApplicationName,Interface,Option),
+ dubbo_zookeeper:register_consumer(ConsumerInfo),
+ lager:info("register consumer success ~p",[Interface])
+ end,ConsumerList),
+ ok.
+
+start_provider()->
+ ProviderList = application:get_env(dubboerl,provider,[]),
+ ApplicationName = application:get_env(dubboerl,application,<<"defaultApplication">>),
+ DubboServerPort = application:get_env(dubboerl,port,20881),
+ start_provider_listen(DubboServerPort),
+ lists:map(fun({ImplModuleName,BehaviourModuleName,Interface,Option})->
+ ok = dubbo_provider_protocol:register_impl_provider(Interface,ImplModuleName,BehaviourModuleName),
+ MethodList= apply(BehaviourModuleName,get_method_999_list,[]),
+ ProviderInfo = dubbo_config_util:gen_provider(ApplicationName,DubboServerPort,Interface,MethodList,Option),
+ dubbo_zookeeper:register_provider(ProviderInfo),
+ lager:info("register provider success ~p ~p",[ImplModuleName,Interface])
+ end,ProviderList),
+ ok.
+
+start_provider_listen(Port)->
+ ets:new(?PROVIDER_IMPL_TABLE,[public,named_table]),
+ {ok, _} = ranch:start_listener(tcp_reverse,
+ ranch_tcp, [{port, Port}], dubbo_provider_protocol, []),
+ ok.
+
+
+
+
diff --git a/src/dubboerl_app.erl b/src/dubboerl_app.erl
new file mode 100644
index 0000000..fb3a1a2
--- /dev/null
+++ b/src/dubboerl_app.erl
@@ -0,0 +1,42 @@
+%%%-------------------------------------------------------------------
+%% @doc dubboerl public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(dubboerl_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1,env_init/0]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ io:format("[START] server start~n"),
+ env_init(),
+ dubboerl_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+env_init()->
+ dubbo_traffic_control:init(),
+ type_register:init(),
+ register_type_list().
+%% type_decoding:init().
+
+
+register_type_list()->
+ List = java_type_defined:get_list(),
+ lists:map(
+ fun({NativeType,ForeignType,Fields}) ->
+ de_type_transfer:pre_process_typedef(NativeType,ForeignType,Fields)
+ end,List),
+ ok.
\ No newline at end of file
diff --git a/src/dubboerl_sup.erl b/src/dubboerl_sup.erl
new file mode 100644
index 0000000..4e42308
--- /dev/null
+++ b/src/dubboerl_sup.erl
@@ -0,0 +1,57 @@
+%%%-------------------------------------------------------------------
+%% @doc dubboerl top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(dubboerl_sup).
+
+-behaviour(supervisor).
+
+-include("common.hrl").
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+
+%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
+init([]) ->
+ ZK = {dubbo_zookeeper,{dubbo_zookeeper, start_link, []},transient,5000,worker,[dubbo_zookeeper]},
+%% NettySer = {dubbo_netty_client,{dubbo_netty_client, start_link, []},transient,5000,worker,[dubbo_netty_client]},
+ Id_count = {de_id_count,{de_id_count, start_link, []},transient,5000,worker,[de_id_count]},
+ ProviderPoolSup = {dubbo_provider_worker_sup,{dubbo_provider_worker_sup, start_link, []},transient,5000,supervisor,[dubbo_provider_worker_sup]},
+ ConsumerPoolSup = {dubbo_consumer_pool_sup,{dubbo_consumer_pool_sup, start_link, []},transient,5000,supervisor,[dubbo_consumer_pool_sup]},
+ ConsumerPool = {dubbo_consumer_pool,{dubbo_consumer_pool, start_link, []},transient,5000,worker,[dubbo_consumer_pool]},
+%% Reloader = {push_reloader,{push_reloader, start_link, []},transient,5000,worker,[push_reloader]},
+%% List = case ?RELOADER of
+%% true ->
+%% io:format("[START] will start push_reloader service2222~n"),
+%% [Reloader];
+%% _->[]
+%% end,
+ ListNew1=
+ case application:get_env(dubboerl,registry,false) of
+ true ->
+ [ZK];
+ false->
+ []
+ end,
+ ListNew = ListNew1 ++ [Id_count,ConsumerPool,ConsumerPoolSup,ProviderPoolSup],
+ {ok, { {one_for_one, 60, 10}, ListNew} }.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/src/encode_test.erl b/src/encode_test.erl
new file mode 100644
index 0000000..e806784
--- /dev/null
+++ b/src/encode_test.erl
@@ -0,0 +1,53 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2016, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 18. 十月 2016 上午10:46
+%%%-------------------------------------------------------------------
+-module(encode_test).
+-author("dlive").
+
+-include("hessian.hrl").
+%% API
+-export([object_test/0,de_object/0]).
+
+-record(de_TestReq, {name, nick,age}).
+-record(de_reg2, {reqinfo,age}).
+
+object_test()->
+ ForeignTypeA = <<"com.ifcoder.demo.bean.UserInfoRequest">>,
+ TypeDefA = #type_def{foreign_type = ForeignTypeA,
+ native_type = de_TestReq,
+ fieldnames = record_info(fields,de_TestReq)},
+ EncodingState0 = type_encoding:enlist(TypeDefA),
+ io:format("state:~p~n",[EncodingState0]),
+ RequestArg0 = #de_TestReq{name = <<"nameinfo">>, nick = <<"nickname">> ,age=10 },
+%% Object = #object{values = [RequestArg0] },
+%% io:format("Object:~p~n",[Object]),
+ {Bin, State0} = hessianEncode:encode(RequestArg0, EncodingState0),
+ file:write_file("/tmp/object_erl.data",Bin),
+%% ?assertMatch(Expected, Bin),
+ %% This test is asymmetric because decode($O,Rest/bin)
+ %% will only consume the outer class definition,
+ %% in normal circumstances, the calling decode/2 function will recursively
+ %% consume all object definitions.
+%% {Rest, TypeDef, State1} = hessian:decode(Bin, DecodingState),
+%% ?assertMatch(TypeDefA, TypeDef),
+%% [Function|Arguments] = hessian:decode(<<99,2,0,109,0,1,97,Bin/binary>>, DecodingState),
+%% ?assertMatch([[A]], Arguments).
+
+ ok.
+
+de_object()->
+ ForeignTypeA = <<"com.ifcoder.demo.bean.UserInfoRequest">>,
+ TypeDefA = #type_def{foreign_type = ForeignTypeA,
+ native_type = de_TestReq,
+ fieldnames = record_info(fields,de_TestReq)},
+ EncodingState0 = hessianDecode2:enlist(TypeDefA),
+ {ok,Bin} = file:read_file("/tmp/object_erl.data"), %% /tmp/objectencode.data
+ Info = hessianDecode2:decode(Bin,EncodingState0),
+ io:format("info:~p~n",[Info]),
+ ok.
+
diff --git a/src/hessian.erl b/src/hessian.erl
new file mode 100644
index 0000000..b877b97
--- /dev/null
+++ b/src/hessian.erl
@@ -0,0 +1,590 @@
+% ---------------------------------------------------------------------------
+% Copyright (C) 2008 0x6e6562
+%
+% Licensed under the Apache License, Version 2.0 (the "License");
+% you may not use this file except in compliance with the License.
+% You may obtain a copy of the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS,
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+% See the License for the specific language governing permissions and
+% limitations under the License.
+% ---------------------------------------------------------------------------
+
+-module(hessian).
+
+-include("hessian.hrl").
+
+-export([call/5]).
+-export([invoke/4]).
+-export([decode/2]).
+-export([encode/2, encode/3, encode/4, encode/5]).
+%% TODO Move the e2cc function to encoding module
+-export([erlang_to_camel_case/1]).
+
+%---------------------------------------------------------------------------
+% Decoding
+%---------------------------------------------------------------------------
+
+% Binaries
+decode(<<16#20,Rest/binary>>, State) -> {Rest, <<>>, State};
+decode(<<Len:8/unsigned,Rest/binary>>, State) when Len =< 16#2f, 16#20 < Len ->
+ _Len = Len - 16#20,
+ <<Bin:_Len/binary,_Rest/binary>> = Rest,
+ {_Rest, Bin, State};
+decode(<<$B,Len:16/unsigned,Bin:Len/binary,Rest/binary>>, State) -> {Rest, Bin, State};
+decode(<<$b,Rest/binary>>,State) -> decode(<<$b,Rest/binary>>, [], State);
+%% Booleans
+decode(<<$T,Rest/binary>>, State) -> {Rest, true, State};
+decode(<<$F,Rest/binary>>, State) -> {Rest, false, State};
+%% Dates
+decode(<<$d,Date:64/unsigned,Rest/binary>>, State) ->
+ MegaSecs = Date div ?MegaSeconds,
+ Secs = (Date - MegaSecs * ?MegaSeconds) div ?Seconds,
+ MicroSecs = (Date - MegaSecs * ?MegaSeconds - Secs * ?Seconds) * ?MicroSeconds,
+ {Rest, {MegaSecs, Secs, MicroSecs}, State};
+%% Doubles
+decode(<<16#67,Rest/binary>>, State)-> {Rest, 0.0, State};
+decode(<<16#68,Rest/binary>>, State)-> {Rest, 1.0, State};
+decode(<<16#69,Int:8/signed,Rest/binary>>, State)-> {Rest, float(Int), State};
+decode(<<16#6a,Int:16/signed,Rest/binary>>, State)-> {Rest, float(Int), State};
+%% TODO ask erlang-questions about not being able to match a 32bit float
+decode(<<16#6b,Int:32/signed,Rest/binary>>, State)->
+ <<Double:64/float>> = <<Int:32,0,0,0,0>>,
+ {Rest, Double, State};
+decode(<<$D,Double:64/float,Rest/binary>>, State)-> {Rest, Double, State};
+%% Ints
+decode(<<$I,Int:32/unsigned,Rest/binary>>, State)-> {Rest, Int, State};
+decode(<<Int:8,Rest/binary>>, State) when Int >= 16#80, Int =< 16#bf -> {Rest, Int - 16#90, State};
+decode(<<B2:8,B1:8,B0:8,Rest/binary>>, State) when B2 >= 16#d0, B2 =< 16#d7 -> {Rest, ((B2 - 16#d4) bsl 16) + (B1 bsl 8) + B0, State};
+decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 >= 16#c0, B1 =< 16#cf -> {Rest, ((B1 - 16#c8) bsl 8) + B0, State};
+%% Longs
+decode(<<$L,Long:64/unsigned,Rest/binary>>, State)-> {Rest, Long, State};
+decode(<<16#77,Long:32,Rest/binary>>, State) -> {Rest, Long, State};
+decode(<<Long:8,Rest/binary>>, State) when Long >= 16#d8, Long =< 16#ef -> {Rest, Long - 16#e0, State};
+decode(<<B2:8,B1:8,B0:8,Rest/binary>>, State) when B2 >= 16#38, B2 =< 16#3f -> {Rest, ((B2 - 16#3c) bsl 16) + (B1 bsl 8) + B0, State};
+decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 >= 16#f0, B1 =< 16#ff -> {Rest, ((B1 - 16#f8) bsl 8) + B0, State};
+%% Strings
+decode(<<0,Rest/binary>>, State) -> {Rest, <<>>, State};
+decode(<<Len:8,String:Len/binary,Rest/binary>>, State) when Len < 32 -> {Rest, list_to_binary(xmerl_ucs:from_utf8(String)), State};
+decode(<<$S,Len:16/unsigned,String:Len/binary,Rest/binary>>, State) -> {Rest, list_to_binary(xmerl_ucs:from_utf8(String)), State};
+decode(<<$s,Rest/binary>>, State) -> decode(<<$s,Rest/binary>>,[], State);
+%% Nulls
+decode(<<$N,Rest/binary>>, State) -> {Rest, undefined, State};
+%% References
+decode(<<$R,Ref:32/unsigned,Rest/binary>>, State)-> {ref, Rest, Ref, State};
+%% Maps
+decode(<<$M,$t,L:16/unsigned,Type:L/binary,Map/binary>>, State) -> decode(map, Map, dict:store(fqn, Type, dict:new()), State);
+decode(<<$M,Map/binary>>, State) -> decode(map, Map, dict:new(), State);
+%%list ::= V type? length? value* z
+%% ::= v int int value*
+%%length ::= 'l' b3 b2 b1 b0
+%% ::= x6e int
+%% Lists
+decode(<<$V,$t,L1:16/unsigned,T:L1/binary,$l,L2:32/unsigned,Bin/binary>>, State) -> decode(list, Bin, [], State);
+decode(<<$V,$l,L1:32/unsigned,Bin/binary>>, State) -> decode(list, Bin, [], State);
+decode(<<$V,16#6e,L1:8,Bin/binary>>, State) -> decode(list, Bin, [], State);
+decode(<<$V,16#6e,L1:16,Bin/binary>>, State) -> decode(list, Bin, [], State);
+decode(<<$V,$t,L1:16/unsigned,T:L1/binary,Bin/binary>>, State) -> decode(list, Bin, [], State);
+decode(<<$V,Bin/binary>>, State) -> decode(list, Bin, [], State);
+decode(<<$O,?M,?m,Hash:32/unsigned,$u,$z>>, State0) -> {peer_cannot_resolve,Hash};
+decode(<<$O,Hash:32/unsigned,$t,L:16/unsigned,Type:L/binary,Bin/binary>>, State0) ->
+ {Rest, TypeDef, State} = decode(type_definition,Type,Bin,State0),
+ NewState = type_decoding:hash_store(Hash,TypeDef,State),
+ {Rest, {Hash,TypeDef}, NewState};
+decode(<<$O,Hash:32/unsigned,Bin/binary>>, State) ->
+ ?TYPEDEF(Hash),
+ case type_decoding:hash_lookup(Hash, State) of
+ {not_found, Hash} ->
+ %?LOG(Hash),
+ {Bin, {not_found, Hash}, State};
+ TypeDef ->
+ %?LOG(TypeDef),
+ NewState = type_decoding:visit(TypeDef,State),
+ {Bin,TypeDef,NewState}
+ end;
+decode(<<$o,Bin/binary>>, State) ->
+ {Rest,Ref,_State} = decode(Bin, State),
+ ?INSTANCE(Ref),
+ ForeignView = type_decoding:lookup_reference(Ref, _State),
+ #type_def{native_type = NativeType,
+ foreign_type = ForeignType,
+ fieldnames = ForeignFieldNames} = ForeignView,
+ NativeView = type_decoding:resolve_native_type(ForeignType,_State),
+ Count = count_fields(ForeignView),
+ case decode(field, Rest, Count,[], _State) of
+ {not_found,Hash} ->
+ {not_found,Hash};
+ {_Rest,FieldValues, NewState} ->
+ Object = type_decoding:project_native_view(ForeignFieldNames,FieldValues,NativeView),
+ {_Rest, Object, NewState}
+ end;
+%---------------------------------------------------------------------------
+% Call and reply decoding
+%---------------------------------------------------------------------------
+decode(<<$c,?M,?m,$m,L1:16/unsigned,Function:L1/binary,Bin/binary>>, State) ->
+ ?START(c, [?M,?m]),
+ ?METHOD(Function),
+ case decode(list, Bin,[], State) of
+ {error, Encoded} ->
+ {error, Encoded};
+ {not_found, Hash} ->
+ {not_found, Hash};
+ {Rest, Arguments, NewState} ->
+ [type_decoding:camel_case_to_erlang(Function), Arguments]
+ end;
+decode(<<$r,?M,?m,$N,$z>>, State) -> ok;
+%% TODO The string decoding in the fault decoding is a bit dodgy
+%% Also, have a look at the way it is being encoded
+decode(<<$r,?M,?m,$f,
+ 4,"code",L1:8,Code:L1/binary,
+ 7,"message",L2:8,Message:L2/binary,
+ 6,"detail",L3:8,Detail:L3/binary,$z>>, State) ->
+ {error, list_to_atom(binary_to_list(Message)) };
+decode(<<$r,?M,?m,Args/binary>>, State) ->
+ case decode(Args,[], State) of
+ {<<>>, Decoded,_State} ->
+ case lists:dropwhile(fun is_type_def/1, Decoded) of
+ [Value] ->
+ Value;
+ [H|T] ->
+ [H|T]
+ end;
+ {error, Encoded} ->
+ {error, Encoded}
+ end;
+%% Type queries
+decode(<<$q,?M,?m,Hash:32/unsigned,$z>>, State) ->
+ ?START(q, [?M,?m]),
+ ?VALUE(<<Hash:32>>),
+ case type_decoding:hash_lookup(Hash, State) of
+ {not_found, Hash} ->
+ {error, encode(fault, <<"ProtocolException">>, unknown_hash, Hash, State) };
+ TypeDef ->
+ {Hash,TypeDef, State}
+ end;
+decode(<<$y,?M,?m,Hash:32/unsigned,$z>>, State) ->
+ {type_query_ack, Hash};
+%% Anything else
+decode(<<Unexpected/binary>>, State) ->
+ {error, encode(fault, <<"ProtocolException">>, unexpected_byte_sequence, Unexpected, State) }.
+%----------------------------------
+decode(<<$b,Len:16/unsigned,Bin:Len/binary,$b,Rest/binary>>, Acc, State) ->
+ decode(<<$b,Rest/binary>>,Acc ++ [Bin], State);
+decode(<<$b,Len:16/unsigned,Bin:Len/binary,$B,Rest/binary>>, Acc, State) ->
+ _Acc = Acc ++ [Bin],
+ {_Rest,_Bin, State} = decode(<<$B,Rest/binary>>, State),
+ {_Rest, list_to_binary(_Acc ++ [_Bin]), State};
+decode(<<$s,Len:16,String:Len/binary,$s,Rest/binary>>,Acc, State) ->
+ _String = list_to_binary(xmerl_ucs:from_utf8(String)),
+ decode(<<$s,Rest/binary>>,Acc ++ [_String], State);
+decode(<<$s,Len:16,String:Len/binary,$S,Rest/binary>>,Acc, State) ->
+ _String = list_to_binary(xmerl_ucs:from_utf8(String)),
+ _Acc = Acc ++ [_String],
+ {_Rest,_Bin, _State} = decode(<<$S,Rest/binary>>, State),
+ {_Rest, list_to_binary(_Acc ++ [_Bin]), _State};
+decode(<<>>, List, State) -> {<<>>, List, State};
+decode(<<$z>>, List, State) -> {<<>>, List, State};
+decode(Args, List, State) ->
+ case decode(Args,State) of
+ {Rest, [H|T], _State} ->
+ decode(Rest, List ++ [H|T], _State);
+ {Rest, Result, _State} ->
+ decode(Rest, List ++ [Result], _State);
+ {ref, Rest, Ref, _State} ->
+ _Ref = lists:nth(Ref + 1, List),
+ decode(Rest, [List,[_Ref]] , _State);
+ {error, Encoded} ->
+ {error, Encoded}
+ end.
+decode(map, <<$z>>, Dict, State) -> {Dict, State};
+decode(map, <<$z,Rest/binary>>, Dict, State) -> {Rest,Dict, State};
+decode(map, Bin, Dict, State) ->
+ {_Rest, Key, _State} = decode(Bin, State),
+ case decode(_Rest, _State) of
+ {Rest, Value, __State} ->
+ decode(map, Rest, dict:store(Key, Value, Dict), __State);
+ {ref, Rest, Ref, __State} ->
+ %Value = lists:nth(Ref + 1, List),
+ Value = Ref,
+ decode(map, Rest, dict:store(Key, Value, Dict), __State)
+ end;
+decode(list, <<>>, List, State) -> {<<>>,List, State};
+decode(list, <<$z>>, List, State) -> {<<>>,List, State};
+decode(list, <<$z,Rest/binary>>, List, State) -> {Rest, List, State};
+decode(list, Bin, List, State) ->
+ case decode(Bin, State) of
+ {error, Encoded} ->
+ {error, Encoded};
+ {Rest, {type_def,_,_,_}, _State} ->
+ decode(list, Rest, List, _State);
+ {not_found,Hash} ->
+ {not_found, Hash};
+ {Rest, {not_found, Hash}, _State} ->
+ {not_found, Hash};
+ {Rest, Value, _State} ->
+ ?VALUE(Value),
+ decode(list, Rest, List ++ [Value], _State)
+ end;
+decode(type_definition, ForeignType, Bin, State) ->
+ {Rest,Count, _State} = decode(Bin, State),
+ {NewRest,FieldNames, NewState} = decode(field, Rest, Count, [], _State),
+ TypeDef = type_decoding:build_foreign_view(ForeignType,FieldNames,NewState),
+ {NewRest, TypeDef, NewState}.
+decode(field, <<$z,Rest/binary>>, Count, Acc, State) -> {Rest, Acc, State};
+decode(field, Rest, 0, Acc, State) -> {Rest, Acc, State};
+decode(field, Bin, Count, Acc, State) ->
+ {Rest,Field, _State} = decode(Bin, State),
+ case Field of
+ {not_found,Hash} ->
+ {not_found,Hash};
+ {type_def,_,_,_} ->
+ {_Rest, Object, __State} = decode(Rest, _State),
+ decode(field, _Rest, Count - 1, Acc ++ [Object], __State);
+ _ ->
+ decode(field, Rest, Count - 1, Acc ++ [Field], _State)
+ end.
+
+%---------------------------------------------------------------------------
+% Encoding
+%---------------------------------------------------------------------------
+
+encode(type_query, Hash) ->
+ ?START(q,[?M,?m]),
+ ?VALUE(<<Hash:32>>),
+ <<$q,?M,?m,Hash:32/unsigned,$z>>;
+encode(type_query_ok, Hash) -> <<$y,?M,?m,Hash:32/unsigned,$z>>;
+encode(type_information, TypeDef = #type_def{fieldnames = FieldNames,
+ foreign_type = ForeignType}) ->
+ Size = size(ForeignType),
+ Count = count_fields(TypeDef),
+ Int = encode(int, Count, []),
+ _FieldNames = lists:map(fun erlang_to_camel_case/1,FieldNames),
+ {AccOut, _} = lists:foldl(fun encode/2,{<<>>, []},_FieldNames),
+ Hash = erlang:phash2(TypeDef),
+ <<$O,Hash:32/unsigned,$t,Size:16/unsigned,ForeignType/binary,Int/binary,AccOut/binary,$z>>;
+
+encode(Value, {Acc,State}) ->
+ case encode(Value, State) of
+ {ValueBin, _State} ->
+ {<<Acc/binary,ValueBin/binary>>,_State};
+ ValueBin ->
+ {<<Acc/binary,ValueBin/binary>>,State}
+ end;
+
+% Null
+encode(undefined, State) -> <<$N>>;
+
+encode(Value, State) when is_integer(Value) -> encode(int, Value, State);
+encode(Value, State) when is_float(Value) -> encode(double, Value, State);
+encode(Value, State) when is_boolean(Value) -> encode(boolean, Value, State);
+encode(Value, State) when is_atom(Value) -> encode(string, atom_to_list(Value), State);
+encode(Value, State) when is_list(Value) -> encode(list, Value, State);
+encode(Value, State) when is_pid(Value) -> throw("Erlang Pid encoding not supported");
+%% Assume that a binary is a string
+%% TODO what about encapsulating binary-data????
+encode(Value, State) when is_binary(Value) -> encode(string, Value, State);
+%% TODO The order of this is_tuple guard worries me a bit, needs further attention.
+encode(Value, State) when is_tuple(Value) -> encode(object, Value, State).
+
+% x20 # zero-length binary data
+% x23 x01 x02 x03 # 3 octet data
+% B x10 x00 .... # 4k final chunk of data
+% b x04 x00 .... # 1k non-final chunk of data
+encode(binary, <<>>, State) -> <<16#20>>;
+encode(binary, Value, State) when size(Value) < 15 ->
+ Size = 16#20 + size(Value),
+ <<Size:8/unsigned,Value/binary>>;
+encode(binary, Value, State) -> encode(binary, Value, <<>>, State);
+encode(boolean, true, State) -> <<$T>>;
+encode(boolean, false, State) -> <<$F>>;
+encode(timestamp, {MegaSecs, Secs, MicroSecs}, State) ->
+ Date = MegaSecs * ?MegaSeconds + Secs * ?Seconds + MicroSecs div ?MicroSeconds,
+ <<$d,Date:64/unsigned>>;
+encode(localtime, DateTime={{Year,Month,Day},{Hour,Min,Sec}}, State) ->
+ [Universal] = calendar:local_time_to_universal_time_dst(DateTime),
+ Seconds = calendar:datetime_to_gregorian_seconds(Universal),
+ MilliSeconds = (Seconds - ?UnixEpoch) * 1000,
+ <<$d,MilliSeconds:64/unsigned>>;
+encode(double, 0.0, State) -> <<16#67>>;
+encode(double, 1.0, State) -> <<16#68>>;
+encode(double, Double, State) when Double >= -128.0, Double =< 127.0, Double == round(Double) ->
+ Byte = round(Double),
+ <<16#69, Byte/signed>>;
+encode(double, Double, State) when Double >= -32768.0, Double =< 32767.0, Double == round(Double) ->
+ Byte = round(Double),
+ <<16#6a, Byte:16/signed>>;
+encode(double, Double, State) ->
+ case <<Double/float>> of
+ <<B24,B16,B8,B0,0,0,0,0>> ->
+ <<16#6b,B24,B16,B8,B0>>;
+ Other ->
+ <<$D,Other/binary>>
+ end;
+encode(int, Int, State) when Int >= -16, Int =< 47 ->
+ _Int = Int + 16#90,
+ <<_Int:8>>;
+encode(int, Int, State) when Int >= -2048, Int =< 2047 ->
+ <<B1:8,B0:8>> = <<Int:16>>,
+ _B1 = B1 + 16#c8,
+ <<_B1,B0>>;
+encode(int, Int, State) when Int >= -262144, Int =< 262143 ->
+ <<B2:8,B1:8,B0:8>> = <<Int:24>>,
+ _B2 = B2 + 16#d4,
+ <<_B2,B1,B0>>;
+encode(int, Int, State) when Int > 16#100000000 -> <<$L,Int:64/unsigned>>;
+encode(int, Int, State) -> <<$I,Int:32/unsigned>>;
+encode(long, Long, State) when Long >= -8, Long =< 15 ->
+ _Long = Long + 16#e0,
+ <<_Long:8>>;
+encode(long, Long, State) when Long >= -2048, Long =< 2047 ->
+ <<B1:8,B0:8>> = <<Long:16>>,
+ _B1 = B1 + 16#f8,
+ <<_B1,B0>>;
+encode(long, Long, State) when Long >= -262144, Long =< 262143 ->
+ <<B2:8,B1:8,B0:8>> = <<Long:24>>,
+ _B2 = B2 + 16#3c,
+ <<_B2,B1,B0>>;
+encode(long, Long, State) when Long >= -16#100000000, Long =< 16#100000000 -> <<16#77,Long:32>>;
+encode(long, Long, State) -> <<$L,Long:64/unsigned>>;
+encode(string, <<>>, State) -> <<0>>;
+encode(string, [], State) -> <<0>>;
+encode(string, String, State) when is_binary(String)-> encode(string, binary_to_list(String), State);
+encode(string, String, State) when is_list(String)->
+ UTF8 = case catch xmerl_ucs:is_incharset(String,'utf-8') of
+ true ->
+ String;
+ _ ->
+ xmerl_ucs:to_utf8(String)
+ end,
+ %% There is a question pending on the hessian list as to whether the length
+ %% refers to the UTF-8 or the native length
+ %Length = length(String),
+ Length = length(UTF8),
+ if
+ Length < 32 ->
+ Bin = list_to_binary(UTF8),
+ <<Length:8,Bin/binary>>;
+ true ->
+ encode(string, list_to_binary(String), <<>>, State)
+ end;
+encode(dictionary, Dict, State) ->
+ Encoder = fun(Key, Value, AccIn) ->
+ KeyBin = encode(Key, State),
+ ValueBin = encode(Value, State),
+ <<AccIn/binary,KeyBin/binary,ValueBin/binary>> end,
+ AccOut = dict:fold(Encoder, <<$M>>, Dict),
+ <<AccOut/binary,$z>>;
+%% Length
+%::= 'l' b3 b2 b1 b0
+%::= x6e int
+encode(length, List, State) ->
+ Length = length(List),
+ case encode(int, Length, State) of
+ <<$I,Int/binary>> ->
+ <<$l,Int/binary>>;
+ Other ->
+ <<16#6e,Other/binary>>
+ end;
+%% Lists
+encode(list, List, State) ->
+ Length = encode(length, List, State),
+ encode(list, List, <<$V,Length/binary>>, State);
+encode(method, Method, State) when is_atom(Method) ->
+ String = atom_to_list(Method),
+ encode(method, String, State);
+encode(method, Method, State) when is_binary(Method) ->
+ _Method = erlang_to_camel_case(Method),
+ Size = size(_Method),
+ <<$m,Size:16/unsigned,_Method/binary>>;
+%---------------------------------------------------
+% TODO I don't think this ever gets called
+encode(method, String, State) when is_list(String) ->
+ _String = erlang_to_camel_case(String),
+ Length = string:len(_String),
+ Bin = list_to_binary(_String),
+ <<$m,Length:16/unsigned,Bin/binary>>;
+%---------------------------------------------------
+encode(type, FullyQualifiedName, State) when is_list(FullyQualifiedName) ->
+ Bin = list_to_binary(FullyQualifiedName),
+ encode(type,Bin, State);
+encode(type, FullyQualifiedName, State) when is_binary(FullyQualifiedName) ->
+ Length = size(FullyQualifiedName),
+ <<$t,Length:16,FullyQualifiedName/binary>>;
+%% TODO implement header
+%% reply ::= r x01 x00 header* object z
+%% ::= r x01 x00 header* fault z
+encode(reply, ok, State) -> <<$r,?M,?m,$N,$z>>;
+encode(reply, {ok, Object}, State) -> encode(reply, Object, State);
+encode(reply, {error, {Error, Reason} }, State) -> encode(fault, Error, Reason, State);
+encode(reply, Object, State) ->
+ ?START(r,[?M,?m]),
+ Bin = case encode(Object, State) of
+ {_Bin, NewState} ->
+ _Bin;
+ _Bin ->
+ _Bin
+ end,
+ <<$r,?M,?m,Bin/binary,$z>>;
+encode(object, DateTime = {MegaSecs, Secs, MicroSecs}, State)
+ when is_integer(MegaSecs), is_integer(Secs), is_integer(MicroSecs)->
+ ?LOG("Employing date heuristic"),
+ encode(timestamp,DateTime,State);
+encode(object, Object, State) when is_tuple(Object) ->
+ [NativeType|Values] = tuple_to_list(Object),
+ {TypeEncoding, EncodedRef, NewState} =
+ case type_encoding:visit(NativeType,State) of
+ {ref, Ref} ->
+ encode(type_information, {ref, Ref}, State);
+ {hash, Hash, Ref, State0} ->
+ encode(type_information, {hash, Hash, Ref}, State0)
+ end,
+ {AccOut, _NewState} = lists:foldl(fun encode/2,{<<>>, NewState},Values),
+ {<<TypeEncoding/binary,$o,EncodedRef/binary,AccOut/binary>>, _NewState};
+encode(type_information, {ref, Ref}, State) ->
+ {<<>>, encode(int, Ref, State), State};
+encode(type_information, {hash, Hash, Ref}, State) ->
+ EncodedRef = encode(int, Ref, State),
+ {<<$O,Hash:32/unsigned>>, EncodedRef, State}.
+encode(binary, Value, <<>>, State) when size(Value) =< ?CHUNK_SIZE ->
+ Size = size(Value),
+ <<$B,Size:16,Value/binary>>;
+encode(binary, Value, Acc, State) when size(Value) =< ?CHUNK_SIZE ->
+ Size = size(Value),
+ <<Acc/binary,$B,Size:16,Value/binary>>;
+encode(binary, Value, <<>>, State) ->
+ <<Chunk:?CHUNK_SIZE/binary,Rest/binary>> = Value,
+ encode(binary, Rest, <<$b,?CHUNK_SIZE:16,Chunk/binary>>, State);
+encode(binary, Value, Acc, State) ->
+ <<Chunk:?CHUNK_SIZE/binary,Rest/binary>> = Value,
+ encode(binary, Rest, <<Acc/binary,$b,?CHUNK_SIZE:16,Chunk/binary>>, State);
+encode(string, Value, Acc, State) when size(Value) =< ?CHUNK_SIZE ->
+ Size = size(Value),
+ <<Acc/binary,$S,Size:16,Value/binary>>;
+encode(string, Value, <<>>, State) ->
+ <<Chunk:?CHUNK_SIZE/binary,Rest/binary>> = Value,
+ encode(string, Rest, <<$s,?CHUNK_SIZE:16,Chunk/binary>>, State);
+encode(string, Value, Acc, State) ->
+ <<Chunk:?CHUNK_SIZE/binary,Rest/binary>> = Value,
+ encode(string, Rest, <<Acc/binary,$s,?CHUNK_SIZE:16,Chunk/binary>>, State);
+%---------------------------------------------------------------------------
+% List Encoding
+%---------------------------------------------------------------------------
+encode(list, Type, List, State) when is_binary(Type) ->
+ TypeLength = size(Type),
+ ListLength = length(List),
+ encode(list, List, <<$V,$t,TypeLength:16/unsigned,Type/binary,$l,ListLength:32/unsigned>>, State);
+encode(list, List, Acc0, State) when is_binary(Acc0) ->
+ {AccOut, State} = lists:foldl(fun encode_accumulate/2, {Acc0,State}, List),
+ <<AccOut/binary,$z>>;
+%---------------------------------------------------------------------------
+% Call and Reply Encoding
+%---------------------------------------------------------------------------
+%% TODO implement header call ::= c x01 x00 header* method object* z
+encode(call, Method, Args, State) ->
+ encode(call, Method, Args, fun encode_accumulate/2, State);
+encode(fault, _Error, _Reason, State) ->
+ encode(fault, <<"ServiceException">>, _Error, _Reason, State).
+encode(fault, Code, _Error, _Reason, State) ->
+ EncodedCode = encode(string,Code, State),
+ <<131,100,L2:16/unsigned,Error/binary>> = term_to_binary(_Error),
+ EncodedError = encode(string,Error, State),
+ <<$r,?M,?m,$f,
+ 4,"code",EncodedCode/binary,
+ 7,"message",EncodedError/binary,
+ 6,"detail",31,"Stack trace not yet implemented",
+ $z>>;
+encode(call, Method, Args, Fun, State) when is_function(Fun) ->
+ MethodBin = encode(method,Method, State),
+ {Bin, NewState} = lists:foldl(Fun, {<<>>, State}, Args),
+ <<$c,?M,?m,MethodBin/binary,Bin/binary,$z>>.
+
+%---------------------------------------------------------------------------
+% Invocation
+%---------------------------------------------------------------------------
+
+invoke(Module,Bin,DecodingState,EncodingState) ->
+ case decode(Bin,DecodingState) of
+ {error, Reason} ->
+ encode(fault, decoding_error, Reason, EncodingState);
+ {not_found, Hash} ->
+ encode(type_query,Hash);
+ {peer_cannot_resolve,Hash} ->
+ %% TODO what should happen here? Is this good enough?
+ encode(reply,ok,[]);
+ {type_query_ack, Hash} ->
+ %% TODO This has not been implemented yet
+ ok;
+ %% Don't know if I like this weakly typed differentiation,
+ %% it seems a bit brittle
+ {_,{Hash,TypeDef},_} ->
+ encode(type_query_ok, Hash);
+ %% TODO look into whether this 2nd branch for type_query_ok is needed,
+ %% or whether to refactor the corresponding return value from the
+ %% decode function
+ {_,TypeDef,_} ->
+ encode(type_information, TypeDef);
+ [Function,Args] ->
+ Result = apply(Module,Function,Args),
+ encode(reply,Result,EncodingState)
+ end.
+
+%% This is used for synchronous transports that can respond
+%% to a type query
+call(Dispatcher,Method,Args,DecodingState,EncodingState) ->
+ Bin = encode(call,Method,Args,EncodingState),
+ Response0 = apply(Dispatcher,[Bin]),
+ case decode(Response0,DecodingState) of
+ {Hash,TypeDef,_} ->
+ ok = send_type_negotiation(Dispatcher,TypeDef,DecodingState),
+ Response = apply(Dispatcher,[Bin]),
+ decode(Response,DecodingState);
+ Other ->
+ Other
+ end.
+
+send_type_negotiation(Dispatcher,TypeDef,DecodingState) ->
+ TypeDefBin = hessian:encode(type_information, TypeDef),
+ Response = apply(Dispatcher,[TypeDefBin]),
+ case hessian:decode(Response,DecodingState) of
+ ok ->
+ ok;
+ Error ->
+ throw({type_negotiation_error,Error})
+ end.
+
+%---------------------------------------------------------------------------
+% Utility methods
+%---------------------------------------------------------------------------
+erlang_to_camel_case(String) when is_binary(String) ->
+ AsList = binary_to_list(String),
+ AsCamel = lists:foldl(fun camelize/2,[],AsList),
+ list_to_binary(AsCamel);
+erlang_to_camel_case(String) when is_atom(String) ->
+ AsList = atom_to_list(String),
+ AsCamel = lists:foldl(fun camelize/2,[],AsList),
+ list_to_binary(AsCamel).
+
+camelize(Element,Acc) when Element == $_ -> [$_|Acc];
+camelize(Element,[$_|Acc]) -> lists:append(Acc,[Element - 16#20]);
+camelize(Element,Acc) -> lists:append(Acc,[Element]).
+
+
+%% May want to take another look at this:
+%% All of the primitive encoding functions do not return the state.
+%% Maybe they should to avoid differentiations like this.
+encode_accumulate(Value, {Acc, State}) ->
+ case encode(Value, State) of
+ {Encoded,NewState} ->
+ {<<Acc/binary,Encoded/binary>>,NewState};
+ Encoded ->
+ {<<Acc/binary,Encoded/binary>>,State}
+ end.
+
+is_type_def(#type_def{}) -> true;
+is_type_def(_) -> false.
+
+count_fields(#type_def{fieldnames = FieldNames}) -> length(FieldNames).
diff --git a/src/hessianDecode.erl b/src/hessianDecode.erl
new file mode 100755
index 0000000..adc06fe
--- /dev/null
+++ b/src/hessianDecode.erl
@@ -0,0 +1,347 @@
+-module(hessianDecode).
+
+-include("hessian.hrl").
+
+-export([decode/2]).
+%%-export([init/0]).
+
+%%-record(decoding_state,{type_pool = dict:new(), reference_pool = dict:new()}).
+-record(decoding_state,{type_pool = dict:new(), reference_pool = dict:new(),obj_define_no=0}).
+%---------------------------------------------------------------------------
+% Decoding
+%---------------------------------------------------------------------------
+decode_str(Rest, 0, R) ->
+ {list_to_binary(xmerl_ucs:to_utf8(lists:reverse(R))), Rest};
+decode_str(<<C/utf8, Rest/binary>>, N, R) ->
+ decode_str(Rest, N-1, [C|R]).
+
+% Call
+decode(<<$H,?M,?m,$C,Bin/binary>>, State) ->
+ {Rest, Function, _State} = decode(Bin,State),
+ {Rest2, Count, __State} = decode(Rest,_State),
+ case decode(list, Rest2,Count,[], __State) of
+ {error, Encoded} ->
+ {error, Encoded};
+ {not_found, Hash} ->
+ {not_found, Hash};
+ {_Rest, Arguments, _NewState} ->
+ [Function, Arguments]
+ end;
+% Fault
+decode(<<$H,?M,?m,$F,
+ Rest/binary>>, State) ->
+ {_Rest,#map{dict=Dict},_State} = decode(Rest, State),
+ {ok, Message} = dict:find(<<"message">>, Dict),
+ {error, Message};
+% Reply
+decode(<<$H,?M,?m,$R,Args/binary>>, State) ->
+ case decode(Args,[], State) of
+ {<<>>, Decoded,_State} ->
+ TypeSet = get_type_set(_State),
+ case Decoded of
+ [Value] ->
+ {Value,TypeSet};
+ [H|T] ->
+ {[H|T],TypeSet}
+ end;
+ {error, Encoded} ->
+ {error, Encoded}
+ end;
+% Binaries
+decode(<<16#20,Rest/binary>>, State) -> {Rest, <<>>, State};
+decode(<<Len:8/unsigned,Rest/binary>>, State) when Len =< 16#2f, 16#20 < Len ->
+ _Len = Len - 16#20,
+ <<Bin:_Len/binary,_Rest/binary>> = Rest,
+ {_Rest, Bin, State};
+decode(<<$B,Len:16/unsigned,Bin:Len/binary,Rest/binary>>, State) -> {Rest, Bin, State};
+decode(<<$b,Rest/binary>>,State) -> decode(<<$b,Rest/binary>>, [], State);
+%% Booleans
+decode(<<$T,Rest/binary>>, State) -> {Rest, true, State};
+decode(<<$F,Rest/binary>>, State) -> {Rest, false, State};
+%% Dates
+decode(<<16#4a,Date:64/unsigned,Rest/binary>>, State) ->
+ MegaSecs = Date div ?MegaSeconds,
+ Secs = (Date - MegaSecs * ?MegaSeconds) div ?Seconds,
+ MicroSecs = (Date - MegaSecs * ?MegaSeconds - Secs * ?Seconds) * ?MicroSeconds,
+ {Rest, {MegaSecs, Secs, MicroSecs}, State};
+decode(<<16#4b,Date:32/unsigned,Rest/binary>>, State) ->
+ MegaSecs = (Date * 60000) div ?MegaSeconds,
+ Secs = (Date * 60000 - MegaSecs * ?MegaSeconds) div ?Seconds,
+ {Rest, {MegaSecs, Secs, 0}, State};
+%% Doubles
+decode(<<16#5b,Rest/binary>>, State)-> {Rest, 0.0, State};
+decode(<<16#5c,Rest/binary>>, State)-> {Rest, 1.0, State};
+decode(<<16#5d,Int:8/signed,Rest/binary>>, State)-> {Rest, float(Int), State};
+decode(<<16#5e,Int:16/signed,Rest/binary>>, State)-> {Rest, float(Int), State};
+decode(<<16#5f,Int:32/signed,Rest/binary>>, State)->
+ <<Double:64/float>> = <<Int:32,0,0,0,0>>,
+ {Rest, Double, State};
+decode(<<$D,Double:64/float,Rest/binary>>, State)-> {Rest, Double, State};
+%% Ints
+decode(<<$I,Int:32/signed,Rest/binary>>, State)-> {Rest, Int, State};
+decode(<<Int:8,Rest/binary>>, State) when Int >= 16#80, Int =< 16#bf -> {Rest, Int - 16#90, State};
+decode(<<B2:8,B1:8,B0:8,Rest/binary>>, State) when B2 >= 16#d0, B2 =< 16#d7 -> {Rest, ((B2 - 16#d4) bsl 16) + (B1 bsl 8) + B0, State};
+decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 >= 16#c0, B1 =< 16#cf -> {Rest, ((B1 - 16#c8) bsl 8) + B0, State};
+%% Longs
+decode(<<$L,Long:64/signed,Rest/binary>>, State)-> {Rest, Long, State};
+decode(<<16#59,Long:32/signed,Rest/binary>>, State) -> {Rest, Long, State};
+decode(<<Long:8,Rest/binary>>, State) when Long >= 16#d8, Long =< 16#ef -> {Rest, Long - 16#e0, State};
+decode(<<B2:8,B1:8,B0:8,Rest/binary>>, State) when B2 >= 16#38, B2 =< 16#3f -> {Rest, ((B2 - 16#3c) bsl 16) + (B1 bsl 8) + B0, State};
+decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 >= 16#f0, B1 =< 16#ff -> {Rest, ((B1 - 16#f8) bsl 8) + B0, State};
+%% Strings
+decode(<<0,Rest/binary>>, State) -> {Rest, <<>>, State};
+decode(<<Len:8,Rest/binary>>, State) when Len < 32 ->
+ {String, NewRest} = decode_str(Rest, Len, []),
+ {NewRest, String, State};
+decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 =< 16#33,B1 >= 16#30 ->
+ Len = ((B1 - 16#30) bsl 8) + B0,
+ {String, NewRest} = decode_str(Rest, Len, []),
+ {NewRest, String, State};
+decode(<<$S,Len:16/unsigned,Rest/binary>>, State) ->
+ {String, NewRest} = decode_str(Rest, Len, []),
+ {NewRest, String, State};
+decode(<<$R,Rest/binary>>, State) -> decode(<<$R,Rest/binary>>,[], State);
+%% Nulls
+decode(<<$N,Rest/binary>>, State) -> {Rest, undefined, State};
+%% References
+decode(<<$Q,Bin/binary>>, State)->
+ {Rest, IntRef, _State} = decode(Bin,State),
+ {Rest, {ref, IntRef}, _State};
+%% Maps
+decode(<<$M,Map/binary>>, State) ->
+ {Rest,Type,_State} = decode(Map, State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = map},_State),
+ {_Rest,Dict, NewState} = decode(map, Rest, dict:new(), __State),
+ {_Rest,#map{refNo=_Refnum,type=Type,dict=Dict},NewState};
+decode(<<$H,Map/binary>>, State) ->
+ {_Refnum,_State} = visit(#type_def{foreign_type = map},State),
+ {_Rest,Dict, NewState} = decode(map, Map, dict:new(), _State),
+ {_Rest,#map{refNo=_Refnum,dict=Dict},NewState};
+%% Lists
+% 'V' type int value* # fixed-length list
+decode(<<$V,Bin/binary>>, State) ->
+ {Rest1,_Type,_State1} = decode(Bin,State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = list},_State1),
+ {Rest2,Len,_State2} = decode(Rest1,__State),
+ {NewRest,List,NewState} = decode(list, Rest2, Len, [], _State2),
+ {NewRest,#list{refNo=_Refnum,len=Len,type=_Type,values=List},NewState};
+% 'X' int value* # fixed-length untyped list
+decode(<<$X,Bin/binary>>, State) ->
+ {Rest1,Len,_State} = decode(Bin,State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
+ {NewRest,List,NewState} = decode(list, Rest1, Len, [], __State),
+ {NewRest,#list{refNo=_Refnum,len=Len,values=List},NewState};
+% [x70-77] type value* # fixed-length typed list
+decode(<<H:5,Len:3,Bin/binary>>, State) when H == 14 ->
+ {Rest1,_Type,_State} = decode(Bin,State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
+ {NewRest,List,NewState} = decode(list, Rest1, Len, [], __State),
+ {NewRest,#list{refNo=_Refnum,len=Len,type=_Type,values=List},NewState};
+% [x78-7f] value* # fixed-length untyped list
+decode(<<H:5,Len:3,Bin/binary>>, State) when H == 15 ->
+ {_Refnum,_State} = visit(#type_def{foreign_type = list},State),
+ {NewRest,List,NewState} = decode(list, Bin, Len, [], _State),
+ {NewRest,#list{refNo=_Refnum,len=Len,values=List},NewState};
+% 'U' type value* 'Z' # variable-length list
+decode(<<$U,Bin/binary>>, State) ->
+ {Rest1,_Type,_State} = decode(Bin,State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
+ {NewRest,List,NewState} = decode(list, Rest1, [], __State),
+ {NewRest,#list{refNo=_Refnum,type=_Type,values=List},NewState};
+% 'W' value* 'Z' # variable-length untyped list
+decode(<<$W,Bin/binary>>, State) ->
+ {_Refnum,_State} = visit(#type_def{foreign_type = list},State),
+ {NewRest,List,NewState} = decode(list, Bin, [], _State),
+ {NewRest,#list{refNo=_Refnum,values=List},NewState};
+decode(<<$C,Bin/binary>>, State0) ->
+ {Rest1,Type,State1} = decode(Bin,State0), %% Rest1 剩余字节码, Type 对象类型
+ {Rest, TypeDef, State} = decode(type_definition,Type,Rest1,State1),
+ NewState = type_decoding:hash_store(TypeDef,State),
+ decode(Rest,NewState);
+decode(<<$O,Bin/binary>>, State) ->
+ {Rest,Ref,_State} = decode(Bin, State),
+ case type_decoding:hash_lookup(Ref, _State) of
+ {not_found, Ref} ->
+ {Rest, {not_found, Ref}, _State};
+ ForeignView ->
+ #type_def{fieldnames = ForeignFieldNames} = ForeignView,
+ {_Refnum,__State} = type_decoding:visit(ForeignView,_State),
+ Count = count_fields(ForeignView),
+ case decode(field, Rest, Count,[], __State) of
+ {not_found,Hash} ->
+ {not_found,Hash};
+ {_Rest,FieldValues, NewState} ->
+ Object = type_decoding:project_native_view(ForeignFieldNames,FieldValues,ForeignView),
+%% {_Rest, #object{refNo=_Refnum, typeRef=Ref,values=Object}, NewState}
+ {_Rest, Object, NewState}
+ end
+ end;
+decode(<<H:4,Ref:4,Rest/binary>>, _State) when H == 6 ->
+ case type_decoding:hash_lookup(Ref, _State) of
+ {not_found, Ref} ->
+ {Rest, {not_found, Ref}, _State};
+ ForeignView ->
+ #type_def{fieldnames = ForeignFieldNames} = ForeignView,
+ {_Refnum,__State} = visit(ForeignView,_State),
+ Count = count_fields(ForeignView),
+ case decode(field, Rest, Count,[], __State) of
+ {not_found,Hash} ->
+ {not_found,Hash};
+ {_Rest,FieldValues, NewState} ->
+ Object = type_decoding:project_native_view(ForeignFieldNames,FieldValues,ForeignView),
+ %%{_Rest, #object{refNo =_Refnum, typeRef=Ref,values=Object}, NewState}
+ {_Rest, Object, NewState}
+ end
+ end;
+%% Anything else
+decode(<<Unexpected/binary>>, State) ->
+ {error, hessianEncode:encode(fault, <<"ProtocolException">>, unexpected_byte_sequence, Unexpected, State) }.
+decode(<<$b,Len:16/unsigned,Bin:Len/binary,$b,Rest/binary>>, Acc, State) ->
+ decode(<<$b,Rest/binary>>,Acc ++ [Bin], State);
+decode(<<$b,Len:16/unsigned,Bin:Len/binary,$B,Rest/binary>>, Acc, State) ->
+ _Acc = Acc ++ [Bin],
+ {_Rest,_Bin, State} = decode(<<$B,Rest/binary>>, State),
+ {_Rest, list_to_binary(_Acc ++ [_Bin]), State};
+decode(<<$R,Len:16/unsigned,Rest/binary>>,Acc, State) ->
+ {_String, NewRest} = decode_str(Rest, Len, []),
+ <<H:8,_/binary>> = NewRest,
+ case H of
+ $R -> decode(NewRest,list_to_binary([Acc|[_String]]), State);
+ $S -> {_Rest,_Bin, _State} = decode(NewRest, State),
+ _Acc2 =[Acc |[_String]],
+ {_Rest, list_to_binary(_Acc2 ++ [_Bin]), _State}
+ end;
+decode(<<>>, List, State) -> {<<>>, List, State};
+decode(<<$Z>>, List, State) -> {<<>>, List, State};
+decode(Args, List, State) ->
+ case decode(Args,State) of
+ {Rest,{ref, Ref}, _State} ->
+ decode(Rest, List ++ [{ref, Ref}] , _State);
+ {Rest, [H|T], _State} ->
+ decode(Rest, List ++ [H|T], _State);
+ {Rest, Result, _State} ->
+ decode(Rest, List ++ [Result], _State);
+ {error, Encoded} ->
+ {error, Encoded}
+ end.
+decode(map, <<$Z>>, Dict, State) -> {<<>>,Dict, State};
+decode(map, <<$Z,Rest/binary>>, Dict, State) -> {Rest,Dict, State};
+decode(map, Bin, Dict, State) ->
+ {_Rest, Key, _State} = decode(Bin, State),
+ case decode(_Rest, _State) of
+ {Rest, {ref, Ref}, __State} ->
+ %Value = lists:nth(Ref + 1, List),
+ Value = Ref,
+ decode(map, Rest, dict:store(Key, {ref, Value}, Dict), __State);
+ {Rest, Value, __State} ->
+ decode(map, Rest, dict:store(Key, Value, Dict), __State)
+ end;
+decode(list, <<>>, List, State) -> {<<>>,lists:reverse(List), State};
+decode(list, <<$Z>>, List, State) -> {<<>>,lists:reverse(List), State};
+decode(list, <<$Z,Rest/binary>>, List, State) -> {Rest, lists:reverse(List), State};
+decode(list, Bin, List, State) ->
+ case decode(Bin, State) of
+ {error, Encoded} ->
+ {error, Encoded};
+ {not_found,Hash} ->
+ {not_found, Hash};
+ {_Rest, {not_found, Hash}, _State} ->
+ {not_found, Hash};
+ {Rest, Value, _State} ->
+ decode(list, Rest, [Value|List], _State)
+ end;
+decode(type_definition, ForeignType, Bin, State) ->
+ {Rest,Count, _State} = decode(Bin, State),
+ {NewRest,FieldNames, NewState} = decode(field, Rest, Count, [], _State),
+ %% change 用回之前的 build_foreign_view NewState 不变
+ {TypeDef,NewState2} = type_decoding:build_foreign_view(ForeignType,FieldNames,NewState),
+ {NewRest, TypeDef, NewState2}.
+decode(list, Bin, 0, List, State) -> {Bin, lists:reverse(List), State};
+decode(list, Bin, Len, List, State) ->
+ case decode(Bin, State) of
+ {error, Encoded} ->
+ {error, Encoded};
+ {not_found,Hash} ->
+ {not_found, Hash};
+ {_Rest, {not_found, Hash}, _State} ->
+ {not_found, Hash};
+ {Rest, Value, _State} ->
+ decode(list, Rest, Len - 1, [Value|List], _State)
+ end;
+decode(field, Rest, 0, Acc, State) -> {Rest, Acc, State};
+decode(field, <<$Z,Rest/binary>>, _Count, Acc, State) -> {Rest, Acc, State};
+decode(field, Bin, Count, Acc, State) ->
+ {Rest,Field, _State} = decode(Bin, State),
+ case Field of
+ {not_found,Hash} ->
+ {not_found,Hash};
+ _ ->
+ decode(field, Rest, Count - 1, Acc ++ [Field], _State)
+ end.
+
+get_type_set(#decoding_state{type_pool = Pool}) ->
+ case dict:to_list(Pool) of
+ [] -> [];
+ Data -> [Value || {_Key,Value} <- Data]
+ end.
+
+build_foreign_view(ForeignType,FieldNames,State) ->
+ ForeignView = FieldNames,
+ #decoding_state{type_pool = OldPool} = State,
+ Native = dict:size(OldPool),
+ NewPool = dict:store(Native,
+ #type_def{native_type = Native,
+ foreign_type = ForeignType,
+ fieldnames = ForeignView},
+ OldPool),
+ {
+ #type_def{native_type = Native,
+ foreign_type = ForeignType,
+ fieldnames = ForeignView},
+ State#decoding_state{type_pool = NewPool}
+ }.
+
+project_native_view(ForeignView,ForeignData,
+ #type_def{native_type = _NativeType, foreign_type = _ForeignType, fieldnames = NativeView}) ->
+ AsDict = dict:from_list(lists:zip(ForeignView,ForeignData)),
+ NativeData = [dict:fetch(Key,AsDict) || Key <- NativeView],
+ NativeData.
+
+visit(TypeDef, State = #decoding_state{reference_pool = OldPool}) ->
+ Size = dict:size(OldPool),
+ NewPool = dict:store(Size, TypeDef, OldPool),
+ {Size,State#decoding_state{reference_pool = NewPool}}.
+
+%%hash_lookup(Hash,_State) ->
+%% init(false),
+%% case ets:lookup(hashes, Hash) of
+%% [] ->
+%% {not_found, Hash};
+%% [{Hash,TypeDef}] ->
+%% TypeDef
+%% end.
+
+%%hash_store(TypeDef = #type_def{native_type = Hash}, State) ->
+%% init(false),
+%% ets:insert(hashes,{Hash,TypeDef}),
+%% State.
+%%
+%%init() -> init(true).
+%%
+%%init(Delete) when is_boolean(Delete) ->
+%% case ets:info(hashes) of
+%% undefined ->
+%% ets:new(hashes,[public,named_table]);
+%% _ ->
+%% if
+%% Delete ->
+%% ets:delete(hashes),
+%% ets:new(hashes,[public,named_table]);
+%% true ->
+%% ok
+%% end
+%% end,
+%% #decoding_state{}.
+
+count_fields(#type_def{fieldnames = FieldNames}) -> length(FieldNames).
+
diff --git a/src/hessianDecode2.erl b/src/hessianDecode2.erl
new file mode 100644
index 0000000..93c8173
--- /dev/null
+++ b/src/hessianDecode2.erl
@@ -0,0 +1,361 @@
+-module(hessianDecode2).
+
+-include("hessian.hrl").
+
+-export([decode/2]).
+-export([init/0]).
+-export([get_deftype/2]).
+-record(decoding_state,{type_pool = dict:new(), reference_pool = dict:new(),hash_pool = dict:new()}).
+
+%---------------------------------------------------------------------------
+% Decoding
+%---------------------------------------------------------------------------
+decode_str(Rest, 0, R) ->
+ {list_to_binary(xmerl_ucs:to_utf8(lists:reverse(R))), Rest};
+decode_str(<<C/utf8, Rest/binary>>, N, R) ->
+ decode_str(Rest, N-1, [C|R]).
+
+% Call
+decode(<<$H,?M,?m,$C,Bin/binary>>, State) ->
+ {Rest, Function, _State} = decode(Bin,State),
+ {Rest2, Count, __State} = decode(Rest,_State),
+ case decode(list, Rest2,Count,[], __State) of
+ {error, Encoded} ->
+ {error, Encoded};
+ {not_found, Hash} ->
+ {not_found, Hash};
+ {_Rest, Arguments, _NewState} ->
+ [Function, Arguments]
+ end;
+% Fault
+decode(<<$H,?M,?m,$F,
+ Rest/binary>>, State) ->
+ {_Rest,#map{dict=Dict},_State} = decode(Rest, State),
+ {ok, Message} = dict:find(<<"message">>, Dict),
+ {error, Message};
+% Reply
+decode(<<$H,?M,?m,$R,Args/binary>>, State) ->
+ case decode(Args,[], State) of
+ {<<>>, Decoded,_State} ->
+ TypeSet = get_type_set(_State),
+ case Decoded of
+ [Value] ->
+ {Value,TypeSet};
+ [H|T] ->
+ {[H|T],TypeSet}
+ end;
+ {error, Encoded} ->
+ {error, Encoded}
+ end;
+% Binaries
+decode(<<16#20,Rest/binary>>, State) -> {Rest, <<>>, State};
+decode(<<Len:8/unsigned,Rest/binary>>, State) when Len =< 16#2f, 16#20 < Len ->
+ _Len = Len - 16#20,
+ <<Bin:_Len/binary,_Rest/binary>> = Rest,
+ {_Rest, Bin, State};
+decode(<<$B,Len:16/unsigned,Bin:Len/binary,Rest/binary>>, State) -> {Rest, Bin, State};
+decode(<<$b,Rest/binary>>,State) -> decode(<<$b,Rest/binary>>, [], State);
+%% Booleans
+decode(<<$T,Rest/binary>>, State) -> {Rest, true, State};
+decode(<<$F,Rest/binary>>, State) -> {Rest, false, State};
+%% Dates
+decode(<<16#4a,Date:64/unsigned,Rest/binary>>, State) ->
+ MegaSecs = Date div ?MegaSeconds,
+ Secs = (Date - MegaSecs * ?MegaSeconds) div ?Seconds,
+ MicroSecs = (Date - MegaSecs * ?MegaSeconds - Secs * ?Seconds) * ?MicroSeconds,
+ {Rest, {MegaSecs, Secs, MicroSecs}, State};
+decode(<<16#4b,Date:32/unsigned,Rest/binary>>, State) ->
+ MegaSecs = (Date * 60000) div ?MegaSeconds,
+ Secs = (Date * 60000 - MegaSecs * ?MegaSeconds) div ?Seconds,
+ {Rest, {MegaSecs, Secs, 0}, State};
+%% Doubles
+decode(<<16#5b,Rest/binary>>, State)-> {Rest, 0.0, State};
+decode(<<16#5c,Rest/binary>>, State)-> {Rest, 1.0, State};
+decode(<<16#5d,Int:8/signed,Rest/binary>>, State)-> {Rest, float(Int), State};
+decode(<<16#5e,Int:16/signed,Rest/binary>>, State)-> {Rest, float(Int), State};
+decode(<<16#5f,Int:32/signed,Rest/binary>>, State)->
+ <<Double:64/float>> = <<Int:32,0,0,0,0>>,
+ {Rest, Double, State};
+decode(<<$D,Double:64/float,Rest/binary>>, State)-> {Rest, Double, State};
+%% Ints
+decode(<<$I,Int:32/signed,Rest/binary>>, State)-> {Rest, Int, State};
+decode(<<Int:8,Rest/binary>>, State) when Int >= 16#80, Int =< 16#bf -> {Rest, Int - 16#90, State};
+decode(<<B2:8,B1:8,B0:8,Rest/binary>>, State) when B2 >= 16#d0, B2 =< 16#d7 -> {Rest, ((B2 - 16#d4) bsl 16) + (B1 bsl 8) + B0, State};
+decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 >= 16#c0, B1 =< 16#cf -> {Rest, ((B1 - 16#c8) bsl 8) + B0, State};
+%% Longs
+decode(<<$L,Long:64/signed,Rest/binary>>, State)-> {Rest, Long, State};
+decode(<<16#59,Long:32/signed,Rest/binary>>, State) -> {Rest, Long, State};
+decode(<<Long:8,Rest/binary>>, State) when Long >= 16#d8, Long =< 16#ef -> {Rest, Long - 16#e0, State};
+decode(<<B2:8,B1:8,B0:8,Rest/binary>>, State) when B2 >= 16#38, B2 =< 16#3f -> {Rest, ((B2 - 16#3c) bsl 16) + (B1 bsl 8) + B0, State};
+decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 >= 16#f0, B1 =< 16#ff -> {Rest, ((B1 - 16#f8) bsl 8) + B0, State};
+%% Strings
+decode(<<0,Rest/binary>>, State) -> {Rest, <<>>, State};
+decode(<<Len:8,Rest/binary>>, State) when Len < 32 ->
+ {String, NewRest} = decode_str(Rest, Len, []),
+ {NewRest, String, State};
+decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 =< 16#33,B1 >= 16#30 ->
+ Len = ((B1 - 16#30) bsl 8) + B0,
+ {String, NewRest} = decode_str(Rest, Len, []),
+ {NewRest, String, State};
+decode(<<$S,Len:16/unsigned,Rest/binary>>, State) ->
+ {String, NewRest} = decode_str(Rest, Len, []),
+ {NewRest, String, State};
+decode(<<$R,Rest/binary>>, State) -> decode(<<$R,Rest/binary>>,[], State);
+%% Nulls
+decode(<<$N,Rest/binary>>, State) -> {Rest, undefined, State};
+%% References
+decode(<<$Q,Bin/binary>>, State)->
+ {Rest, IntRef, _State} = decode(Bin,State),
+ {Rest, {ref, IntRef}, _State};
+%% Maps
+decode(<<$M,Map/binary>>, State) ->
+ {Rest,Type,_State} = decode(Map, State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = map},_State),
+ {_Rest,Dict, NewState} = decode(map, Rest, dict:new(), __State),
+ {_Rest,#map{refNo=_Refnum,type=Type,dict=Dict},NewState};
+decode(<<$H,Map/binary>>, State) ->
+ {_Refnum,_State} = visit(#type_def{foreign_type = map},State),
+ {_Rest,Dict, NewState} = decode(map, Map, dict:new(), _State),
+ {_Rest,#map{refNo=_Refnum,dict=Dict},NewState};
+%% Lists
+% 'V' type int value* # fixed-length list
+decode(<<$V,Bin/binary>>, State) ->
+ {Rest1,_Type,_State1} = decode(Bin,State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = list},_State1),
+ {Rest2,Len,_State2} = decode(Rest1,__State),
+ {NewRest,List,NewState} = decode(list, Rest2, Len, [], _State2),
+ {NewRest,#list{refNo=_Refnum,len=Len,type=_Type,values=List},NewState};
+% 'X' int value* # fixed-length untyped list
+decode(<<$X,Bin/binary>>, State) ->
+ {Rest1,Len,_State} = decode(Bin,State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
+ {NewRest,List,NewState} = decode(list, Rest1, Len, [], __State),
+ {NewRest,#list{refNo=_Refnum,len=Len,values=List},NewState};
+% [x70-77] type value* # fixed-length typed list
+decode(<<H:5,Len:3,Bin/binary>>, State) when H == 14 ->
+ {Rest1,_Type,_State} = decode(Bin,State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
+ {NewRest,List,NewState} = decode(list, Rest1, Len, [], __State),
+ {NewRest,#list{refNo=_Refnum,len=Len,type=_Type,values=List},NewState};
+% [x78-7f] value* # fixed-length untyped list
+decode(<<H:5,Len:3,Bin/binary>>, State) when H == 15 ->
+ {_Refnum,_State} = visit(#type_def{foreign_type = list},State),
+ {NewRest,List,NewState} = decode(list, Bin, Len, [], _State),
+ {NewRest,#list{refNo=_Refnum,len=Len,values=List},NewState};
+% 'U' type value* 'Z' # variable-length list
+decode(<<$U,Bin/binary>>, State) ->
+ {Rest1,_Type,_State} = decode(Bin,State),
+ {_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
+ {NewRest,List,NewState} = decode(list, Rest1, [], __State),
+ {NewRest,#list{refNo=_Refnum,type=_Type,values=List},NewState};
+% 'W' value* 'Z' # variable-length untyped list
+decode(<<$W,Bin/binary>>, State) ->
+ {_Refnum,_State} = visit(#type_def{foreign_type = list},State),
+ {NewRest,List,NewState} = decode(list, Bin, [], _State),
+ {NewRest,#list{refNo=_Refnum,values=List},NewState};
+decode(<<$C,Bin/binary>>, State0) ->
+ {Rest1,Type,State1} = decode(Bin,State0),
+ {Rest, TypeDef, State} = decode(type_definition,Type,Rest1,State1),
+ NewState = hash_store(TypeDef,State),
+ decode(Rest,NewState);
+decode(<<$O,Bin/binary>>, State) ->
+ {Rest,Ref,_State} = decode(Bin, State),
+ case hash_lookup(Ref, _State) of
+ {not_found, Ref} ->
+ {Rest, {not_found, Ref}, _State};
+ ForeignView ->
+ #type_def{fieldnames = ForeignFieldNames} = ForeignView,
+ {_Refnum,__State} = visit(ForeignView,_State),
+ Count = count_fields(ForeignView),
+ case decode(field, Rest, Count,[], __State) of
+ {not_found,Hash} ->
+ {not_found,Hash};
+ {_Rest,FieldValues, NewState} ->
+ Object = project_native_view(ForeignFieldNames,FieldValues,ForeignView),
+ {_Rest, #object{refNo=_Refnum, typeRef=Ref,values=Object}, NewState}
+ end
+ end;
+decode(<<H:4,Ref:4,Rest/binary>>, _State) when H == 6 ->
+ lager:debug("decode data find ref ~p",[Ref]),
+ case hash_lookup(Ref, _State) of
+ {not_found, Ref} ->
+ {Rest, {not_found, Ref}, _State};
+ ForeignView ->
+ #type_def{fieldnames = ForeignFieldNames} = ForeignView,
+ {_Refnum,__State} = visit(ForeignView,_State),
+ Count = count_fields(ForeignView),
+ case decode(field, Rest, Count,[], __State) of
+ {not_found,Hash} ->
+ {not_found,Hash};
+ {_Rest,FieldValues, NewState} ->
+ Object = project_native_view(ForeignFieldNames,FieldValues,ForeignView),
+ {_Rest, #object{refNo=_Refnum, typeRef=Ref,values=Object}, NewState}
+ end
+ end;
+%% Anything else
+decode(<<Unexpected/binary>>, State) ->
+ {error, hessianEncode:encode(fault, <<"ProtocolException">>, unexpected_byte_sequence, Unexpected, State) }.
+decode(<<$b,Len:16/unsigned,Bin:Len/binary,$b,Rest/binary>>, Acc, State) ->
+ decode(<<$b,Rest/binary>>,Acc ++ [Bin], State);
+decode(<<$b,Len:16/unsigned,Bin:Len/binary,$B,Rest/binary>>, Acc, State) ->
+ _Acc = Acc ++ [Bin],
+ {_Rest,_Bin, State} = decode(<<$B,Rest/binary>>, State),
+ {_Rest, list_to_binary(_Acc ++ [_Bin]), State};
+decode(<<$R,Len:16/unsigned,Rest/binary>>,Acc, State) ->
+ {_String, NewRest} = decode_str(Rest, Len, []),
+ <<H:8,_/binary>> = NewRest,
+ case H of
+ $R -> decode(NewRest,list_to_binary([Acc|[_String]]), State);
+ $S -> {_Rest,_Bin, _State} = decode(NewRest, State),
+ _Acc2 =[Acc |[_String]],
+ {_Rest, list_to_binary(_Acc2 ++ [_Bin]), _State}
+ end;
+decode(<<>>, List, State) -> {<<>>, List, State};
+decode(<<$Z>>, List, State) -> {<<>>, List, State};
+decode(Args, List, State) ->
+ case decode(Args,State) of
+ {Rest,{ref, Ref}, _State} ->
+ decode(Rest, List ++ [{ref, Ref}] , _State);
+ {Rest, [H|T], _State} ->
+ decode(Rest, List ++ [H|T], _State);
+ {Rest, Result, _State} ->
+ decode(Rest, List ++ [Result], _State);
+ {error, Encoded} ->
+ {error, Encoded}
+ end.
+decode(map, <<$Z>>, Dict, State) -> {<<>>,Dict, State};
+decode(map, <<$Z,Rest/binary>>, Dict, State) -> {Rest,Dict, State};
+decode(map, Bin, Dict, State) ->
+ {_Rest, Key, _State} = decode(Bin, State),
+ case decode(_Rest, _State) of
+ {Rest, {ref, Ref}, __State} ->
+ %Value = lists:nth(Ref + 1, List),
+ Value = Ref,
+ decode(map, Rest, dict:store(Key, {ref, Value}, Dict), __State);
+ {Rest, Value, __State} ->
+ decode(map, Rest, dict:store(Key, Value, Dict), __State)
+ end;
+decode(list, <<>>, List, State) -> {<<>>,lists:reverse(List), State};
+decode(list, <<$Z>>, List, State) -> {<<>>,lists:reverse(List), State};
+decode(list, <<$Z,Rest/binary>>, List, State) -> {Rest, lists:reverse(List), State};
+decode(list, Bin, List, State) ->
+ case decode(Bin, State) of
+ {error, Encoded} ->
+ {error, Encoded};
+ {not_found,Hash} ->
+ {not_found, Hash};
+ {_Rest, {not_found, Hash}, _State} ->
+ {not_found, Hash};
+ {Rest, Value, _State} ->
+ decode(list, Rest, [Value|List], _State)
+ end;
+decode(type_definition, ForeignType, Bin, State) ->
+ {Rest,Count, _State} = decode(Bin, State),
+ {NewRest,FieldNames, NewState} = decode(field, Rest, Count, [], _State),
+ {TypeDef,NewState2} = build_foreign_view(ForeignType,FieldNames,NewState),
+ {NewRest, TypeDef, NewState2}.
+decode(list, Bin, 0, List, State) -> {Bin, lists:reverse(List), State};
+decode(list, Bin, Len, List, State) ->
+ case decode(Bin, State) of
+ {error, Encoded} ->
+ {error, Encoded};
+ {not_found,Hash} ->
+ {not_found, Hash};
+ {_Rest, {not_found, Hash}, _State} ->
+ {not_found, Hash};
+ {Rest, Value, _State} ->
+ decode(list, Rest, Len - 1, [Value|List], _State)
+ end;
+decode(field, Rest, 0, Acc, State) -> {Rest, Acc, State};
+decode(field, <<$Z,Rest/binary>>, _Count, Acc, State) -> {Rest, Acc, State};
+decode(field, Bin, Count, Acc, State) ->
+ {Rest,Field, _State} = decode(Bin, State),
+ case Field of
+ {not_found,Hash} ->
+ {not_found,Hash};
+ _ ->
+ decode(field, Rest, Count - 1, Acc ++ [Field], _State)
+ end.
+
+get_type_set(#decoding_state{type_pool = Pool}) ->
+ case dict:to_list(Pool) of
+ [] -> [];
+ Data -> [Value || {_Key,Value} <- Data]
+ end.
+
+build_foreign_view(ForeignType,FieldNames,State) ->
+ lager:debug("[DECODE] build_foreign_view ForeignType ~p FieldNames ~p",[ForeignType,FieldNames]),
+ ForeignView = FieldNames,
+ #decoding_state{type_pool = OldPool} = State,
+ Native = dict:size(OldPool),
+ NewPool = dict:store(Native,
+ #type_def{
+ defineNo = Native,
+%% native_type = Native,
+ foreign_type = ForeignType,
+ fieldnames = ForeignView},
+ OldPool),
+ {
+ #type_def{defineNo = Native,
+%% native_type = Native,
+ foreign_type = ForeignType,
+ fieldnames = ForeignView},
+ State#decoding_state{type_pool = NewPool}
+ }.
+
+project_native_view(ForeignView,ForeignData,
+ #type_def{native_type = _NativeType, foreign_type = _ForeignType, fieldnames = NativeView}) ->
+ AsDict = dict:from_list(lists:zip(ForeignView,ForeignData)),
+ NativeData = [dict:fetch(Key,AsDict) || Key <- NativeView],
+ NativeData.
+
+visit(TypeDef, State = #decoding_state{reference_pool = OldPool}) ->
+ lager:debug("[DECODE] visit typedef ~p",[TypeDef]),
+ Size = dict:size(OldPool),
+ NewPool = dict:store(Size, TypeDef, OldPool),
+ {Size,State#decoding_state{reference_pool = NewPool}}.
+
+hash_lookup(Hash,#decoding_state{hash_pool = HashPool} = State) ->
+ case dict:find(Hash,HashPool) of
+ error->
+ {not_found, Hash};
+ {ok,TypeDef}->
+ TypeDef
+ end.
+%% init(false),
+%% case ets:lookup(hashes, Hash) of
+%% [] ->
+%% {not_found, Hash};
+%% [{Hash,TypeDef}] ->
+%% TypeDef
+%% end.
+
+hash_store(TypeDef = #type_def{defineNo = Hash}, #decoding_state{hash_pool = HashPool} = State) ->
+%% init(false),
+%% ets:insert(hashes,{Hash,TypeDef}),
+ lager:debug("[DECODE] hash store typedef ~p",[TypeDef]),
+ NewPool = dict:store(Hash,TypeDef,HashPool),
+ State#decoding_state{hash_pool = NewPool}.
+
+init() -> init(true).
+
+init(Delete) when is_boolean(Delete) ->
+%% case ets:info(hashes) of
+%% undefined ->
+%% ets:new(hashes,[private,named_table]);
+%% _ ->
+%% if
+%% Delete ->
+%% ets:delete(hashes),
+%% ets:new(hashes,[private,named_table]);
+%% true ->
+%% ok
+%% end
+%% end,
+ #decoding_state{}.
+
+count_fields(#type_def{fieldnames = FieldNames}) -> length(FieldNames).
+
+%% extend func
+get_deftype(Ref,#decoding_state{type_pool = Pool})->
+ dict:fetch(Ref,Pool).
\ No newline at end of file
diff --git a/src/hessianEncode.erl b/src/hessianEncode.erl
new file mode 100755
index 0000000..0b29a9e
--- /dev/null
+++ b/src/hessianEncode.erl
@@ -0,0 +1,577 @@
+-module(hessianEncode).
+
+-include("hessian.hrl").
+
+-export([encode/2, encode/3, encode/4, encode/5]).
+-export([get_value/3, get_value/4]).
+-export([wrap_class/1, encode_for_decode/2,encode_object/3]).
+
+%---------------------------------------------------------------------------
+% Encoding
+%---------------------------------------------------------------------------
+encode_for_decode(Value, [{set,SetInfo},ClassList]) when is_tuple(Value) -> encode(struct, Value, [{set,SetInfo},wrap_class(ClassList)]);
+encode_for_decode(Value, State) when is_tuple(Value) -> encode(struct, Value, wrap_class(State)).
+%---------------------------------------------------------------------------
+% encode/2 <-- ** Entry **
+%---------------------------------------------------------------------------
+
+encode(undefined, _State) -> <<$N>>;
+encode(null, _State) -> <<$N>>;
+encode(true, _State) -> <<$T>>;
+encode(false, _State) -> <<$F>>;
+encode(Value, State) when is_integer(Value) -> encode(int, Value, State);
+encode(Value, State) when is_atom(Value) -> encode(string, erlang:atom_to_binary(Value,utf8), State);
+encode(Value, State) when is_float(Value) -> encode(double, Value, State);
+encode(Value, State) when is_list(Value) -> encode(string, Value, State);
+encode(Value, State) when is_binary(Value) -> encode(string, Value, State);
+encode(Value, State) when is_tuple(Value) -> encode(struct, Value, State).
+%---------------------------------------------------------------------------
+% encode/3
+%---------------------------------------------------------------------------
+encode(struct, Input, State) ->
+ case Input of
+ #list{} -> encode(list, Input, State);
+ #map{} -> encode(map, Input, State);
+ #object{} -> encode(object, Input, State);
+ #class{} -> encode(class_store, Input, State);
+ {binary, Bin} -> encode(binary, Bin, State);
+ {date, Timestamp} -> encode(date, Timestamp, State);
+ {ref, Index} ->
+ IndexBin = encode(int, Index, State),
+ <<$Q, IndexBin/binary>>;
+ _Object ->
+ lager:debug("[encode] object ~p",[Input]),
+ encode(class_object, Input, State);
+%% {<<>>,State};
+ {K, V} ->
+ {BK,SK} = encode(K, State),
+ {BV,SV} = encode(V, SK),
+ {<<BK/binary, BV/binary>>, SV}
+ end;
+encode(int, Int, _State) when Int >= -16, Int =< 47 ->
+ _Int = Int + 16#90,
+ <<_Int:8>>;
+encode(int, Int, _State) when Int >= -2048, Int =< 2047 ->
+ <<B1:8,B0:8>> = <<Int:16>>,
+ _B1 = B1 + 16#c8,
+ <<_B1,B0>>;
+encode(int, Int, _State) when Int >= -262144, Int =< 262143 ->
+ <<B2:8,B1:8,B0:8>> = <<Int:24>>,
+ _B2 = B2 + 16#d4,
+ <<_B2,B1,B0>>;
+encode(int, Int, _State) when Int > -16#80000001, Int < 16#80000000 ->
+ <<$I,Int:32/signed>>;
+encode(int, Int, State) ->
+ encode(long, Int, State);
+encode(long, Long, _State) when Long >= -8, Long =< 15 ->
+ _Long = Long + 16#e0,
+ <<_Long:8>>;
+encode(long, Long, _State) when Long >= -2048, Long =< 2047 ->
+ <<B1:8,B0:8>> = <<Long:16>>,
+ _B1 = B1 + 16#f8,
+ <<_B1,B0>>;
+encode(long, Long, _State) when Long >= -262144, Long =< 262143 ->
+ <<B2:8,B1:8,B0:8>> = <<Long:24>>,
+ _B2 = B2 + 16#3c,
+ <<_B2,B1,B0>>;
+encode(long, Long, _State) when Long > -16#80000001, Long < 16#80000000 ->
+ <<16#59,Long:32/signed>>;
+encode(long, Long, _State) ->
+ <<$L,Long:64/signed>>;
+encode(double, 0.0, _State) ->
+ <<16#5b>>;
+encode(double, 1.0, _State) ->
+ <<16#5c>>;
+encode(double, Double, _State) when Double >= -128.0, Double =< 127.0, Double == round(Double) ->
+ Byte = round(Double),
+ <<16#5d, Byte/signed>>;
+encode(double, Double, _State) when Double >= -32768.0, Double =< 32767.0, Double == round(Double) ->
+ Byte = round(Double),
+ <<16#5e, Byte:16/signed>>;
+encode(double, Double, _State) ->
+ case <<Double/float>> of
+ <<B24,B16,B8,B0,0,0,0,0>> -> <<16#5f,B24,B16,B8,B0>>;
+ Other -> <<$D,Other/binary>>
+ end;
+encode(string, <<>>, _State) ->
+ <<0>>;
+encode(string, [], _State) ->
+ <<0>>;
+encode(string, String, State) when is_list(String)->
+ UTF8 = xmerl_ucs:from_utf8(String),
+ Length = length(UTF8),
+ encode(sub, Length, String, [UTF8|State]);
+encode(string, String, State) when is_binary(String)->
+ encode(string, binary_to_list(String), State);
+encode(binary, <<>>, _State) ->
+ <<16#20>>;
+encode(binary, Value, _State) when size(Value) < 15 ->
+ Size = 16#20 + size(Value),
+ <<Size:8/unsigned,Value/binary>>;
+encode(binary, Value, State) ->
+ encode(binary, Value, <<>>, State);
+encode(date, {MegaSeconds,Seconds,MicroSeconds}, State) ->
+ MilliSeconds = MegaSeconds * ?MegaSeconds + Seconds * ?Seconds + MicroSeconds div ?MicroSeconds,
+ encode(date, MilliSeconds, State);
+encode(date, MilliSeconds, _State) ->
+ MinuteRemain = MilliSeconds rem (?Seconds * 60),
+ case MinuteRemain of
+ 0 ->
+ Minutes = MilliSeconds div (?Seconds * 60),
+ <<16#4b,Minutes:32/unsigned>>;
+ _ -> <<16#4a,MilliSeconds:64/unsigned>>
+ end;
+encode(list, Input, [{set,SetInfo},_T]=State) when is_record(Input, list) ->
+ case find_set_info(Input#list.refNo, SetInfo) of
+ not_found -> encode(list, Input#list.len, Input#list.type, Input#list.values, State);
+ List -> encode(list, List#list.len, List#list.type, List#list.values, State)
+ end;
+encode(list, Input, State) when is_record(Input, list) ->
+ encode(list, Input#list.len, Input#list.type, Input#list.values, State);
+encode(list, List, State) ->
+ ListLength = length(List),
+ encode(fixedlist, ListLength, List, State);
+encode(vlist, List, State) ->
+ case List of
+ [] -> <<$W,$Z>>;
+ _ -> encode(vlist, List, <<$W>>, State)
+ end;
+encode(map, Input, [{set,SetInfo},_T]=State) when is_record(Input, map) ->
+ case find_set_info(Input#map.refNo, Input#map.type, SetInfo) of
+ not_found -> encode(map, Input#map.dict, State);
+ Map -> encode(map, Map#map.dict, State)
+ end;
+encode(map, Input, State) when is_record(Input, map) ->
+ encode(map, Input#map.dict, State);
+encode(map, Dict, State) ->
+ List = dict:to_list(Dict),
+ Encoder = fun({Key, Value}, {AccIn, StateIn}) ->
+ % Key
+ RK = encode(Key, StateIn),
+ {KeyBin, NewStateKey} = case RK of
+ {_, _} -> RK;
+ KeyB -> {KeyB, StateIn}
+ end,
+ % Value
+ RV = encode(Value, NewStateKey),
+ {ValueBin, NewStateValue} = case RV of
+ {_, _} -> RV;
+ ValueB -> {ValueB, NewStateKey}
+ end,
+ % Acc Out
+ {<<AccIn/binary,KeyBin/binary,ValueBin/binary>>, NewStateValue}
+ end,
+ {AccOut, NewState} = lists:foldl(Encoder, {<<$H>>, State}, List),
+ {<<AccOut/binary,$Z>>, NewState};
+encode(class, #class{name=Name, fields=Fields}, State) ->
+ % type
+ NameBin = encode(Name, State),
+ % fields length
+ FieldsLen = length(Fields),
+ FieldsLenBin = encode(int, FieldsLen, State),
+ % fields
+ encode(list, Fields, <<$C,NameBin/binary,FieldsLenBin/binary>>, State);
+encode(class_store, Input, [{set,SetInfo},ClassList]=State) ->
+ {Bin, _} = encode(class, Input, State),
+ {Bin, [{set,SetInfo},[Input#class{encoded=true}|ClassList]]};
+encode(class_store, Input, State) ->
+ {Bin, _} = encode(class, Input, State),
+%% {Bin, [Input#class{encoded=true}|State]};
+ {Bin, State};
+encode(object, Input, [{set,SetInfo},T]=State) when is_record(Input, object) ->
+ ClassRes = class(Input#object.typeRef, Input#object.class, [], T),
+ {BaseBin, TypeNum, C, NewState} = case ClassRes of
+ {encoded, Class, TypeNo} -> {<<>>, TypeNo, Class, State};
+ {Class, TypeNo, NewST} ->
+ {Bin, NewClassST} = encode(class, Class, NewST),
+ {Bin, TypeNo, Class, [{set,SetInfo},NewClassST]}
+ end,
+ case find_set_info(Input#object.refNo, C#class.name, SetInfo) of
+ not_found -> encode(object, BaseBin, TypeNum, Input#object.values, NewState);
+ Obj -> encode(object, BaseBin, TypeNum, Obj#object.values, NewState)
+ end;
+encode(object, Input, State) when is_record(Input, object) ->
+ ClassRes = class(Input#object.typeRef, Input#object.class, [], State),
+
+ {BaseBin, TypeNum, NewState} = case ClassRes of
+ {encoded, _Class, TypeNo} -> {<<>>, TypeNo, State};
+ {Class, TypeNo, NewST} ->
+ {Bin, NewClassST} = encode(class, Class, NewST),
+ {Bin, TypeNo, NewClassST}
+ end,
+ encode(object, BaseBin, TypeNum, Input#object.values, NewState);
+encode(class_object,Input,State)->
+ [NativeType|Values] = tuple_to_list(Input),
+ {ClassEncodingBin, EncodedRef, NewState} =
+ case type_encoding:visit(NativeType,State) of
+ {ref, Ref} ->
+%% encode_object(type_information, {ref, Ref}, State);
+ %% todo 还未验证
+ {<<>>,Ref,State};
+ {hash, Ref, Typedef , State0} ->
+ Class = typedef_to_class(Typedef,Ref),
+ {Bin,NewStateClass} = encode(class_store,Class,State0),
+ {Bin,Ref,NewStateClass}
+%% encode(type_information, {hash, Ref,Typedef }, State0)
+ end,
+ encode(object, ClassEncodingBin, EncodedRef, Values, NewState);
+%% {AccOut, _NewState} = lists:foldl(fun encode/2,{<<>>, NewState},Values),
+%% {<<ClassEncodingBin/binary,$o,EncodedRef/binary,AccOut/binary>>, _NewState};
+
+encode(method, Method, State) when is_atom(Method) ->
+ String = atom_to_list(Method),
+ encode(method, String, State);
+encode(method, Method, _State) when is_binary(Method) ->
+ CamMethod = erlang_to_camel_case(Method),
+ Size = size(CamMethod),
+ <<$m,Size:16/unsigned,CamMethod/binary>>;
+encode(method, String, _State) when is_list(String) ->
+ CamString = erlang_to_camel_case(String),
+ Length = string:len(CamString),
+ Bin = list_to_binary(CamString),
+ <<$m,Length:16/unsigned,Bin/binary>>;
+encode(reply, ok, _State) ->
+ <<$H,16#02,16#00,$R,16#01,$N>>;
+encode(reply, {ok, Object}, State) ->
+ encode(reply, Object, State);
+encode(reply, {error, {Error, Reason} }, State) ->
+ encode(fault, Error, Reason, State);
+encode(reply, Object, State) ->
+ case encode(Object, State) of
+ {Bin, _NewState} -> Bin;
+ Bin -> Bin
+ end,
+ <<$H,16#02,16#00,$R,Bin/binary>>.
+%---------------------------------------------------------------------------
+% encode/4
+%---------------------------------------------------------------------------
+encode(sub, Length, String, _State) when Length < 32 ->
+ Bin = list_to_binary(String),
+ <<Length:8,Bin/binary>>;
+encode(sub, Length, String, _State) when Length < 256 ->
+ Bin = list_to_binary(String),
+ <<16#30,Length:8,Bin/binary>>;
+encode(sub, Length, String, _State) when Length < 512 ->
+ Bin = list_to_binary(String),
+ <<16#31,(Length-256):8,Bin/binary>>;
+encode(sub, Length, String, _State) when Length < 768 ->
+ Bin = list_to_binary(String),
+ <<16#32,(Length-512):8,Bin/binary>>;
+encode(sub, Length, String, _State) when Length < ?CHUNK_SIZE ->
+ Bin = list_to_binary(String),
+ <<16#33,(Length-768):8,Bin/binary>>;
+encode(sub, Length, String, _State) when Length =:= ?CHUNK_SIZE ->
+ Bin = list_to_binary(String),
+ <<$S,Length:16,Bin/binary>>;
+encode(sub, Length, _String, [UTF8|State]) ->
+ encode(string, Length, UTF8, <<>>, State);
+encode(binary, Value, <<>>, _State) when size(Value) =< ?CHUNK_SIZE ->
+ Size = size(Value),
+ <<$B,Size:16,Value/binary>>;
+encode(binary, Value, Acc, _State) when size(Value) =< ?CHUNK_SIZE ->
+ Size = size(Value),
+ <<Acc/binary,$B,Size:16,Value/binary>>;
+encode(binary, Value, <<>>, State) ->
+ <<Chunk:?CHUNK_SIZE/binary,Rest/binary>> = Value,
+encode(binary, Rest, <<$b,?CHUNK_SIZE:16,Chunk/binary>>, State);
+encode(binary, Value, Acc, State) ->
+ <<Chunk:?CHUNK_SIZE/binary,Rest/binary>> = Value,
+ encode(binary, Rest, <<Acc/binary,$b,?CHUNK_SIZE:16,Chunk/binary>>, State);
+encode(list, List, Acc0, State) when is_binary(Acc0) ->
+ lists:foldl(fun encode_accumulate/2, {Acc0,State}, List);
+encode(list, Type, List, State) ->
+ ListLength = length(List),
+ encode(list, ListLength, Type, List, State);
+encode(vlist, List, Acc0, State) when is_binary(Acc0) ->
+ {AccOut, NewState} = lists:foldl(fun encode_accumulate/2, {Acc0,State}, List),
+ {<<AccOut/binary,$Z>>, NewState};
+encode(vlist, Type, List, State) ->
+ TypeBin = encode(Type, State),
+ case List of
+ [] -> <<$U,TypeBin/binary,$Z>>;
+ _ -> encode(vlist, List, <<$U,TypeBin/binary>>, State)
+ end;
+encode(fixedlist, ListLength, List, State) ->
+ case ListLength < 8 of
+ true ->
+ ListLengthFlag = 16#78 + ListLength,
+ case List of
+ [] -> <<ListLengthFlag:8>>;
+ _ -> encode(list, List, <<ListLengthFlag:8>>, State)
+ end;
+ false ->
+ ListLengthBin = encode(int, ListLength, State),
+ encode(list, List, <<$X,ListLengthBin/binary>>, State)
+ end;
+encode(fault, _Error, _Reason, State) ->
+ encode(fault, <<"ServiceException">>, _Error, _Reason, State);
+encode(call, Method, Args, State) ->
+ encode(call, Method, Args, fun encode_accumulate/2, State).
+%---------------------------------------------------------------------------
+% encode/5
+%---------------------------------------------------------------------------
+encode(string, Length, UTF8, Acc, _State) when Length =< ?CHUNK_SIZE ->
+ Bin = list_to_binary(xmerl_ucs:to_utf8(UTF8)),
+ <<Acc/binary,$S,Length:16,Bin/binary>>;
+encode(string, Length, UTF8, Acc, State) ->
+ {Chunk, Rest} = lists:split(?CHUNK_SIZE, UTF8),
+ ChunkBin = list_to_binary(xmerl_ucs:to_utf8(Chunk)),
+ encode(string, (Length-?CHUNK_SIZE), Rest, <<Acc/binary,$R,?CHUNK_SIZE:16,ChunkBin/binary>>, State);
+encode(list, -1, untyped, List, State) ->
+ encode(vlist, List, State);
+encode(list, -1, Type, List, State) ->
+ encode(vlist, Type, List, State);
+encode(list, Len, untyped, List, State) ->
+ encode(fixedlist, Len, List, State);
+encode(list, Len, Type, List, State) ->
+ TypeBin = encode(Type, State),
+ if
+ Len < 8 ->
+ ListLengthFlag = 16#70 + Len,
+ case List of
+ [] -> <<ListLengthFlag:8,TypeBin/binary>>;
+ _ -> encode(list, List, <<ListLengthFlag:8,TypeBin/binary>>, State)
+ end;
+ true ->
+ ListLengthBin = encode(int, Len, State),
+ encode(list, List, <<$V,TypeBin/binary,ListLengthBin/binary>>, State)
+ end;
+encode(object, BaseBin, TypeNo, Values, State) ->
+ IndexBin = if
+ TypeNo < 16 ->
+ lager:debug("[encode] encode object TypeNo ~p",[TypeNo]),
+ IndexWrap = TypeNo + 16#60,
+ <<IndexWrap:8>>;
+ true ->
+ IndexWrap = encode(int, TypeNo, State),
+ <<$O, IndexWrap/binary>>
+ end,
+ {Bin, NewState} = encode(list, Values, IndexBin, State),
+ {<<BaseBin/binary,Bin/binary>>, NewState};
+encode(call, Method, Args, Fun, State) when is_function(Fun) ->
+ MethodBin = encode(string, Method, State),
+ ArgsCount = encode(int, erlang:length(Args), State),
+ {Bin, _NewState} = lists:foldl(Fun, {<<>>, State}, Args),
+ <<$H,2,0,$C,MethodBin/binary,ArgsCount/binary,Bin/binary>>;
+encode(fault, Code, _Error, _Reason, State) ->
+ EncodedCode = encode(string,Code, State),
+ <<131,100,_L2:16/unsigned,Error/binary>> = term_to_binary(_Error),
+ EncodedError = encode(string,Error, State),
+ <<$H,16#02,16#00,$F,$H,4,"code",EncodedCode/binary,7,"message",EncodedError/binary,6,"detail",31,"Stack trace not yet implemented",$Z>>.
+
+%---------------------------------------------------------------------------
+% Utility methods
+%---------------------------------------------------------------------------
+erlang_to_camel_case(String) when is_binary(String) ->
+ AsList = binary_to_list(String),
+ AsCamel = lists:foldl(fun camelize/2,[],AsList),
+ list_to_binary(AsCamel);
+erlang_to_camel_case(String) when is_atom(String) ->
+ AsList = atom_to_list(String),
+ AsCamel = lists:foldl(fun camelize/2,[],AsList),
+ list_to_binary(AsCamel).
+
+camelize(Element,Acc) when Element == $_ -> [$_|Acc];
+camelize(Element,[$_|Acc]) -> lists:append(Acc,[Element - 16#20]);
+camelize(Element,Acc) -> lists:append(Acc,[Element]).
+
+encode_accumulate(Value, {Acc, State}) ->
+ lager:debug("[encode] encode_accumulate value ~p",[Value]),
+ case encode(Value, State) of
+ {Encoded,NewState} -> {<<Acc/binary,Encoded/binary>>,NewState};
+ Encoded -> {<<Acc/binary,Encoded/binary>>,State}
+ end.
+
+find_set_info(RefNo, SetInfo) ->
+ find_set_info(RefNo, untyped, SetInfo).
+find_set_info(_RefNo, _Type, []) ->
+ not_found;
+find_set_info(RefNo, Type, [Set|SetInfo]) ->
+ if
+ RefNo =:= Set#set.ref -> Set#set.value;
+ true ->
+ case equal(Type, Set#set.ref) of
+ true -> Set#set.value;
+ false -> find_set_info(RefNo, Type, SetInfo)
+ end
+ end.
+
+type_index(Type, [CurrentType|RestTypes]) when is_record(Type, class) ->
+ case equal(Type#class.name, CurrentType#class.name) of
+ true -> length(RestTypes);
+ false -> type_index(Type, RestTypes)
+ end;
+type_index(Type, [CurrentType|RestTypes]) ->
+ case equal(Type, CurrentType#class.name) of
+ true -> length(RestTypes);
+ false -> type_index(Type, RestTypes)
+ end.
+
+get_class(Where, ClassList) ->
+ SortClassList = lists:reverse(ClassList),
+ case Where#object.typeRef of
+ -1 ->
+ if
+ Where#object.class =:= auto -> hd(ClassList);
+ true ->
+ Index = type_index(Where#object.class, ClassList),
+ lists:nth(Index+1, SortClassList)
+ end;
+ TypeRef ->
+ lists:nth(TypeRef+1, SortClassList)
+ end.
+
+get_type_no(C, RestClasses) when is_record(C, class) ->
+ case C#class.typeNo of
+ -1 -> length(RestClasses);
+ _ -> C#class.typeNo
+ end.
+
+get_type(CListH, C, CListE) when is_record(C, class) ->
+ TypeNo = get_type_no(C, CListE),
+ case C#class.encoded of
+ true -> {encoded, C, TypeNo};
+ false -> {C, TypeNo, CListH++[C#class{encoded=true}|CListE]}
+ end.
+
+class(-1, auto, [], [C|CListE]) ->
+ get_type([], C, CListE);
+class(-1, Class, CListH, [C|CListE]) when is_record(Class, class) ->
+ case Class#class.name of
+ auto ->
+ case CListH of
+ [] -> get_type([], C, CListE);
+ [H|T] ->get_type([], H, T++[C|CListE])
+ end;
+ _ ->
+ case equal(Class#class.name, C#class.name) of
+ true -> get_type(CListH, C, CListE);
+ false -> class(-1, Class, CListH++[C], CListE)
+ end
+ end;
+class(-1, Class, CListH, [C|CListE]) ->
+ case equal(Class, C#class.name) of
+ true -> get_type(CListH, C, CListE);
+ false -> class(-1, Class, CListH++[C], CListE)
+ end;
+class(RefNo, _Class, CListH, [C|CListE]) ->
+ if
+ RefNo =:= length(CListE) -> get_type(CListH, C, CListE);
+ true -> class(RefNo, _Class, CListH++[C], CListE)
+ end.
+
+equal(S1, S2) when is_binary(S1) ->
+ if
+ S1 =:= S2 -> true;
+ true ->
+ S1List = binary_to_list(S1),
+ if
+ S1List =:= S2 -> true;
+ true -> false
+ end
+ end;
+equal(S1, S2) when is_list(S1) ->
+ if
+ S1 =:= S2 -> true;
+ true ->
+ S1Binary = list_to_binary(S1),
+ if
+ S1Binary =:= S2 -> true;
+ true -> false
+ end
+ end;
+equal(_S1, _S2) ->
+ false.
+
+wrap_class(ClassList) ->
+ Fun = fun(OldClass, AccIn) ->
+ [#class{typeNo=OldClass#type_def.native_type, name=OldClass#type_def.foreign_type, fields=OldClass#type_def.fieldnames}|AccIn]
+ end,
+ lists:foldl(Fun, [], ClassList).
+
+get_value(Where, Ref, Field) ->
+ get_value(Where, Ref, Field, [], []).
+get_value(Where, Ref, Field, ClassList) ->
+ get_value(Where, Ref, Field, wrap_class(ClassList), []).
+get_value(Where, Ref, Field, ClassList, ObjVlues) when is_record(Where, list) ->
+ get_value(Where#list.values, Ref, Field, ClassList, ObjVlues);
+get_value(Where, Ref, Field, ClassList, _ObjVlues) when is_record(Where, map) ->
+ List = dict:to_list(Where#map.dict),
+ get_value(List, Ref, Field, ClassList, Where#map.refNo);
+get_value(Where, _Ref, _Field, ClassList, _ObjVlues) when is_record(Where, type_def) ->
+ {class, [#class{typeNo=Where#type_def.native_type, name=Where#type_def.foreign_type, fields=Where#type_def.fieldnames}|ClassList]};
+get_value(Where, _Ref, _Field, ClassList, _ObjVlues) when is_record(Where, class) ->
+ {class, [Where|ClassList]};
+get_value(Where, Ref, Field, ClassList, _ObjVlues) when is_record(Where, object) ->
+ Class = get_class(Where, ClassList),
+ if
+ Where#object.refNo =:= Ref -> get_value(Class#class.fields, Ref, Field, ClassList, Where#object.values);
+ Class =:= Ref -> get_value(Class#class.fields, Ref, Field, ClassList, Where#object.values);
+ true ->
+ case equal(Class#class.name, Ref) of
+ true -> get_value(Class#class.fields, Ref, Field, ClassList, Where#object.values);
+ false -> get_value(Where#object.values, Ref, Field, ClassList, skip)
+ end
+ end;
+get_value(Where, _RefNo, _Field, _ClassList, skip) when is_binary(Where) ->
+ next;
+get_value(Where, _RefNo, Field, _ClassList, Value) when is_binary(Where) ->
+ case equal(Where, Field) of
+ true -> {return, Value};
+ false -> next
+ end;
+get_value(Where, _RefNo, _Field, _ClassList, _Value) when is_atom(Where) ->
+ next;
+get_value(Where, _RefNo, _Field, _ClassList, _Value) when is_integer(Where) ->
+ next;
+get_value({ref, _No}, _RefNo, _Field, _ClassList, _ObjVlues) ->
+ next;
+get_value({K, V}, RefNo, Field, ClassList, MapRefNo) ->
+ case is_binary(K) of
+ true ->
+ if
+ RefNo =/= MapRefNo -> R = next;
+ true -> R = get_value(K, RefNo, Field, ClassList, V)
+ end;
+ false -> R = get_value(K, RefNo, Field, ClassList, [])
+ end,
+ case R of
+ next -> get_value(V, RefNo, Field, ClassList, []);
+ _ -> R
+ end;
+get_value([], _RefNo, _Field, _ClassList, _ObjVlues) ->
+ not_found;
+get_value([CurrentField|Fields], RefNo, Field, ClassList, [Value|Values]) ->
+ R = get_value(CurrentField, RefNo, Field, ClassList, Value),
+ case R of
+ {return, _} -> R;
+ {class, NewClassList} -> get_value(Fields, RefNo, Field, NewClassList, Values);
+ _ -> get_value(Fields, RefNo, Field, ClassList, Values)
+ end;
+get_value([Item|T], RefNo, Field, ClassList, Option)->
+ R = get_value(Item, RefNo, Field, ClassList, Option),
+ case R of
+ {return, _} -> R;
+ {class, NewClassList} -> get_value(T, RefNo, Field, NewClassList, Option);
+ _ -> get_value(T, RefNo, Field, ClassList, Option)
+ end.
+
+encode_object(object, Object, State) when is_tuple(Object) ->
+ [NativeType|Values] = tuple_to_list(Object),
+ {TypeEncoding, EncodedRef, NewState} =
+ case type_encoding:visit(NativeType,State) of
+ {ref, Ref} ->
+ encode_object(type_information, {ref, Ref}, State);
+ {hash, Hash, Ref, State0} ->
+ encode(type_information, {hash, Hash, Ref}, State0)
+ end,
+ {AccOut, _NewState} = lists:foldl(fun encode/2,{<<>>, NewState},Values),
+ {<<TypeEncoding/binary,$o,EncodedRef/binary,AccOut/binary>>, _NewState};
+encode_object(type_information, {ref, Ref}, State) ->
+ {<<>>, encode(int, Ref, State), State};
+encode_object(type_information, {hash, Hash, Ref}, State) ->
+ EncodedRef = encode(int, Ref, State),
+ {<<$O,Hash:32/unsigned>>, EncodedRef, State}.
+
+typedef_to_class(Typedef,DefineNo)->
+ #class{typeNo=DefineNo, name=Typedef#type_def.foreign_type, fields=Typedef#type_def.fieldnames}.
\ No newline at end of file
diff --git a/src/java_type_defined.erl b/src/java_type_defined.erl
new file mode 100644
index 0000000..b4eda9e
--- /dev/null
+++ b/src/java_type_defined.erl
@@ -0,0 +1,21 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 09. Mar 2018 11:06 PM
+%%%-------------------------------------------------------------------
+-module(java_type_defined).
+-author("dlive").
+
+-include("java_type.hrl").
+%% API
+-export([get_list/0]).
+
+
+get_list()->
+ [
+ {null_pointer_exception,<<"java.lang.NullPointerException">>,record_info(fields,null_pointer_exception)},
+ {stack_stack_trace_element,<<"java.lang.StackTraceElement">>,record_info(fields,stack_stack_trace_element)}
+ ].
\ No newline at end of file
diff --git a/src/lists_util.erl b/src/lists_util.erl
new file mode 100644
index 0000000..fec6cf0
--- /dev/null
+++ b/src/lists_util.erl
@@ -0,0 +1,43 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 27. Dec 2017 6:09 PM
+%%%-------------------------------------------------------------------
+-module(lists_util).
+-author("dlive").
+
+%% API
+-export([join/2,del_duplicate/1]).
+
+-spec(join(List :: list(), Separator :: binary()) -> binary()).
+join(List, _Separator) when length(List) == 0 ->
+ <<"">>;
+join(List, Separator) ->
+ [First | Rst] = List,
+ Acc2 = lists:foldl(fun(Item, Acc) ->
+ if
+ is_binary(Item) ->
+ <<Acc/binary, Separator/binary, Item/binary>>;
+ is_list(Item) ->
+ Item2 = list_to_binary(Item),
+ <<Acc/binary, Separator/binary, Item2/binary>>;
+ true ->
+ Acc
+ end
+ end, First, Rst),
+ Acc2.
+
+
+del_duplicate(List) ->
+ lists:foldl(
+ fun(X, List2) ->
+ case lists:member(X, List2) of
+ true ->
+ List2;
+ _ ->
+ [X]++ List2
+ end
+ end, [], List).
diff --git a/src/request_context.erl b/src/request_context.erl
new file mode 100644
index 0000000..ea9da32
--- /dev/null
+++ b/src/request_context.erl
@@ -0,0 +1,117 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 23. May 2018 4:28 PM
+%%%-------------------------------------------------------------------
+-module(request_context).
+-author("dlive").
+
+%%-define(PER_LOG,true).
+-define(PER_LOG,false).
+
+%% API
+-export([init/0,init/1,update/2,update/3,to_log/1,test/0,trace/1,trace_end/1]).
+
+init()->
+ case ?PER_LOG of
+ true->
+ #{<<"t_b">> => time_util:timestamp_ms()};
+ _->
+ #{}
+ end.
+init(ID)->
+ case ?PER_LOG of
+ true->
+ #{<<"id">> =>ID,<<"t_b">> => time_util:timestamp_ms()};
+ _->
+ #{<<"id">> =>ID}
+ end.
+
+update(Key,Map)->
+ case ?PER_LOG of
+ true->
+ Map#{Key=>time_util:timestamp_ms()};
+ _->
+ Map
+ end.
+
+update(Key,Value,Map)->
+ Map#{Key=>Value}.
+
+to_log(Map)->
+ case ?PER_LOG of
+ true->
+ to_log2(Map);
+ _->
+ ok
+ end.
+
+to_log2(Map)->
+
+ #{
+ <<"t_b">> := Begin,
+ <<"t_lb">> := LoadBalance,
+ <<"t_send_agent">> := SendAgent,
+ <<"t_agent_b">> := AgentBegin,
+ <<"t_agent_e">> := AgentEnd,
+ <<"t_net_b">> := NetBegin,
+%% <<"t_net_bd">> := NetBeginDecode,
+ <<"t_net_e">> := NetEnd,
+ <<"t_backer_b">> := BackerBegin,
+ <<"t_backer_e1">> := BackerGet,
+ <<"t_backer_e">> := BackerEnd,
+ <<"t_gate_back">> := GateWayBack,
+%% <<"t_e">> := End,
+ <<"t_nd">> :=Node
+ } = Map,
+ Total=GateWayBack-Begin,
+ if
+ Total > 90 ->
+ performance:warning("total ~-5.10B lb ~-5.10B to_send ~-5.10B agent_b ~-5.10B agent_e ~-5.10B net_b ~-5.10B net_e ~-5.10B backer_b ~-5.10B backer_e1 ~-5.10B backer_e ~-5.10B gw_b ~-5.10B ~p",
+ [
+ Total,
+ LoadBalance - Begin,
+ SendAgent-Begin,
+ AgentBegin-SendAgent,
+ AgentEnd-AgentBegin,
+ NetBegin-AgentEnd,
+ NetEnd-NetBegin,
+ BackerBegin-NetEnd,
+ BackerGet-BackerBegin,
+ BackerEnd-BackerBegin,
+ GateWayBack-BackerEnd,
+%% End-GateWayBack,
+ Node
+ ]);
+ true->
+ performance:info("total ~-5.10B lb ~-5.10B to_send ~-5.10B agent_b ~-5.10B agent_e ~-5.10B net_b ~-5.10B net_e ~-5.10B backer_b ~-5.10B backer_e1 ~-5.10B backer_e ~-5.10B gw_b ~-5.10B ~p",
+ [
+ Total,
+ LoadBalance - Begin,
+ SendAgent-Begin,
+ AgentBegin-SendAgent,
+ AgentEnd-AgentBegin,
+ NetBegin-AgentEnd,
+ NetEnd-NetBegin,
+ BackerBegin-NetEnd,
+ BackerGet-BackerBegin,
+ BackerEnd-BackerBegin,
+ GateWayBack-BackerEnd,
+%% End-GateWayBack,
+ Node
+ ])
+ end.
+
+trace(Info)->
+%% gen_server:cast({monitor_trace,'monitor@agent_monitor.me'},{trace,Info}).
+ ok.
+
+trace_end(Info)->
+%% gen_server:cast({monitor_trace,'monitor@agent_monitor.me'},{trace_end,Info}).
+ ok.
+
+test()->
+ lager:error("a~-10.5sb",[123]).
\ No newline at end of file
diff --git a/src/time_util.erl b/src/time_util.erl
new file mode 100644
index 0000000..b6bda7a
--- /dev/null
+++ b/src/time_util.erl
@@ -0,0 +1,154 @@
+
+
+
+-module(time_util).
+
+-include_lib("kernel/include/file.hrl").
+
+
+-export([
+ get_cur_time/0,get_cur_time/1,
+ format_time_to_str/1,
+ timestamp/0,timestamp_ms/0,
+ timestamp_to_datetime/1,
+ timestamp_to_local_datetime/1,
+ get_cur_date/0,
+ datetime_to_timestamp/1,
+ datetime_string_to_timestamp/1,get_curdate_timestamp/0]).
+
+
+get_cur_time()->
+ {{Year,Month,Day},{Hour,Min,Second}}=calendar:now_to_local_time(os:timestamp()),
+ io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",[Year, Month, Day, Hour,Min,Second]).
+
+get_cur_date()->
+ {{Year,Month,Day},{Hour,Min,Second}}=calendar:now_to_local_time(os:timestamp()),
+ io_lib:format("~4..0w-~2..0w-~2..0w",[Year, Month, Day]).
+
+get_cur_time({{Year,Month,Day},{Hour,Min,Second}})->
+ io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",[Year, Month, Day, Hour,Min,Second]).
+
+format_time_to_str({{Year,Month,Day},{Hour,Min,Second}})->
+ io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",[Year, Month, Day, Hour,Min,Second]).
+
+timestamp() ->
+ {M, S, _} = os:timestamp(),
+ M * 1000000 + S.
+timestamp_ms()->
+ {M,S,W} = os:timestamp(),
+ M*1000000000+S*1000+(W div 1000).
+
+timestamp_to_datetime(Timestamp) ->
+ calendar:gregorian_seconds_to_datetime(Timestamp +
+ calendar:datetime_to_gregorian_seconds({{1970,1,1}, {0,0,0}})).
+timestamp_to_local_datetime(Timestamp) ->
+ Date=calendar:gregorian_seconds_to_datetime(Timestamp +
+ calendar:datetime_to_gregorian_seconds({{1970,1,1}, {0,0,0}})),
+ calendar:universal_time_to_local_time(Date).
+
+%% @doc 时间转时间戳
+datetime_to_timestamp(Date)->
+ [{D,T}]=calendar:local_time_to_universal_time_dst(Date),
+ S = calendar:datetime_to_gregorian_seconds({D, T}),
+ S1 = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
+ Seconds = (S - S1).
+ % {Seconds div 1000000, Seconds rem 1000000, MS}.
+
+%% @doc 时间字符串转 时间戳
+datetime_string_to_timestamp(TimeStr) ->
+ case catch parse_datetime(TimeStr) of
+ {'EXIT', _Err} ->
+ undefined;
+ TimeStamp ->
+ TimeStamp
+ end.
+
+parse_datetime(TimeStr) ->
+ [Date, Time] = string:tokens(TimeStr, "T"),
+ D = parse_date(Date),
+ {T, MS, TZH, TZM} = parse_time(Time),
+ S = calendar:datetime_to_gregorian_seconds({D, T}),
+ S1 = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
+ Seconds = (S - S1) - TZH * 60 * 60 - TZM * 60,
+ {Seconds div 1000000, Seconds rem 1000000, MS}.
+
+% yyyy-mm-dd
+parse_date(Date) ->
+ [Y, M, D] = string:tokens(Date, "-"),
+ Date1 = {list_to_integer(Y), list_to_integer(M), list_to_integer(D)},
+ case calendar:valid_date(Date1) of
+ true ->
+ Date1;
+ _ ->
+ false
+ end.
+
+% hh:mm:ss[.sss]TZD
+parse_time(Time) ->
+ case string:str(Time, "Z") of
+ 0 ->
+ parse_time_with_timezone(Time);
+ _ ->
+ [T | _] = string:tokens(Time, "Z"),
+ {TT, MS} = parse_time1(T),
+ {TT, MS, 0, 0}
+ end.
+
+parse_time_with_timezone(Time) ->
+ case string:str(Time, "+") of
+ 0 ->
+ case string:str(Time, "-") of
+ 0 ->
+ false;
+ _ ->
+ parse_time_with_timezone(Time, "-")
+ end;
+ _ ->
+ parse_time_with_timezone(Time, "+")
+ end.
+
+parse_time_with_timezone(Time, Delim) ->
+ [T, TZ] = string:tokens(Time, Delim),
+ {TZH, TZM} = parse_timezone(TZ),
+ {TT, MS} = parse_time1(T),
+ case Delim of
+ "-" ->
+ {TT, MS, -TZH, -TZM};
+ "+" ->
+ {TT, MS, TZH, TZM}
+ end.
+
+parse_timezone(TZ) ->
+ [H, M] = string:tokens(TZ, ":"),
+ {[H1, M1], true} = check_list([{H, 12}, {M, 60}]),
+ {H1, M1}.
+
+parse_time1(Time) ->
+ [HMS | T] = string:tokens(Time, "."),
+ MS = case T of
+ [] ->
+ 0;
+ [Val] ->
+ list_to_integer(string:left(Val, 6, $0))
+ end,
+ [H, M, S] = string:tokens(HMS, ":"),
+ {[H1, M1, S1], true} = check_list([{H, 24}, {M, 60}, {S, 60}]),
+ {{H1, M1, S1}, MS}.
+
+check_list(List) ->
+ lists:mapfoldl(
+ fun({L, N}, B)->
+ V = list_to_integer(L),
+ if
+ (V >= 0) and (V =< N) ->
+ {V, B};
+ true ->
+ {false, false}
+ end
+ end, true, List).
+%% @doc 获取当天零点时间戳
+get_curdate_timestamp()->
+ {M, S, T} = os:timestamp(),
+ TimestampTmp = M * 1000000 + S,
+ {{Year,Month,Day},{Hour,Min,Second}}=calendar:now_to_local_time({M, S, T}),
+ TimestampTmp - Second -(Hour*60+Min)*60.
\ No newline at end of file
diff --git a/src/type_decoding.erl b/src/type_decoding.erl
new file mode 100644
index 0000000..343725e
--- /dev/null
+++ b/src/type_decoding.erl
@@ -0,0 +1,214 @@
+% ---------------------------------------------------------------------------
+% Copyright (C) 2008 0x6e6562
+%
+% Licensed under the Apache License, Version 2.0 (the "License");
+% you may not use this file except in compliance with the License.
+% You may obtain a copy of the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS,
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+% See the License for the specific language governing permissions and
+% limitations under the License.
+% ---------------------------------------------------------------------------
+
+-module(type_decoding).
+-include("hessian.hrl").
+
+%% The encoding state contains all of the statically known tuple types.
+%% When a tuple is to be decoded at run-time, a lookup is performed against
+%% the hash value that the sender passes in.
+%% This must resolve to some type definition,
+%% otherwise no instance of the decoded type can be created.
+-record(decoding_state,{type_pool = dict:new(), reference_pool = dict:new(),obj_define_no=0}).
+
+-export([init/0,init/1]).
+-export([lookup_reference/2]).
+-export([enlist/1,enlist/2]).
+-export([hash_lookup/2,hash_store/2,hash_store/1,store_typepool/1]).
+-export([visit/2]).
+-export([resolve_native_type/2,resolve_native_type/1]).
+-export([camel_case_to_erlang/1]).
+-export([build_foreign_view/3]).
+-export([project_native_view/3]).
+
+-define(TYPEPOOL_TABLE,type_pool).
+-define(REFERENCEPOOL_TABLE,reference_pool).
+
+camel_case_to_erlang(String) when is_binary(String) ->
+ AsList = binary_to_list(String),
+ AsErlang = lists:foldl(fun decamelize/2,[],AsList),
+ list_to_atom(AsErlang).
+
+decamelize(Element,Acc) when Element >= $A, Element =< $Z ->
+ lists:append(Acc,[$_,(Element + 16#20)] );
+decamelize(Element,Acc) -> lists:append(Acc,[Element]).
+
+%% Facility to register a particular type to the pool of known types.
+%% Adds the type to the pool of known types if it doesn't already exist.
+enlist(TypeDef) -> enlist(TypeDef,init()).
+enlist(TypeDef = #type_def{foreign_type = Key},
+ State = #decoding_state{type_pool = OldPool}) ->
+ ets:insert(?TYPEPOOL_TABLE,{Key,TypeDef}),
+ State.
+
+store_typepool(TypeDef = #type_def{foreign_type = Key}) ->
+ ets:insert(?TYPEPOOL_TABLE,{Key,TypeDef}),
+ TypeDef.
+
+
+
+build_foreign_view(ForeignType,FieldNames,State) ->
+ #type_def{native_type = Native} = resolve_native_type(ForeignType,State),
+ ForeignView = [camel_case_to_erlang(Fieldname) || Fieldname <- FieldNames],
+ DefineNo = State#decoding_state.obj_define_no,
+ {#type_def{
+ defineNo = State#decoding_state.obj_define_no,
+ native_type = Native,
+ foreign_type = ForeignType,
+ fieldnames = ForeignView}, State#decoding_state{obj_define_no = DefineNo}}.
+
+% Projects the native view over tuples that in foreign order
+project_native_view(ForeignView,ForeignData,
+ #type_def{native_type = NativeType, fieldnames = NativeView}) ->
+ AsDict = dict:from_list(lists:zip(ForeignView,ForeignData)),
+ NativeData = [dict:fetch(Key,AsDict) || Key <- NativeView],
+ list_to_tuple( [NativeType] ++ NativeData).
+
+resolve_native_type(ForeignType, #decoding_state{type_pool = Pool}) ->
+ case ets:lookup(?TYPEPOOL_TABLE,ForeignType) of
+ []->
+ throw({cannot_resolve_type,ForeignType});
+ [{ForeignType,TypeDef}]->
+ TypeDef
+ end.
+resolve_native_type(ForeignType) ->
+ case ets:lookup(?TYPEPOOL_TABLE,ForeignType) of
+ []->
+ undefined;
+ [{ForeignType,TypeDef}]->
+ TypeDef
+ end.
+%% case dict:fetch(ForeignType,Pool) of
+%% error ->
+%% throw({cannot_resolve_type,ForeignType});
+%% TypeDef ->
+%% TypeDef
+%% end.
+
+%% Creates a reference for a type def that is unique within the
+%% current invocation context
+visit(TypeDef, State = #decoding_state{reference_pool = OldPool}) ->
+ Size=ets:info(?REFERENCEPOOL_TABLE,size),
+ ets:insert(?REFERENCEPOOL_TABLE,{Size,TypeDef#type_def{defineNo = Size}}),
+ State.
+%% Size = dict:size(OldPool),
+%% NewPool = dict:store(Size, TypeDef, OldPool),
+%% State#decoding_state{reference_pool = NewPool}.
+
+%% Resolves a type def based on a reference that must be have set
+%% with the current invocation context
+lookup_reference(Ref, #decoding_state{reference_pool = Pool}) ->
+ case ets:lookup(?REFERENCEPOOL_TABLE,Ref) of
+ []->
+ throw("Lookup of unknown reference");
+ [{Ref,TypeDef}]->
+ TypeDef
+ end.
+ %?LOG(Pool),
+ %?LOG(Ref),
+%% case dict:fetch(Ref,Pool) of
+%% error ->
+%%%% ?ERROR(Ref, Pool),
+%% throw("Lookup of unknown reference");
+%% TypeDef ->
+%% TypeDef
+%% end.
+
+%% Does what it says on the tin.
+hash_lookup(Hash,_State) ->
+%% init(false),
+ case ets:lookup(?REFERENCEPOOL_TABLE, Hash) of
+ [] ->
+ {not_found, Hash};
+ [{Hash,TypeDef}] ->
+ TypeDef
+ end.
+
+%% Does what it says on the tin.
+hash_store(#type_def{defineNo = Hash} = TypeDef, State) ->
+%% init(false),
+ case Hash of
+ -1->
+ Size=ets:info(?REFERENCEPOOL_TABLE,size),
+ ets:insert(?REFERENCEPOOL_TABLE,{Size,TypeDef#type_def{defineNo = Size}});
+ _ ->
+ ets:insert(?REFERENCEPOOL_TABLE,{Hash,TypeDef})
+ end,
+ State.
+hash_store(#type_def{defineNo = Hash} = TypeDef) ->
+%% init(false),
+ NewTypeDef=case Hash of
+ -1->
+ Size=ets:info(?REFERENCEPOOL_TABLE,size),
+ TypeDef2=TypeDef#type_def{defineNo = Size},
+ TypeDef2;
+ _ ->
+ TypeDef
+ end,
+ ets:insert(?REFERENCEPOOL_TABLE,{NewTypeDef#type_def.defineNo,NewTypeDef}),
+ NewTypeDef.
+
+init() -> init(false).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Non-API functions
+
+
+%% This creates a new ETS table for the types that have been received by
+%% this instance of Hessian
+init(Delete) when is_boolean(Delete) ->
+%% case ets:info(hashes) of
+%% undefined ->
+%% io:format("init decoding table pid ~p~n",[self()]),
+%% ets:new(hashes,[public,named_table]); %% public
+%% EtsInfo ->
+%%%% io:format("type decoding etsinfo ~p~n",[EtsInfo]),
+%% if
+%% Delete == true ->
+%% ets:delete(hashes),
+%% ets:new(hashes,[public,named_table]);
+%% true ->
+%% ok
+%% end
+%% end,
+ case ets:info(?TYPEPOOL_TABLE) of
+ undefined ->
+ io:format("init decoding type_pool table pid ~p~n",[self()]),
+ ets:new(?TYPEPOOL_TABLE,[public,named_table]); %% public
+ _ ->
+%% io:format("type decoding etsinfo ~p~n",[EtsInfo]),
+ if
+ Delete == true ->
+ ets:delete(?TYPEPOOL_TABLE),
+ ets:new(?TYPEPOOL_TABLE,[public,named_table]);
+ true ->
+ ok
+ end
+ end,
+ case ets:info(?REFERENCEPOOL_TABLE) of
+ undefined ->
+ io:format("init decoding REFERENCEPOOL_TABLE table pid ~p~n",[self()]),
+ ets:new(?REFERENCEPOOL_TABLE,[public,named_table]); %% public
+ _ ->
+ if
+ Delete == true ->
+ ets:delete(?REFERENCEPOOL_TABLE),
+ ets:new(?REFERENCEPOOL_TABLE,[public,named_table]);
+ true ->
+ ok
+ end
+ end,
+ #decoding_state{}.
\ No newline at end of file
diff --git a/src/type_encoding.erl b/src/type_encoding.erl
new file mode 100644
index 0000000..6105ab3
--- /dev/null
+++ b/src/type_encoding.erl
@@ -0,0 +1,91 @@
+% ---------------------------------------------------------------------------
+% Copyright (C) 2008 0x6e6562
+%
+% Licensed under the Apache License, Version 2.0 (the "License");
+% you may not use this file except in compliance with the License.
+% You may obtain a copy of the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS,
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+% See the License for the specific language governing permissions and
+% limitations under the License.
+% ---------------------------------------------------------------------------
+
+-module(type_encoding).
+
+-include("hessian.hrl").
+
+%% The encoding state contains all of the statically known tuple types.
+%% When a tuple is to be encoded at run-time, a lookup is performed against
+%% the type tag. This must resolve to some type definition,
+%% otherwise no type information can be encoded into the output stream.
+-record(encoding_state,{pool = dict:new(), count = -1}).
+
+-export([init/0]).
+-export([enlist/1,enlist/2]).
+-export([visit/2]).
+
+%% Facility to register a particular type to the pool of known types.
+%% Adds the type to the pool of known types if it doesn't already exist.
+init()->
+ #encoding_state{}.
+
+enlist(TypeDef) -> enlist(TypeDef,#encoding_state{}).
+enlist(TypeDef = #type_def{native_type = Key},
+ State = #encoding_state{pool = OldPool}) ->
+ case dict:is_key(Key,OldPool) of
+ true ->
+ State;
+ false ->
+ NewPool = dict:store(Key, {-1, TypeDef}, OldPool),
+ State#encoding_state{pool = NewPool}
+ end.
+
+%% Facility to record the fact that an instance of a type is about to be
+%% encoded into the stream. This needs to decide whether the hash of the
+%% type def has already been written or not.
+%%
+%% If not, a reference to this needs to be generated for future instances
+%% and the hash value of the type def needs to be written out.
+%%
+%% If it already has been written out, it must be back-referenced.
+visit(NativeType, State = #encoding_state{pool = Pool}) ->
+ lager:debug("[encode] visit ~p",[NativeType]),
+ case dict:find(NativeType,Pool) of
+ {ok,{-1, TypeDef}} ->
+ %% The type needs hashing and it's reference needs updating
+ {Ref,NewTypeDef,NewState} = assign_reference(TypeDef, State),
+%% Hash = erlang:phash2(TypeDef),
+ %%%%%%%%%%%%%%%%%%%%%%%%%%
+ %% LOOK INTO THIS DEPENDENCY, MAYBE EXTRACT IT OUT
+%% type_decoding:hash_store(NewTypeDef,NewState), %% 貌似这个没用,可以去掉.
+ %%%%%%%%%%%%%%%%%%%%%%%%%%
+ {hash, Ref,NewTypeDef , NewState};
+ {ok,{Ref, TypeDef} } ->
+ {ref, Ref};
+ error ->
+ case get_deftype_public_pool(NativeType) of
+ undefined ->
+ throw("unkonw native type "++ atom_to_list(NativeType));
+ TypeDefTmp ->
+ State2 = enlist(TypeDefTmp,State),
+ visit(NativeType,State2)
+ end
+ end.
+
+%% This increments the reference count for the current scope and updates the
+%% reference in the pool of known types
+assign_reference(TypeDef = #type_def{native_type = Key},
+ #encoding_state{pool = OldPool, count = Count}) ->
+ NewCount = Count + 1,
+ NewTypeDef = TypeDef#type_def{defineNo = NewCount},
+ Value = {NewCount, NewTypeDef},
+ NewPool = dict:store(Key, Value, OldPool),
+ lager:debug("[encode] assign_reference type ~p definedNo ~p",[Key,NewCount]),
+ {NewCount,NewTypeDef,#encoding_state{pool = NewPool, count = NewCount}}.
+
+get_deftype_public_pool(NativeType)->
+ type_register:lookup_native_type(NativeType).
\ No newline at end of file
diff --git a/src/type_register.erl b/src/type_register.erl
new file mode 100644
index 0000000..6912cf2
--- /dev/null
+++ b/src/type_register.erl
@@ -0,0 +1,57 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 05. Mar 2018 2:06 PM
+%%%-------------------------------------------------------------------
+-module(type_register).
+-author("dlive").
+%% API
+-export([init/0,regiest_foreign_native/1,lookup_foreign_type/1,lookup_native_type/1]).
+-include("hessian.hrl").
+-define(FOREIGN_NATIVE_TABLE,foreign_native_table).
+-define(NATIVE_FOREIGN_TABLE,native_foreign_table).
+
+init()->
+ case ets:info(?FOREIGN_NATIVE_TABLE) of
+ undefined ->
+ io:format("init decoding foreign_native_table table pid ~p~n",[self()]),
+ ets:new(?FOREIGN_NATIVE_TABLE,[public,named_table]); %% public
+ _ ->
+ ets:delete(?FOREIGN_NATIVE_TABLE),
+ ets:new(?FOREIGN_NATIVE_TABLE,[public,named_table])
+ end,
+ case ets:info(?NATIVE_FOREIGN_TABLE) of
+ undefined ->
+ io:format("init decoding foreign_native_table table pid ~p~n",[self()]),
+ ets:new(?NATIVE_FOREIGN_TABLE,[public,named_table]); %% public
+ _ ->
+ ets:delete(?NATIVE_FOREIGN_TABLE),
+ ets:new(?NATIVE_FOREIGN_TABLE,[public,named_table])
+ end,
+ ok.
+
+
+regiest_foreign_native(TypeDef)->
+ lager:debug("regiest foreign info ~p",[TypeDef]),
+ ets:insert(?FOREIGN_NATIVE_TABLE,{TypeDef#type_def.foreign_type,TypeDef}),
+ ets:insert(?NATIVE_FOREIGN_TABLE,{TypeDef#type_def.native_type,TypeDef}).
+
+
+lookup_foreign_type(ForeignType)->
+ case ets:lookup(?FOREIGN_NATIVE_TABLE,ForeignType) of
+ []->
+ undefined;
+ [{_,TypeDef}] ->
+ TypeDef
+ end.
+
+lookup_native_type(NativeType)->
+ case ets:lookup(?NATIVE_FOREIGN_TABLE,NativeType) of
+ []->
+ undefined;
+ [{_,TypeDef}] ->
+ TypeDef
+ end.
\ No newline at end of file
diff --git a/test/de_codec_tests.erl b/test/de_codec_tests.erl
new file mode 100644
index 0000000..5cf1e04
--- /dev/null
+++ b/test/de_codec_tests.erl
@@ -0,0 +1,20 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2018, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 06. Apr 2018 4:49 PM
+%%%-------------------------------------------------------------------
+-module(de_codec_tests).
+-author("dlive").
+
+-include_lib("eunit/include/eunit.hrl").
+-include("dubbo.hrl").
+
+-record(databaseOperateRequest,{
+ param}).
+
+
+simple_test() ->
+ ?assert(true).
diff --git a/test/dubbo_config_parser_tests.erl b/test/dubbo_config_parser_tests.erl
new file mode 100644
index 0000000..3bbf79f
--- /dev/null
+++ b/test/dubbo_config_parser_tests.erl
@@ -0,0 +1,22 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 30. Dec 2017 2:52 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_config_parser_tests).
+-author("dlive").
+
+-include_lib("eunit/include/eunit.hrl").
+-include("dubbo.hrl").
+
+simple_test() ->
+ {ok,ProviderConfig} = dubbo_node_config_util:parse_provider_info("..."),
+%% io:format(user,"parse config result ~p~n",[ProviderConfig]),
+ ?assertEqual(ProviderConfig#provider_config.protocol,dubbo),
+ ?assertEqual(ProviderConfig#provider_config.host,"192.168.1.6"),
+ ?assertEqual(ProviderConfig#provider_config.port,20880),
+ ?assertEqual(ProviderConfig#provider_config.interface,<<"...">>),
+ ?assert(true).
diff --git a/test/dubbo_zookeeper_tests.erl b/test/dubbo_zookeeper_tests.erl
new file mode 100644
index 0000000..b67248d
--- /dev/null
+++ b/test/dubbo_zookeeper_tests.erl
@@ -0,0 +1,19 @@
+%%%-------------------------------------------------------------------
+%%% @author dlive
+%%% @copyright (C) 2017, <COMPANY>
+%%% @doc
+%%%
+%%% @end
+%%% Created : 26. Dec 2017 4:55 PM
+%%%-------------------------------------------------------------------
+-module(dubbo_zookeeper_tests).
+-author("dlive").
+
+-include_lib("eunit/include/eunit.hrl").
+-include("dubbo.hrl").
+exist_test() ->
+ Consumer=#consumer_config{interface = <<"com.ifcoder.demo.facade.User">>,
+ methods = [<<"a">>,<<"b">>]},
+ V= dubbo_zookeeper:gen_consumer_node_info(Consumer),
+ io:format(user,"gen consumer info:~p~n",[V]),
+ ?assert(true).