schleyfox-ernie 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +2 -0
- data/History.txt +40 -0
- data/LICENSE +20 -0
- data/README.md +306 -0
- data/Rakefile +67 -0
- data/VERSION.yml +5 -0
- data/bin/ernie +116 -0
- data/contrib/ebench.erl +76 -0
- data/ebin/ernie_server_app.app +2 -0
- data/elib/asset_pool.erl +155 -0
- data/elib/asset_pool_sup.erl +13 -0
- data/elib/bert.erl +69 -0
- data/elib/ernie.hrl +18 -0
- data/elib/ernie_access_logger.erl +170 -0
- data/elib/ernie_access_logger_sup.erl +15 -0
- data/elib/ernie_admin.erl +60 -0
- data/elib/ernie_config.erl +30 -0
- data/elib/ernie_native.erl +26 -0
- data/elib/ernie_server.erl +326 -0
- data/elib/ernie_server_app.erl +20 -0
- data/elib/ernie_server_sup.erl +22 -0
- data/elib/logger.erl +108 -0
- data/elib/logger_sup.erl +13 -0
- data/elib/port_wrapper.erl +65 -0
- data/ernie.gemspec +94 -0
- data/examples/example.cfg +12 -0
- data/examples/ext.erl +8 -0
- data/examples/ext.rb +39 -0
- data/examples/nat.erl +12 -0
- data/ext/Makefile +2 -0
- data/ext/extconf.rb +1 -0
- data/lib/ernie.rb +225 -0
- data/test/ernie_server_test.rb +77 -0
- data/test/ernie_test.rb +43 -0
- data/test/helper.rb +17 -0
- data/test/load.rb +18 -0
- data/test/sample/ext.rb +42 -0
- data/test/sample/sample.cfg +4 -0
- metadata +134 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
-module(ernie_server).
|
|
2
|
+
-behaviour(gen_server).
|
|
3
|
+
-include_lib("ernie.hrl").
|
|
4
|
+
|
|
5
|
+
%% api
|
|
6
|
+
-export([start_link/1, start/1, process/1, kick/0]).
|
|
7
|
+
|
|
8
|
+
%% gen_server callbacks
|
|
9
|
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
|
10
|
+
terminate/2, code_change/3]).
|
|
11
|
+
|
|
12
|
+
%%====================================================================
|
|
13
|
+
%% API
|
|
14
|
+
%%====================================================================
|
|
15
|
+
|
|
16
|
+
start_link(Args) ->
|
|
17
|
+
gen_server:start_link({global, ?MODULE}, ?MODULE, Args, []).
|
|
18
|
+
|
|
19
|
+
start(Args) ->
|
|
20
|
+
gen_server:start({global, ?MODULE}, ?MODULE, Args, []).
|
|
21
|
+
|
|
22
|
+
process(Sock) ->
|
|
23
|
+
gen_server:cast({global, ?MODULE}, {process, Sock}).
|
|
24
|
+
|
|
25
|
+
kick() ->
|
|
26
|
+
gen_server:cast({global, ?MODULE}, kick).
|
|
27
|
+
|
|
28
|
+
%%====================================================================
|
|
29
|
+
%% gen_server callbacks
|
|
30
|
+
%%====================================================================
|
|
31
|
+
|
|
32
|
+
%%--------------------------------------------------------------------
|
|
33
|
+
%% Function: init(Args) -> {ok, State} |
|
|
34
|
+
%% {ok, State, Timeout} |
|
|
35
|
+
%% ignore |
|
|
36
|
+
%% {stop, Reason}
|
|
37
|
+
%% Description: Initiates the server
|
|
38
|
+
%%--------------------------------------------------------------------
|
|
39
|
+
init([Port, Configs]) ->
|
|
40
|
+
process_flag(trap_exit, true),
|
|
41
|
+
error_logger:info_msg("~p starting~n", [?MODULE]),
|
|
42
|
+
{ok, LSock} = try_listen(Port, 500),
|
|
43
|
+
spawn(fun() -> loop(LSock) end),
|
|
44
|
+
Map = init_map(Configs),
|
|
45
|
+
run_init_functions(Configs),
|
|
46
|
+
io:format("pidmap = ~p~n", [Map]),
|
|
47
|
+
{ok, #state{lsock = LSock, map = Map}}.
|
|
48
|
+
|
|
49
|
+
%%--------------------------------------------------------------------
|
|
50
|
+
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
|
51
|
+
%% {reply, Reply, State, Timeout} |
|
|
52
|
+
%% {noreply, State} |
|
|
53
|
+
%% {noreply, State, Timeout} |
|
|
54
|
+
%% {stop, Reason, Reply, State} |
|
|
55
|
+
%% {stop, Reason, State}
|
|
56
|
+
%% Description: Handling call messages
|
|
57
|
+
%%--------------------------------------------------------------------
|
|
58
|
+
handle_call(_Request, _From, State) ->
|
|
59
|
+
{reply, ok, State}.
|
|
60
|
+
|
|
61
|
+
%%--------------------------------------------------------------------
|
|
62
|
+
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
|
63
|
+
%% {noreply, State, Timeout} |
|
|
64
|
+
%% {stop, Reason, State}
|
|
65
|
+
%% Description: Handling cast messages
|
|
66
|
+
%%--------------------------------------------------------------------
|
|
67
|
+
handle_cast({process, Sock}, State) ->
|
|
68
|
+
Log = #log{hq = queue:len(State#state.hq),
|
|
69
|
+
lq = queue:len(State#state.lq),
|
|
70
|
+
taccept = erlang:now()},
|
|
71
|
+
Request = #request{sock = Sock, log = Log},
|
|
72
|
+
State2 = receive_term(Request, State),
|
|
73
|
+
{noreply, State2};
|
|
74
|
+
handle_cast(kick, State) ->
|
|
75
|
+
case queue:out(State#state.hq) of
|
|
76
|
+
{{value, Request}, Hq2} ->
|
|
77
|
+
State2 = process_request(Request, hq, Hq2, State),
|
|
78
|
+
{noreply, State2};
|
|
79
|
+
{empty, _Hq} ->
|
|
80
|
+
case queue:out(State#state.lq) of
|
|
81
|
+
{{value, Request}, Lq2} ->
|
|
82
|
+
State2 = process_request(Request, lq, Lq2, State),
|
|
83
|
+
{noreply, State2};
|
|
84
|
+
{empty, _Lq} ->
|
|
85
|
+
{noreply, State}
|
|
86
|
+
end
|
|
87
|
+
end;
|
|
88
|
+
handle_cast(_Msg, State) -> {noreply, State}.
|
|
89
|
+
|
|
90
|
+
handle_info(Msg, State) ->
|
|
91
|
+
error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
|
|
92
|
+
{noreply, State}.
|
|
93
|
+
|
|
94
|
+
terminate(_Reason, _State) -> ok.
|
|
95
|
+
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
|
96
|
+
|
|
97
|
+
%%====================================================================
|
|
98
|
+
%% Internal
|
|
99
|
+
%%====================================================================
|
|
100
|
+
|
|
101
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
102
|
+
% Module mapping
|
|
103
|
+
|
|
104
|
+
init_map(Configs) ->
|
|
105
|
+
lists:map((fun extract_mapping/1), Configs).
|
|
106
|
+
|
|
107
|
+
extract_mapping(Config) ->
|
|
108
|
+
Id = proplists:get_value(id, Config),
|
|
109
|
+
Mod = proplists:get_value(module, Config),
|
|
110
|
+
{Mod, Id}.
|
|
111
|
+
|
|
112
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
113
|
+
% Init Function
|
|
114
|
+
|
|
115
|
+
run_init_functions(Configs) ->
|
|
116
|
+
lists:foreach(fun(Config) ->
|
|
117
|
+
case proplists:get_value(init, Config) of
|
|
118
|
+
undefined -> undefined;
|
|
119
|
+
{Mod, Func, Args} -> apply(Mod, Func, Args)
|
|
120
|
+
end
|
|
121
|
+
end,
|
|
122
|
+
Configs).
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
126
|
+
% Listen and loop
|
|
127
|
+
|
|
128
|
+
try_listen(Port, 0) ->
|
|
129
|
+
error_logger:error_msg("Could not listen on port ~p~n", [Port]),
|
|
130
|
+
{error, "Could not listen on port"};
|
|
131
|
+
try_listen(Port, Times) ->
|
|
132
|
+
Res = gen_tcp:listen(Port, [binary, {packet, 4}, {active, false}, {reuseaddr, true}]),
|
|
133
|
+
case Res of
|
|
134
|
+
{ok, LSock} ->
|
|
135
|
+
error_logger:info_msg("Listening on port ~p~n", [Port]),
|
|
136
|
+
{ok, LSock};
|
|
137
|
+
{error, Reason} ->
|
|
138
|
+
error_logger:info_msg("Could not listen on port ~p: ~p~n", [Port, Reason]),
|
|
139
|
+
timer:sleep(5000),
|
|
140
|
+
try_listen(Port, Times - 1)
|
|
141
|
+
end.
|
|
142
|
+
|
|
143
|
+
loop(LSock) ->
|
|
144
|
+
{ok, Sock} = gen_tcp:accept(LSock),
|
|
145
|
+
logger:debug("Accepted socket: ~p~n", [Sock]),
|
|
146
|
+
ernie_server:process(Sock),
|
|
147
|
+
loop(LSock).
|
|
148
|
+
|
|
149
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
150
|
+
% Receive and process
|
|
151
|
+
|
|
152
|
+
receive_term(Request, State) ->
|
|
153
|
+
Sock = Request#request.sock,
|
|
154
|
+
case gen_tcp:recv(Sock, 0) of
|
|
155
|
+
{ok, BinaryTerm} ->
|
|
156
|
+
logger:debug("Got binary term: ~p~n", [BinaryTerm]),
|
|
157
|
+
Term = binary_to_term(BinaryTerm),
|
|
158
|
+
logger:info("Got term: ~p~n", [Term]),
|
|
159
|
+
case Term of
|
|
160
|
+
{call, '__admin__', Fun, Args} ->
|
|
161
|
+
ernie_admin:process(Sock, Fun, Args, State);
|
|
162
|
+
{info, Command, Args} ->
|
|
163
|
+
Infos = Request#request.infos,
|
|
164
|
+
Infos2 = [BinaryTerm | Infos],
|
|
165
|
+
Request2 = Request#request{infos = Infos2},
|
|
166
|
+
Request3 = process_info(Request2, Command, Args),
|
|
167
|
+
receive_term(Request3, State);
|
|
168
|
+
_Any ->
|
|
169
|
+
Request2 = Request#request{action = BinaryTerm},
|
|
170
|
+
close_if_cast(Term, Request2),
|
|
171
|
+
case Request2#request.priority of
|
|
172
|
+
high ->
|
|
173
|
+
Hq2 = queue:in(Request2, State#state.hq),
|
|
174
|
+
Lq2 = State#state.lq;
|
|
175
|
+
low ->
|
|
176
|
+
Hq2 = State#state.hq,
|
|
177
|
+
Lq2 = queue:in(Request2, State#state.lq)
|
|
178
|
+
end,
|
|
179
|
+
ernie_server:kick(),
|
|
180
|
+
State#state{hq = Hq2, lq = Lq2}
|
|
181
|
+
end;
|
|
182
|
+
{error, closed} ->
|
|
183
|
+
ok = gen_tcp:close(Sock),
|
|
184
|
+
State
|
|
185
|
+
end.
|
|
186
|
+
|
|
187
|
+
process_info(Request, priority, [Priority]) ->
|
|
188
|
+
Request#request{priority = Priority};
|
|
189
|
+
process_info(Request, _Command, _Args) ->
|
|
190
|
+
Request.
|
|
191
|
+
|
|
192
|
+
process_request(Request, Priority, Q2, State) ->
|
|
193
|
+
ActionTerm = bert:decode(Request#request.action),
|
|
194
|
+
{_Type, Mod, _Fun, _Args} = ActionTerm,
|
|
195
|
+
Specs = lists:filter(fun({X, _Id}) -> Mod =:= X end, State#state.map),
|
|
196
|
+
process_module(ActionTerm, Specs, Request, Priority, Q2, State).
|
|
197
|
+
|
|
198
|
+
process_module(ActionTerm, [], Request, Priority, Q2, State) ->
|
|
199
|
+
{_Type, Mod, _Fun, _Args} = ActionTerm,
|
|
200
|
+
logger:debug("No such module ~p~n", [Mod]),
|
|
201
|
+
Sock = Request#request.sock,
|
|
202
|
+
Class = <<"ServerError">>,
|
|
203
|
+
Message = list_to_binary(io_lib:format("No such module '~p'", [Mod])),
|
|
204
|
+
gen_tcp:send(Sock, term_to_binary({error, [server, 0, Class, Message, []]})),
|
|
205
|
+
ok = gen_tcp:close(Sock),
|
|
206
|
+
finish(Priority, Q2, State);
|
|
207
|
+
process_module(ActionTerm, Specs, Request, Priority, Q2, State) ->
|
|
208
|
+
[{_Mod, Id} | OtherSpecs] = Specs,
|
|
209
|
+
case Id of
|
|
210
|
+
native ->
|
|
211
|
+
logger:debug("Dispatching to native module~n", []),
|
|
212
|
+
{_Type, Mod, Fun, Args} = ActionTerm,
|
|
213
|
+
case erlang:function_exported(Mod, Fun, length(Args)) of
|
|
214
|
+
false ->
|
|
215
|
+
logger:debug("Not found in native module ~p~n", [Mod]),
|
|
216
|
+
process_module(ActionTerm, OtherSpecs, Request, Priority, Q2, State);
|
|
217
|
+
true ->
|
|
218
|
+
PredFun = list_to_atom(atom_to_list(Fun) ++ "_pred"),
|
|
219
|
+
logger:debug("Checking ~p:~p(~p) for selection.~n", [Mod, PredFun, Args]),
|
|
220
|
+
case erlang:function_exported(Mod, PredFun, length(Args)) of
|
|
221
|
+
false ->
|
|
222
|
+
logger:debug("No such predicate function ~p:~p(~p).~n", [Mod, PredFun, Args]),
|
|
223
|
+
process_native_request(ActionTerm, Request, Priority, Q2, State);
|
|
224
|
+
true ->
|
|
225
|
+
case apply(Mod, PredFun, Args) of
|
|
226
|
+
false ->
|
|
227
|
+
logger:debug("Predicate ~p:~p(~p) returned false.~n", [Mod, PredFun, Args]),
|
|
228
|
+
process_module(ActionTerm, OtherSpecs, Request, Priority, Q2, State);
|
|
229
|
+
true ->
|
|
230
|
+
logger:debug("Predicate ~p:~p(~p) returned true.~n", [Mod, PredFun, Args]),
|
|
231
|
+
process_native_request(ActionTerm, Request, Priority, Q2, State)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end;
|
|
235
|
+
ValidPid when is_pid(ValidPid) ->
|
|
236
|
+
logger:debug("Found external pid ~p~n", [ValidPid]),
|
|
237
|
+
process_external_request(ValidPid, Request, Priority, Q2, State)
|
|
238
|
+
end.
|
|
239
|
+
|
|
240
|
+
close_if_cast(ActionTerm, Request) ->
|
|
241
|
+
case ActionTerm of
|
|
242
|
+
{cast, _Mod, _Fun, _Args} ->
|
|
243
|
+
Sock = Request#request.sock,
|
|
244
|
+
gen_tcp:send(Sock, term_to_binary({noreply})),
|
|
245
|
+
ok = gen_tcp:close(Sock),
|
|
246
|
+
logger:debug("Closed cast.~n", []);
|
|
247
|
+
_Any ->
|
|
248
|
+
ok
|
|
249
|
+
end.
|
|
250
|
+
|
|
251
|
+
finish(Priority, Q2, State) ->
|
|
252
|
+
case Priority of
|
|
253
|
+
hq -> State#state{hq = Q2};
|
|
254
|
+
lq -> State#state{lq = Q2}
|
|
255
|
+
end.
|
|
256
|
+
|
|
257
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
258
|
+
% Native
|
|
259
|
+
|
|
260
|
+
process_native_request(ActionTerm, Request, Priority, Q2, State) ->
|
|
261
|
+
Count = State#state.count,
|
|
262
|
+
State2 = State#state{count = Count + 1},
|
|
263
|
+
logger:debug("Count = ~p~n", [Count + 1]),
|
|
264
|
+
Log = Request#request.log,
|
|
265
|
+
Log2 = Log#log{type = native, tprocess = erlang:now()},
|
|
266
|
+
Request2 = Request#request{log = Log2},
|
|
267
|
+
spawn(fun() -> ernie_native:process(ActionTerm, Request2) end),
|
|
268
|
+
finish(Priority, Q2, State2).
|
|
269
|
+
|
|
270
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
271
|
+
% External
|
|
272
|
+
|
|
273
|
+
process_external_request(Pid, Request, Priority, Q2, State) ->
|
|
274
|
+
Count = State#state.count,
|
|
275
|
+
State2 = State#state{count = Count + 1},
|
|
276
|
+
logger:debug("Count = ~p~n", [Count + 1]),
|
|
277
|
+
case asset_pool:lease(Pid) of
|
|
278
|
+
{ok, Asset} ->
|
|
279
|
+
logger:debug("Leased asset for pool ~p~n", [Pid]),
|
|
280
|
+
Log = Request#request.log,
|
|
281
|
+
Log2 = Log#log{type = external, tprocess = erlang:now()},
|
|
282
|
+
Request2 = Request#request{log = Log2},
|
|
283
|
+
spawn(fun() -> process_now(Pid, Request2, Asset) end),
|
|
284
|
+
finish(Priority, Q2, State2);
|
|
285
|
+
empty ->
|
|
286
|
+
State
|
|
287
|
+
end.
|
|
288
|
+
|
|
289
|
+
process_now(Pid, Request, Asset) ->
|
|
290
|
+
try unsafe_process_now(Request, Asset) of
|
|
291
|
+
_AnyResponse ->
|
|
292
|
+
Log = Request#request.log,
|
|
293
|
+
Log2 = Log#log{tdone = erlang:now()},
|
|
294
|
+
Request2 = Request#request{log = Log2},
|
|
295
|
+
ernie_access_logger:acc(Request2)
|
|
296
|
+
catch
|
|
297
|
+
AnyClass:AnyError ->
|
|
298
|
+
Log = Request#request.log,
|
|
299
|
+
Log2 = Log#log{tdone = erlang:now()},
|
|
300
|
+
Request2 = Request#request{log = Log2},
|
|
301
|
+
ernie_access_logger:err(Request2, "External process error ~w: ~w", [AnyClass, AnyError])
|
|
302
|
+
after
|
|
303
|
+
asset_pool:return(Pid, Asset),
|
|
304
|
+
ernie_server:kick(),
|
|
305
|
+
logger:debug("Returned asset ~p~n", [Asset]),
|
|
306
|
+
gen_tcp:close(Request#request.sock),
|
|
307
|
+
logger:debug("Closed socket ~p~n", [Request#request.sock])
|
|
308
|
+
end.
|
|
309
|
+
|
|
310
|
+
unsafe_process_now(Request, Asset) ->
|
|
311
|
+
BinaryTerm = Request#request.action,
|
|
312
|
+
Term = binary_to_term(BinaryTerm),
|
|
313
|
+
case Term of
|
|
314
|
+
{call, Mod, Fun, Args} ->
|
|
315
|
+
logger:debug("Calling ~p:~p(~p)~n", [Mod, Fun, Args]),
|
|
316
|
+
Sock = Request#request.sock,
|
|
317
|
+
{asset, Port, Token} = Asset,
|
|
318
|
+
logger:debug("Asset: ~p ~p~n", [Port, Token]),
|
|
319
|
+
{ok, Data} = port_wrapper:rpc(Port, BinaryTerm),
|
|
320
|
+
ok = gen_tcp:send(Sock, Data);
|
|
321
|
+
{cast, Mod, Fun, Args} ->
|
|
322
|
+
logger:debug("Casting ~p:~p(~p)~n", [Mod, Fun, Args]),
|
|
323
|
+
{asset, Port, Token} = Asset,
|
|
324
|
+
logger:debug("Asset: ~p ~p~n", [Port, Token]),
|
|
325
|
+
{ok, _Data} = port_wrapper:rpc(Port, BinaryTerm)
|
|
326
|
+
end.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-module(ernie_server_app).
|
|
2
|
+
-behaviour(application).
|
|
3
|
+
|
|
4
|
+
-export([boot/0, start/2, stop/1]).
|
|
5
|
+
|
|
6
|
+
boot() ->
|
|
7
|
+
application:start(ernie_server_app).
|
|
8
|
+
|
|
9
|
+
start(_Type, _Args) ->
|
|
10
|
+
case application:get_env(ernie_server_app, access_log) of
|
|
11
|
+
{ok, AccessFile} ->
|
|
12
|
+
ernie_access_logger_sup:start_link(AccessFile);
|
|
13
|
+
undefined ->
|
|
14
|
+
ernie_access_logger_sup:start_link(undefined)
|
|
15
|
+
end,
|
|
16
|
+
logger_sup:start_link(),
|
|
17
|
+
ernie_server_sup:start_link().
|
|
18
|
+
|
|
19
|
+
stop(_State) ->
|
|
20
|
+
ok.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-module(ernie_server_sup).
|
|
2
|
+
-behaviour(supervisor).
|
|
3
|
+
-export([start_link/0, init/1]).
|
|
4
|
+
|
|
5
|
+
start_link() ->
|
|
6
|
+
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
|
7
|
+
|
|
8
|
+
init([]) ->
|
|
9
|
+
{ok, Port} = application:get_env(ernie_server_app, port),
|
|
10
|
+
io:format("Using port ~p~n", [Port]),
|
|
11
|
+
case application:get_env(ernie_server_app, pidfile) of
|
|
12
|
+
{ok, Location} ->
|
|
13
|
+
Pid = os:getpid(),
|
|
14
|
+
ok = file:write_file(Location, list_to_binary(Pid));
|
|
15
|
+
undefined -> ok
|
|
16
|
+
end,
|
|
17
|
+
{ok, Config} = application:get_env(ernie_server_app, config),
|
|
18
|
+
{ok, Configs} = ernie_config:load(Config),
|
|
19
|
+
io:format("~p~n", [Configs]),
|
|
20
|
+
{ok, {{one_for_one, 1, 60},
|
|
21
|
+
[{ernie_server, {ernie_server, start_link, [[Port, Configs]]},
|
|
22
|
+
permanent, brutal_kill, worker, [ernie_server]}]}}.
|
data/elib/logger.erl
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
-module(logger).
|
|
2
|
+
-behaviour(gen_server).
|
|
3
|
+
|
|
4
|
+
%% api
|
|
5
|
+
-export([start_link/1, start/1, set_log_level/1, debug/2, info/2, warn/2, error/2, fatal/2]).
|
|
6
|
+
|
|
7
|
+
%% gen_server callbacks
|
|
8
|
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
|
9
|
+
terminate/2, code_change/3]).
|
|
10
|
+
|
|
11
|
+
-record(state, {log_level = undefined}).
|
|
12
|
+
|
|
13
|
+
%%====================================================================
|
|
14
|
+
%% API
|
|
15
|
+
%%====================================================================
|
|
16
|
+
|
|
17
|
+
start_link(Args) ->
|
|
18
|
+
gen_server:start_link({global, ?MODULE}, ?MODULE, Args, []).
|
|
19
|
+
|
|
20
|
+
start(Args) ->
|
|
21
|
+
gen_server:start({global, ?MODULE}, ?MODULE, Args, []).
|
|
22
|
+
|
|
23
|
+
set_log_level(Level) ->
|
|
24
|
+
gen_server:call({global, ?MODULE}, {set_log_level, Level}).
|
|
25
|
+
|
|
26
|
+
debug(Msg, Args) ->
|
|
27
|
+
gen_server:cast({global, ?MODULE}, {debug, Msg, Args}).
|
|
28
|
+
|
|
29
|
+
info(Msg, Args) ->
|
|
30
|
+
gen_server:cast({global, ?MODULE}, {info, Msg, Args}).
|
|
31
|
+
|
|
32
|
+
warn(Msg, Args) ->
|
|
33
|
+
gen_server:cast({global, ?MODULE}, {warn, Msg, Args}).
|
|
34
|
+
|
|
35
|
+
error(Msg, Args) ->
|
|
36
|
+
gen_server:cast({global, ?MODULE}, {error, Msg, Args}).
|
|
37
|
+
|
|
38
|
+
fatal(Msg, Args) ->
|
|
39
|
+
gen_server:cast({global, ?MODULE}, {fatal, Msg, Args}).
|
|
40
|
+
|
|
41
|
+
%%====================================================================
|
|
42
|
+
%% gen_server callbacks
|
|
43
|
+
%%====================================================================
|
|
44
|
+
|
|
45
|
+
%%--------------------------------------------------------------------
|
|
46
|
+
%% Function: init(Args) -> {ok, State} |
|
|
47
|
+
%% {ok, State, Timeout} |
|
|
48
|
+
%% ignore |
|
|
49
|
+
%% {stop, Reason}
|
|
50
|
+
%% Description: Initiates the server
|
|
51
|
+
%%--------------------------------------------------------------------
|
|
52
|
+
init([LogLevel]) ->
|
|
53
|
+
error_logger:info_msg("~p starting~n", [?MODULE]),
|
|
54
|
+
{ok, #state{log_level = LogLevel}}.
|
|
55
|
+
|
|
56
|
+
%%--------------------------------------------------------------------
|
|
57
|
+
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
|
58
|
+
%% {reply, Reply, State, Timeout} |
|
|
59
|
+
%% {noreply, State} |
|
|
60
|
+
%% {noreply, State, Timeout} |
|
|
61
|
+
%% {stop, Reason, Reply, State} |
|
|
62
|
+
%% {stop, Reason, State}
|
|
63
|
+
%% Description: Handling call messages
|
|
64
|
+
%%--------------------------------------------------------------------
|
|
65
|
+
handle_call({set_log_level, Level}, _From, State) ->
|
|
66
|
+
{reply, ok, State#state{log_level = Level}};
|
|
67
|
+
handle_call(_Request, _From, State) ->
|
|
68
|
+
{reply, ok, State}.
|
|
69
|
+
|
|
70
|
+
%%--------------------------------------------------------------------
|
|
71
|
+
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
|
72
|
+
%% {noreply, State, Timeout} |
|
|
73
|
+
%% {stop, Reason, State}
|
|
74
|
+
%% Description: Handling cast messages
|
|
75
|
+
%%--------------------------------------------------------------------
|
|
76
|
+
handle_cast({debug, Msg, Args}, State) ->
|
|
77
|
+
log(State#state.log_level, 4, Msg, Args),
|
|
78
|
+
{noreply, State};
|
|
79
|
+
handle_cast({info, Msg, Args}, State) ->
|
|
80
|
+
log(State#state.log_level, 3, Msg, Args),
|
|
81
|
+
{noreply, State};
|
|
82
|
+
handle_cast({warn, Msg, Args}, State) ->
|
|
83
|
+
log(State#state.log_level, 2, Msg, Args),
|
|
84
|
+
{noreply, State};
|
|
85
|
+
handle_cast({error, Msg, Args}, State) ->
|
|
86
|
+
log(State#state.log_level, 1, Msg, Args),
|
|
87
|
+
{noreply, State};
|
|
88
|
+
handle_cast({fatal, Msg, Args}, State) ->
|
|
89
|
+
log(State#state.log_level, 0, Msg, Args),
|
|
90
|
+
{noreply, State};
|
|
91
|
+
handle_cast(_Msg, State) -> {noreply, State}.
|
|
92
|
+
|
|
93
|
+
handle_info(Msg, State) ->
|
|
94
|
+
error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
|
|
95
|
+
{noreply, State}.
|
|
96
|
+
|
|
97
|
+
terminate(_Reason, _State) -> ok.
|
|
98
|
+
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
|
99
|
+
|
|
100
|
+
%%====================================================================
|
|
101
|
+
%% Internal
|
|
102
|
+
%%====================================================================
|
|
103
|
+
|
|
104
|
+
log(SystemLogLevel, MessageLogLevel, Message, Args) ->
|
|
105
|
+
case SystemLogLevel >= MessageLogLevel of
|
|
106
|
+
false -> ok;
|
|
107
|
+
true -> io:format(Message, Args)
|
|
108
|
+
end.
|
data/elib/logger_sup.erl
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-module(logger_sup).
|
|
2
|
+
-behaviour(supervisor).
|
|
3
|
+
-export([start_link/0, init/1]).
|
|
4
|
+
|
|
5
|
+
start_link() ->
|
|
6
|
+
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
|
7
|
+
|
|
8
|
+
init([]) ->
|
|
9
|
+
{ok, LogLevel} = application:get_env(ernie_server_app, log_level),
|
|
10
|
+
io:format("Using log level ~p~n", [LogLevel]),
|
|
11
|
+
{ok, {{one_for_one, 1, 60},
|
|
12
|
+
[{logger, {logger, start_link, [[LogLevel]]},
|
|
13
|
+
permanent, brutal_kill, worker, [logger]}]}}.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
-module(port_wrapper).
|
|
2
|
+
-export([wrap/1, wrap/2, wrap_link/1, wrap_link/2, send/2, shutdown/1, close/1, rpc/2]).
|
|
3
|
+
|
|
4
|
+
wrap(Command) ->
|
|
5
|
+
spawn(fun() -> process_flag(trap_exit, true), Port = create_port(Command), loop(Port, infinity, Command) end).
|
|
6
|
+
wrap(Command, Timeout) ->
|
|
7
|
+
spawn(fun() -> process_flag(trap_exit, true), Port = create_port(Command), loop(Port, Timeout, Command) end).
|
|
8
|
+
|
|
9
|
+
wrap_link(Command) ->
|
|
10
|
+
spawn_link(fun() -> process_flag(trap_exit, true), Port = create_port(Command), link(Port), loop(Port, infinity, Command) end).
|
|
11
|
+
wrap_link(Command, Timeout) ->
|
|
12
|
+
spawn_link(fun() -> process_flag(trap_exit, true), Port = create_port(Command), link(Port), loop(Port, Timeout, Command) end).
|
|
13
|
+
|
|
14
|
+
rpc(WrappedPort, Message) ->
|
|
15
|
+
send(WrappedPort, Message),
|
|
16
|
+
receive
|
|
17
|
+
{WrappedPort, Result} -> {ok, Result}
|
|
18
|
+
end.
|
|
19
|
+
|
|
20
|
+
send(WrappedPort, Message) ->
|
|
21
|
+
WrappedPort ! {self(), {command, Message}},
|
|
22
|
+
WrappedPort.
|
|
23
|
+
|
|
24
|
+
shutdown(WrappedPort) ->
|
|
25
|
+
WrappedPort ! shutdown,
|
|
26
|
+
true.
|
|
27
|
+
|
|
28
|
+
close(WrappedPort) ->
|
|
29
|
+
WrappedPort ! noose,
|
|
30
|
+
true.
|
|
31
|
+
|
|
32
|
+
create_port(Command) ->
|
|
33
|
+
open_port({spawn, Command}, [{packet, 4}, nouse_stdio, exit_status, binary]).
|
|
34
|
+
|
|
35
|
+
loop(Port, Timeout, Command) ->
|
|
36
|
+
receive
|
|
37
|
+
noose ->
|
|
38
|
+
port_close(Port),
|
|
39
|
+
noose;
|
|
40
|
+
shutdown ->
|
|
41
|
+
port_close(Port),
|
|
42
|
+
exit(shutdown);
|
|
43
|
+
{Source, {command, Message}} ->
|
|
44
|
+
Port ! {self(), {command, Message}},
|
|
45
|
+
receive
|
|
46
|
+
{Port, {data, Result}} ->
|
|
47
|
+
Source ! {self(), Result}
|
|
48
|
+
after Timeout ->
|
|
49
|
+
error_logger:error_msg("Port Wrapper ~p timed out in mid operation (~p)!~n", [self(),Message]),
|
|
50
|
+
% We timed out, which means we need to close and then restart the port
|
|
51
|
+
port_close(Port), % Should SIGPIPE the child.
|
|
52
|
+
exit(timed_out)
|
|
53
|
+
end,
|
|
54
|
+
loop(Port,Timeout,Command);
|
|
55
|
+
{Port, {exit_status, _Code}} ->
|
|
56
|
+
% Hard and Unanticipated Crash
|
|
57
|
+
error_logger:error_msg( "Port closed! ~p~n", [Port] ),
|
|
58
|
+
exit({error, _Code});
|
|
59
|
+
{'EXIT',_Pid,shutdown} ->
|
|
60
|
+
port_close(Port),
|
|
61
|
+
exit(shutdown);
|
|
62
|
+
Any ->
|
|
63
|
+
error_logger:warning_msg("PortWrapper ~p got unexpected message: ~p~n", [self(), Any]),
|
|
64
|
+
loop(Port, Timeout, Command)
|
|
65
|
+
end.
|
data/ernie.gemspec
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{schleyfox-ernie}
|
|
8
|
+
s.version = "2.2.1"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["Tom Preston-Werner"]
|
|
12
|
+
s.date = %q{2010-03-12}
|
|
13
|
+
s.default_executable = %q{ernie}
|
|
14
|
+
s.description = %q{Ernie is an Erlang/Ruby hybrid BERT-RPC server implementation packaged as a gem.}
|
|
15
|
+
s.email = %q{tom@mojombo.com}
|
|
16
|
+
s.executables = ["ernie"]
|
|
17
|
+
s.extensions = ["ext/extconf.rb", "ext/extconf.rb"]
|
|
18
|
+
s.extra_rdoc_files = [
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"README.md"
|
|
21
|
+
]
|
|
22
|
+
s.files = [
|
|
23
|
+
".document",
|
|
24
|
+
".gitignore",
|
|
25
|
+
"History.txt",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md",
|
|
28
|
+
"Rakefile",
|
|
29
|
+
"VERSION.yml",
|
|
30
|
+
"bin/ernie",
|
|
31
|
+
"contrib/ebench.erl",
|
|
32
|
+
"ebin/ernie_server_app.app",
|
|
33
|
+
"elib/asset_pool.erl",
|
|
34
|
+
"elib/asset_pool_sup.erl",
|
|
35
|
+
"elib/bert.erl",
|
|
36
|
+
"elib/ernie.hrl",
|
|
37
|
+
"elib/ernie_access_logger.erl",
|
|
38
|
+
"elib/ernie_access_logger_sup.erl",
|
|
39
|
+
"elib/ernie_admin.erl",
|
|
40
|
+
"elib/ernie_config.erl",
|
|
41
|
+
"elib/ernie_native.erl",
|
|
42
|
+
"elib/ernie_server.erl",
|
|
43
|
+
"elib/ernie_server_app.erl",
|
|
44
|
+
"elib/ernie_server_sup.erl",
|
|
45
|
+
"elib/logger.erl",
|
|
46
|
+
"elib/logger_sup.erl",
|
|
47
|
+
"elib/port_wrapper.erl",
|
|
48
|
+
"ernie.gemspec",
|
|
49
|
+
"examples/example.cfg",
|
|
50
|
+
"examples/ext.erl",
|
|
51
|
+
"examples/ext.rb",
|
|
52
|
+
"examples/nat.erl",
|
|
53
|
+
"ext/Makefile",
|
|
54
|
+
"ext/extconf.rb",
|
|
55
|
+
"lib/ernie.rb",
|
|
56
|
+
"test/ernie_server_test.rb",
|
|
57
|
+
"test/ernie_test.rb",
|
|
58
|
+
"test/helper.rb",
|
|
59
|
+
"test/load.rb",
|
|
60
|
+
"test/sample/ext.rb",
|
|
61
|
+
"test/sample/sample.cfg"
|
|
62
|
+
]
|
|
63
|
+
s.homepage = %q{http://github.com/mojombo/ernie}
|
|
64
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
65
|
+
s.require_paths = ["lib"]
|
|
66
|
+
s.rubyforge_project = %q{ernie}
|
|
67
|
+
s.rubygems_version = %q{1.3.5}
|
|
68
|
+
s.summary = %q{Ernie is a BERT-RPC server implementation.}
|
|
69
|
+
s.test_files = [
|
|
70
|
+
"test/ernie_server_test.rb",
|
|
71
|
+
"test/ernie_test.rb",
|
|
72
|
+
"test/helper.rb",
|
|
73
|
+
"test/load.rb",
|
|
74
|
+
"test/sample/ext.rb",
|
|
75
|
+
"examples/ext.rb"
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
if s.respond_to? :specification_version then
|
|
79
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
80
|
+
s.specification_version = 3
|
|
81
|
+
|
|
82
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
|
83
|
+
s.add_runtime_dependency(%q<bert>, [">= 1.1.0"])
|
|
84
|
+
s.add_runtime_dependency(%q<bertrpc>, [">= 1.0.0"])
|
|
85
|
+
else
|
|
86
|
+
s.add_dependency(%q<bert>, [">= 1.1.0"])
|
|
87
|
+
s.add_dependency(%q<bertrpc>, [">= 1.0.0"])
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
s.add_dependency(%q<bert>, [">= 1.1.0"])
|
|
91
|
+
s.add_dependency(%q<bertrpc>, [">= 1.0.0"])
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|