Related
I was following this example of executing periodic tasks given by Hynek -Pichi- Vychodil here.
And i ran into a problem as I wanted to pass some Arguments also to the start_link function which would be used inside my do_task() function. But as given here the start_link/4 needs to return {ok,Pid} and in my case it is returning {ok,{Ref,Arguments}} and thus is failing.
How I can I fix this up.Here's my code:
start_link(Period,SERVER,Args) when Period > 0, is_integer(Period) ->
gen_server:start_link({local, SERVER}, ?MODULE, [Period,Args], []).
init([Period,Args]) ->
StartT = erlang:monotonic_time(millisecond),
self() ! tick,
{ok, {StartT, Period,Args}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(tick, {StartT, Period,Args} = S) ->
Next = Period - (erlang:monotonic_time(millisecond)-StartT) rem Period,
_Timer = erlang:send_after(Next, self(), tick),
do_task(Args),
{ok, S};
handle_info(_Info, State) ->
{noreply, State}.
Here
Period is->30000
and Arguments is -> {A,[a,b],'something'}
And here is the crash log
[error] gen_server '95ef60ae-b2fa-491a-821d-ffae85cc57f6' terminated with reason: bad return value: {ok,{-576460723187,30000,{A,[a,b],'something'}}
handle_info cannot return an ok tuple.
(Answer given as a comment.)
I want to write a module which need to be able to push message to pusher in Erlang. I found this repo https://github.com/bradfordw/pusherl, which is not maintained anymore, but I think I can adapt to make it work. I followed the README, and I ran successfully all the Rebar command. Then I open the rebar console by the command
./rel/pusherl/bin/pusherl console
So the server should start. But then when I try to call
gen_server:call(pusherl_server, {push, {"ChannelName", "EventName", "Payload"}}).
Then it throws error:
exception exit: {noproc,{gen_server,call,
[pusherl_server,
{push,
{"ChannelName","EventName","Payload"}}]}}
in function gen_server:call/2 (gen_server.erl, line 182)
I'm quite new with Erlang and OTP, and it takes me half of the day to make it work but not successful. Please help me to solve this. I really appreciate.
By the way, if you know any other pusher client, please suggest me. Thanks a lot.
Here is the code to for gen_server callback:
-module(pusherl_server).
-behaviour(gen_server).
-define(SERVER, ?MODULE).
-define(JP, fun(K,V) -> string:join([K,V],"=") end).
-record(state,{app_id, key, secret}).
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
init(_) ->
{ok, PusherAppId} = application:get_env(pusher_app_id),
{ok, PusherKey} = application:get_env(pusher_key),
{ok, PusherSecret} = application:get_env(pusher_secret),
{ok, #state{app_id=PusherAppId, key=PusherKey, secret=PusherSecret}}.
handle_call({push, {ChannelName, EventName, Payload}}, _From, State) ->
case http_request(ChannelName, EventName, Payload, State) of
{ok, _} -> {reply, ok, State};
{error, _} -> {reply, error, State}
end;
handle_call(_Request, _From, State) ->
{noreply, ok, State}.
handle_cast({push, {ChannelName, EventName, Payload}}, State) ->
case http_request(ChannelName, EventName, Payload, State) of
{ok, _} -> {noreply, ok, State};
{error, _} -> {noreply, error, State}
end;
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
http_request(ChannelName, EventName, Payload, Config) when is_list(ChannelName), is_record(Config, state) ->
{ok, ReqProps} = http_request_props(Payload, EventName, ChannelName, Config),
httpc:request(post, ReqProps, [], []).
http_request_props(Payload, EventName, ChannelName, #state{app_id=AppId, key=AppKey, secret=AppSecret}) ->
Md5String = lists:flatten([io_lib:format("~2.16.0b",[N]) || <<N>> <= crypto:md5(Payload)]),
ToSign = ["POST",
lists:flatten(["/apps/", AppId, "/channels/", ChannelName, "/events"]),
string:join([?JP("auth_key", AppKey),
?JP("auth_timestamp", get_time_as_string()),
?JP("auth_version", "1.0"),
?JP("body_md5", Md5String),
?JP("name", EventName)
],"&")
],
AuthSignature = signed_params(ToSign, AppSecret),
QueryParams = [
?JP("auth_key", AppKey),
?JP("auth_timestamp", get_time_as_string()),
?JP("auth_version","1.0"),
?JP("body_md5", Md5String),
?JP("auth_signature", AuthSignature),
?JP("name", EventName)
],
Url = http_api_url(AppId, ChannelName, QueryParams),
{ok, {Url, [], "application/x-www-form-urlencoded", Payload}}.
http_api_url(AppId, ChannelName, QueryParams) ->
QueryString = string:join(QueryParams,"&"),
lists:flatten(["http://api.pusherapp.com/apps/",AppId,"/channels/",ChannelName,"/events?", QueryString]).
get_time_as_string() ->
{M, S, _} = now(),
integer_to_list(((M * 1000000) + S)).
signed_params(Params, Secret) ->
lists:flatten([io_lib:format("~2.16.0b",[N]) || <<N:8>> <= sha2:hmac_sha256(Secret, string:join(Params,"\n"))]).
noproc means that the process you're trying to call is not running. You need to start it with:
pusherl_server:start_link().
Note that this will link the pusherl_server process to the calling process. If you're running this in the shell, and then do something that causes an error, the error will propagate across the link to the pusherl_server process and kill it, so then you have to start it again.
To avoid that, you can either unlink the process after starting it:
{ok, Pid} = pusherl_server:start_link().
unlink(Pid).
or add a start function to the module, that does the same as start_link except it calls gen_server:start instead of gen_server:start_link.
Please help me, (btw my English is not very good). I want to list all records that have been created for a gen_server, for example, if a had the following code:
-module(mod).
-record(person, {name, phone}).
start_link(Param1, Param2) ->
gen_server:start_link(?MODULE, [Param1, Param2], []).
init([Param1, Param2]) ->
{ok, #person{name=Param1,phone=Param2}}.
I can called:
mod:start_link(Maria,22222).
mod:start_link(Jose,9348).
mod:start_link(lol,232).
After that i want to see all the records that i have created and put it on a list. How can i do this?
You should read this doc: http://www.erlang.org/doc/man/gen_server.html#start_link-3.
The start_link/3 just start the gen_server, do store the record data.
-module(record_make).
-compile(export_all).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-behaviour(gen_server).
-define(SERVER, ?MODULE).
-record(state, {persons = []}).
-record(person, {name, phone}).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
init([]) ->
{ok, #state{}}.
handle_call({create, Name, Phone}, _Frome, State = #state{persons = Persons}) ->
NewPerson = #person{name = Name, phone = Phone},
{reply, ok, State#state{persons = [NewPerson | Persons]}};
handle_call(list, _From, State = #state{persons = Persons}) ->
io:format("~p~n", [Persons]),
{reply, ok, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
create(Name, Phone) ->
gen_server:call(?MODULE, {create, Name, Phone}).
list() ->
gen_server:call(?MODULE, list).
You can do like this:
1> c(record_make)
1> .
{ok,record_make}
2> record_make:start_link().
{ok,<0.41.0>}
3> record_make:create("yang", "123").
ok
4> record_make:create("zhang", "456").
ok
5> record_make:list().
[{person,"zhang","456"},{person,"yang","123"}]
ok
6>
No chance to get the information in this way: there is one record per gen_server and the information is lost as soon as the sever die. If you need this information you shold add a dedicated process to keep the information ({Pid,Record} or record number ...). The information can be updated during the init and the terminate function via function such as record_manager:create/1 and record_manager:delete/1. You can also store them in a shared ets.
I'm writing something to process Erlang source code. Pretty much the first line of the program is:
{ok, Forms} = epp_dodger:parse_file(Filename)
However, I want to do some simple unit testing. So: how do I persuade epp_dodger to take its input from a string instead of a file?
Alternatively, it has epp_dodger:parse_form/2,3, which takes an IODevice, so how do I provide an IODevice over a string?
The code below (which is admittedly a bit hackish) starts a gen_server that takes a string as an argument, and then fulfills the Erlang I/O Protocol enough to satisfy epp_dodger:parse/2 to be able to read and parse the string from that process.
Here's an example of using it:
1> {ok,P} = iostr:start("-module(x).\n-export([f/0]).\nf() -> ok.\n").
2> epp_dodger:parse(P,1).
{ok,[{tree,attribute,
{attr,1,[],none},
{attribute,
{tree,atom,{attr,1,[],none},module},
[{tree,atom,{attr,1,[],none},x}]}},
{tree,attribute,
{attr,2,[],none},
{attribute,
{tree,atom,{attr,2,[],none},export},
[{tree,list,
{attr,2,[],none},
{list,
[{tree,arity_qualifier,
{attr,2,[],none},
{arity_qualifier,
{tree,atom,{attr,...},f},
{tree,integer,{...},...}}}],
none}}]}},
{tree,function,
{attr,3,[],none},
{func,
{tree,atom,{attr,3,[],none},f},
[{tree,clause,
{attr,3,[],none},
{clause,[],none,[{atom,3,ok}]}}]}}]}
The process dies once the string is exhausted.
Note that the code probably misses a few things — handling unicode properly, for example — but it should give you a good idea of how to make a more robust I/O server for this purpose if needed.
-module(iostr).
-behaviour(gen_server).
-export([start_link/1, start/1, stop/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {
data,
line = 1,
lines
}).
start_link(Data) ->
gen_server:start_link(?MODULE, [Data], []).
start(Data) ->
gen_server:start(?MODULE, [Data], []).
stop(Pid) ->
gen_server:cast(Pid, stop).
init([Data0]) ->
Data = [Line++"\n" || Line <- string:tokens(Data0, "\n")],
{ok, #state{data=Data,lines=length(Data)}}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({io_request,From,ReplyAs,{get_until,_,_,_,_,_}},
#state{data=[],lines=L}=State) ->
From ! {io_reply, ReplyAs, {eof,L}},
{stop, normal, State};
handle_info({io_request,From,ReplyAs,{get_until,_,_,M,F,Args}},
#state{data=Data,line=L}=State) ->
case handler(Data,L,[],M,F,Args) of
eof ->
Lines = State#state.lines,
From ! {io_reply, ReplyAs, {eof,Lines}},
{stop, normal, State#state{data=[]}};
{ok,Result,Rest,NData,NL} ->
From ! {io_reply, ReplyAs, Result},
case Rest of
[] ->
{noreply, State#state{data=NData,line=NL}};
_ ->
{noreply, State#state{data=[Rest|NData],line=NL}}
end
end;
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handler([Input|Data],L,Cont,M,F,Extra) ->
case catch apply(M,F,[Cont,Input|Extra]) of
{done,eof,_} ->
eof;
{done,Result,Rest} ->
{ok,Result,Rest,Data,L+1};
{more,NCont} ->
case Data of
[] -> eof;
_ -> handler(Data,L+1,NCont,M,F,Extra)
end
end.
I got one error when starting my gen server, i want to know how to debug it, thanks!
I run "example:add_listener(self(), "127.0.0.1", 10999)." after start_link.
The error is :
=ERROR REPORT==== 11-May-2011::13:41:57 ===
** Generic server <0.37.0> terminating
** Last message in was {'EXIT',<0.35.0>,
{{timeout,
{gen_server,call,
[<0.35.0>,
{add_listener,"127.0.0.1",10999}]}},
[{gen_server,call,2},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
** When Server state == {state,example,
{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]}}},
{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]}}},
[]}
** Reason for termination ==
** {{timeout,{gen_server,call,[<0.35.0>,{add_listener,"127.0.0.1",10999}]}},
[{gen_server,call,2},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}
** exception exit: {timeout,{gen_server,call,
[<0.35.0>,{add_listener,"127.0.0.1",10999}]}}
in function gen_server:call/2
My code is :
-module(test_ess_tcp).
-export([start_link/0,
add_listener/3,
remove_listener/3]).
-export([init/2, handle_call/3, handle_cast/2, handle_info/2]).
-export([terminate/2, sock_opts/0, new_connection/4]).
-behavior(ess_tcp).
start_link() ->
ess_tcp:start_link(?MODULE, []).
add_listener(Pid, IpAddr, Port) ->
gen_server:call(Pid, {add_listener, IpAddr, Port}).
remove_listener(Pid, IpAddr, Port) ->
gen_server:call(Pid, {remove_listener, IpAddr, Port}).
init([], State) ->
%% Example storing callback module specific state
%% This modifies the server state
{ok, ess_tcp:store_cb_state([], State)}.
handle_call({add_listener, IpAddr, Port}, _From, State) ->
%% Example of getting callback module state
io:format("Not used here, but just an example"),
[] = ess_tcp:get_cb_state(State),
case ess_tcp:add_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
{reply, ok, State1};
Error ->
{reply, Error, State}
end;
handle_call({remove_listener, IpAddr, Port}, _From, State) ->
case ess_tcp:remove_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
{reply, ok, State1};
Error ->
{reply, Error, State}
end;
handle_call(_Msg, _From, State) ->
{reply, ignored, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({tcp, Sock, Data}, State) ->
Me = self(),
P = spawn(fun() -> worker(Me, Sock, Data) end),
gen_tcp:controlling_process(Sock, P),
{noreply, State};
handle_info(_Msg, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
sock_opts() ->
[binary, {active, once}, {packet, 0}].
new_connection(_IpAddr, _Port, Sock, State) ->
Me = self(),
P = spawn(fun() -> worker(Me, Sock) end),
gen_tcp:controlling_process(Sock, P),
{ok, State}.
worker(Owner, Sock) ->
gen_tcp:send(Sock, "Hello\n"),
inet:setopts(Sock, [{active, once}]),
gen_tcp:controlling_process(Sock, Owner).
worker(Owner, Sock, Data) ->
gen_tcp:send(Sock, Data),
inet:setopts(Sock, [{active, once}]),
gen_tcp:controlling_process(Sock, Owner).
Well, your gen_server:call is getting a timeout when called. That means that the gen_server is either taking longer than the default 3 second timeout for a call or it is blocked somewhere.
using tracing to debug kind of behaviour is ideal. For instance if you type this in the shell before running the test:
dbg:tracer(),dbg:p(all,c),dbg:tpl(ess_tcp, x).
you will trace on all functions within ess_tcp to see what is going on in there. For more info about dbg see http://www.erlang.org/doc/man/dbg.html
actually best tool to debug in erlang is io:format statements. Put io:formats where you have doubts and see if you get the expected values.
Regarding above question it is mainly stuck somewhere !!