I am writing an ejabberd module to filter packets. I need to get the hostname to pull some configs using gen_mod:get_module_opt().
I have 4 important functions :
start(Host, _Opt) : This is an ejabberd function to load my module. I get the Host atom here
filter_packet({From, To, XML}): This is my packet filter hook. I cannot pass custom params to this function, as it is a hook in ejabberd.
get_translation(XmlData): filter_packet() calls get_translation() in a loop
fetch_translation(XmlData): called recursively from get_translation(). This is where I am calling gen_mod:get_module_opt(), and hence need the Host.
My question is, how can I take Host from start() and put it in a global variable, so that fetch_translation can access it?
The "easiest way" is to create a named ets table, and put it in there.
start(Host, _Opt) ->
ets:new(my_table, [named_table, protected, set, {keypos, 1}]),
ets:insert(my_table, {host, Host}),
...
fetch_translation(XmlData) ->
[{_, Host}] = ets:lookup(my_table, host),
...
Note that this is a "general" solution. Ejabberd might provide facilities for what you want, but I cannot help you with that.
It may sound as an overkill but you may consider implementing a very basic gen_server. It contains a state that is available to its callbacks and the data can be kept there. For your case you can write a module similar to this one:
-module(your_module_name).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([start/2, filter_loop/1]).
start(Host, Opt) ->
%% start the named gen server
gen_server:start({local, ?MODULE}, ?MODULE, Host, []).
filter_packet({From, To, XML}) ->
%% do your thing
gen_server:call(?MODULE, {fetch_translation, XmlData}).
%% this will be called by gen_server:start - just pass the Host
init(Host) ->
{ok, Host}.
handle_call({fetch_translation, XmlData}, _From, Host) ->
%% do your thing
{reply, ok, Host}.
%% you can ignore the rest - they are needed to be present
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
You define your global variable on your module top...like below
-define (Your Variable, "your host name here").
eg.
-define (RelayHost, "smtp.gmail.com").
and you can use this Global variable in all your method in your module.
io:fwrite("Global Value ~p", [?RelayHost]).
-AjAy
You could start a new message filtering process and register it using erlang:register/2, then route all filter_packet/1 requests through it (a potential bottleneck).
-define(?SERVER, msg_filter).
start(Host, Opt) ->
{ok, Pid} = spawn(?MODULE, filter_loop, [Host, Opt]),
register(?SERVER, Pid).
filter_loop(Host, Opt) ->
receive
{Pid, filter_packet, {_From, _To, XML}} ->
Trans = get_translation(XML, Host),
Pid ! {?SERVER, translation, Trans},
filter_loop(Host, Opt)
end.
filter_packet(Pack) ->
?SERVER ! {self(), filter_packet, Pack}
receive
{?SERVER, translation, Trans} ->
% wrap translation
UpdatedPacket
end.
Say you are filtering incoming packets, then To#jid.lserver might be your host.
guessing for your description than you are in a single-domain ejabberd deployment (no virtual hosts),
yo can get the local XMPP domain using the ?MYNAME macro (see ejabberd.hrl for the definition).
Try use persistent_term:
1> persistent_term:put(hello, <<"world">>).
ok
2> persistent_term:get(hello).
<<"world">>
3> persistent_term:erase(hello).
true
4> persistent_term:get(hello).
** exception error: bad argument
in function persistent_term:get/1
called as persistent_term:get(hello)
You cannot create global variable but you can define a record outside your functions and create an instance of that record with properties then pass it down to the methods you call. Therefore, you can only share one record via method parameter.
Related
I have a gen_server in my cavv application that I need to start first to execute a call to. I want to use a command dispatcher for this. For a short example, this it the gen_server's API:
a gen_server: cavv_user
-module(cavv_user).
-behavior(gen_server).
-define(SERVER(UserId), {via, gproc, {n, l, {?MODULE, UserId}}}).
start_link(UserId) ->
gen_server:start_link(?SERVER(UserId), ?MODULE, [UserId], []).
change_email_address(UserId, EmailAddress) ->
gen_server:call(?SERVER(AggregateId), {execute_command, #change_user_email_address{user_id=UserId, email_address=EmailAddress}}).
Before I can call cavv_user:change_email_address(). I need to start the cavv_user. I do this is as a simple_one_for_one child in a supervisor, like so:
a supervisor: cavv_user_sup
-module(cavv_user_sup).
-behaviour(supervisor).
-define(CHILD(ChildName, Type, Args), {ChildName, {ChildName, start_link, Args}, temporary, 5000, Type, [ChildName]}).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_child(UserId) ->
supervisor:start_child(?SERVER, [UserId]).
init([]) ->
RestartStrategy = {simple_one_for_one, 1, 5},
Children = [?CHILD(cavv_user, worker, [])],
{ok, { RestartStrategy, Children} }.
The problem I am now facing is how to dispatch commands to a cavv_user. I want to make sure the proper user is started first using start_child, and then call the cavv_user:change_email_address().
I have found this anwser, to use a dispatcher: Erlang: what supervision tree should I end with writing a task scheduler?
So I created a command dispatcher and end up with a cavv_user_dispatcher and a cavv_user_dispatcher_sup that in turn contains the cavv_user_dispatcher and the earlier cavv_user_sup:
cavv_user_dispatch_sup
| |
cavv_user_dispatcher |
(gen_server) |
|
|
cavv_user_sup
| | |
cavv_user_1...cavv_user_N
The cavv_user_dispatcher
This works beautifully.
The problem I am facing now is, how do I properly write the code in cavv_user_dispatcher? I am facing a problem with code duplication. How to properly call start_child and call the appropriate API of cavv_user?
Should I use some kind of Fun like so?
-module(cavv_user_dispatcher).
dispatch_command(UserId, Fun) ->
gen_server:call(?SERVER, {dispatch_command, {UserId, Fun}}).
handle_call({dispatch_command, {UserId, Fun}}, _From, State) ->
cavv_user_sup:start_child(UserId),
Fun(), %% How to pass: cavv_user:change_email_address(..,..)?
{reply, ok, State};
Or duplicate the cavv_user's API like so?
-module(cavv_user_dispatcher).
change_user_email_address(UserId, EmailAddress) ->
gen_server:call(?SERVER, {change_user_email_address, {UserId, EmailAddress}}).
handle_call({change_user_email_address, {UserId, EmailAddress}}, _From, State) ->
cavv_user_sup:start_child(UserId),
cavv_user:change_email_address(UserId, EmailAddress),
{reply, ok, State};
Or should I re-use the command records from cavv_user into some kind of util to properly build them and pass them around? Maybe some better way to pass the function I want to call at cavv_user?
I would like to solve the problem in the best Erlang way as possible, without code duplication.
Is your dispatcher supposed to handle other commands?
If yes then then how will the next command will come, I mean will the requester know the process pid of the user or not?
if yes then you need 2 functions, one to create a user, it will return the pid to the requester for next call, and one to handle next requests by sending the command to the given pid
if no, then you need also 2 functions, one to create the a user and store the user_id along with the user process pid and one to handle next request by retrieving the process pid and then forward it the command (I suppose this is what you want to do).
if no then you don't need to handle any command and should pass directly the email address when creating the user process. Note that this is true for all cases since you need a different interface to create a user.
I would modify your code this way (not tested, it is too late :o) !)
-module(cavv_user_dispatcher).
create_user(UserId,UserMail) ->
gen_server:call(?SERVER,{new_user,UserId,UserMail}).
% Args is a list of argument, empty if
% F needs only one argument (the user Pid)
dispatch_command(UserId, Fun, Args) ->
gen_server:call(?SERVER, {dispatch_command, {UserId, Fun,Args}}).
handle_call({dispatch_command, {UserId, Fun,Args}}, _From, State) ->
Pid = get_pid(UserId,State),
Answer = case Pid of
unknown_user_id -> unknown_user_id;
_ -> apply(Fun,[Pid|Args]),
ok
end,
{reply, Answer, State};
handle_call({new_user,UserId,UserMail},_From,State) ->
% verify that the user id does not already exists
CheckId = check_id(UserId,State),
{Answer,NewState} = case CheckId of
false -> {already_exist,State};
true -> {ok,Pid} = cavv_user_sup:start_child(UserId,UserMail)
{ok,[{UserId,Pid}|State]}
% State must be initialized as an empty list in the init function.
{reply, Answer, NewState};
...
get_pid(UserId,State) ->
proplists:get_value(UserId, State, unknown_user_id).
check_id(UserId,State) ->
not proplists:is_defined(UserId, State).
and the user supervisor mus be modified this way:
start_child(UserId,UserMail) -> % change arity in the export
supervisor:start_child(?SERVER, [UserId,UserMail]).
and then the user server:
start_link(UserId,UserMail) ->
gen_server:start_link(?SERVER(UserId), ?MODULE, [UserId,UserMail],[]).
init([UserId,UserMail]) ->
{ok,[{user_id,UserId},{user_mail,UserMail}]}.
I'm trying to write and compile a custom behaviour in Erlang.
I cannot find any clear documentation on how to compile this behaviour.
-module(bla).
-export([start_link/0,behaviour_info/1]).
behaviour_info(callbacks)->
[{init,1}];
behaviour_info(_Other)->
undefined.
%% -callback init(Args :: term()) ->
%% {ok, State :: term()} | {ok, State :: term(), timeout()} |
%% {stop, Reason :: term()} | ignore.
start_link()->
init([]).
my command for comiling is :
erlc.exe .\src\bla.erl
resulting:
bla.erl:24: function init/1 undefined
Anyone an idea on writing and compiling behaviours in erlang, please? any links?
Defining behaviour callbacks results in an obligation towards your implementation of callback module. In erlang, modules are just function containers, not classes nor interfaces. Behaviours are based on runtime module name resolution Mod:fun(). OTP gen_server (check it) keeps its callback module name after you pass it in: gen_server:start_link(CallbackModuleName, Args, Opts) and here is code for applying callback init/1:
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
Name = name(Name0),
Debug = debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, infinity, Debug);
{ok, State, Timeout} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, Timeout, Debug);
...
It's applying init/1 of passed callback module kept in Mod parameter, gets its last value, do what you want and keep going (or not, depends on that last value).
Assume we have module bla_impl which looks like this:
-module(bla_impl).
-behaviour(bla).
-export([init/1, start_link/0]).
start_link() -> bla:start_link(?MODULE). %% macro ?MODULE is resolved to bla_impl
init(Args) -> ... .
And now you need to say in bla which module you use by:
-module(bla).
-export([start_link/1]).
start_link(Mod) -> Mod:init([]).
or maybe better solution is to read it from configuration:
-module(bla).
-export([start_link/0]).
start_link() ->
Mod = application:get_env(bla_app, callback_module),
Mod:init([]),
...
There is many ways for doing so.
As you see there is no magic here. This would work even without -behaviour(bla) nor specified callback with -callback. This is just an information for compiler, tools and documentation.
From erlang documentation: Behaviours
And btw. start_link function should spawn another process and link to it.
start_link(Mod) ->
spawn_link(Mod, init, [[]]).
I have written a gen_server module (data_cahe.erl) that will save the data in ETS.
My code is as follows:
-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).
-define(TABLE_ID, ?MODULE).
-record(state, {user_id, my_reading, my_status}).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
init([]) ->
{ok, ?TABLE_ID} = new_cache(?TABLE_ID),
{ok, #state{user_id=undefined, my_reading=undefined, my_status=undefined}}.
The handle_call:
handle_call({save, UserId, Readings}, _From, _Status) ->
io:format("Inside handle_call_save: ~n~p~n",[]);
%Check if email is present
case my_reading(UserId) of
{error, not_found} -> %%Email not present
io:format("Inside handle_call_save Just before save: ~n~p~n",[]),
Result = save_my_readings(UserId, Readings),
{reply, ok, #state{user_id=UserId, my_reading=Readings, my_status=Result}};
{ok, Reading} ->
io:format("Inside handle_call_save Just before delete and save: ~n~p~n",[]),
delete_my_reading(UserId), %%delete last reading
Result = save_my_readings(UserId, Readings), %%Save this new Reading
{reply, ok, #state{user_id=UserId, my_reading=Readings, my_status=Result}}
end;
I'm trying using this handel_call (that has access to Email and AccessToken) to save the data in ETS from the worker module:
case my_app_interface:get_data_summary(binary_to_list(AccessToken)) of
{error, _Reason1} ->
%%Start a new Timer Cycle
..
..
Readings1 ->
gen_server:call(data_cahe, {save, Email, Readings1}), %%HERE IT CRASHES
io:format("Get Data Summary : ~n~p~n",[Readings1]) %%This is printed when the line above is commented
end,
However the gen_server:call(...) crashes. When I comment out this line the Readings are printed in usual sequence.
I've even commeneted out all the lines except the print statement in handle_call method - but nothing is printed. It seems the gen_server:call(...) is not at all going through. Would be extremely grateful if someone throws some light what is going wrong.
maybe you spelled it wrong ? data_cahe instead of ddata_cache..
As general rule, you do not want to expose users of your server to gen_server API. Or to be more specific, you don't want them to call gen_server:call( ModuleName, {SomeAtom, And, DifferentArguments}) since it creates space for many errors (misspellings and missing "arguments" in message tuple). And it makes it hard to find out how you could interact with this server (one would have to look into handle_call's and handle_cast's, which is not easiest approach).
To go around this, all such interactions (all possible calls and casts) should be wrapped in function call. And those functions will be exposed (exported) as module interface. And in the end, the client won't have to even know that it is implemented with gen_server
So if your module is called data_cache, and you have functionality of saving some data, just implement save function.
save( Email, Readings) ->
gen_server:call(?SERVER, {save, Email, Readings1}).
We even used ?SERVER macro (to help a little with possible misspelings), and you can leave handle_call as it was. Now the client call could be changed to
Readings1 ->
data_cache:save(Email, Readings1),
io:format("Get Data Summary : ~n~p~n",[Readings1]) %%This is printed when the line above is commented
end,
which is much easier to read, and harder to break.
I have a gen_server which exports a function like this:
my_function(Param) ->
gen_server:cast(?SERVER, {forward, Param}).
and has an handle_cast like this:
handle_cast({forward, Param}, #state{peer=Socket} = State) ->
gen_tcp:send(Socket, Param),
{noreply, State}.
In most of cases the peer that is connected using gen_tcp will reply with one among different messages, and I handle the reply in the handle_info
handle_info({tcp, Socket, Data}) ->
io:format("Received : ~p~n", [Data]),
{noreply, State}.
Is there any recommended way to test this kind of scenario in erlang?
You can use setup/teardown functions in order to do something like that:
my_test_() -> {
setup,
fun() -> my_server:start_link() end,
fun(_) -> my_server:stop() end,
fun() ->
%% here you do all the message sending and such
end
}.
You also need to export a stop/0 function which will be responsible for call/cast the gen_server in order to stop it.
You can find out more infos on fixtures here
HTH,
Alin
I have gen_server:
start(UserName) ->
case gen_server:start({global, UserName}, player, [], []) of
{ok, _} ->
io:format("Player: " ++ UserName ++ " started");
{error, Error} ->
Error
end
...
Now i want to write function to stop this gen server. I have:
stop(UserName) ->
gen_server:cast(UserName, stop).
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
I run it:
start("shk").
Player: shk startedok
stop(shk).
ok
start("shk").
{already_started,<0.268.0>}
But:
stop(player).
ok
is work.
How can i run gen_server by name and stop by name?
Thank you.
First: You must always use the same name to address a process, "foo" and foo are different, so start by having a strict naming convention.
Second: When using globally registered processes, you also need to use {global, Name} for addressing processes.
In my opinion you should also convert the stop function to use gen_server:call, which will block and let you return a value from the gen_server. An example:
stop(Name) ->
gen_server:call({global, Name}, stop).
handle_call(stop, _From, State) ->
{stop, normal, shutdown_ok, State}
This would return shutdown_ok to the caller.
With this said, the global module is rather limited and alternatives like gproc provides much better distribution.
I don't have the docs infront of me, but my guess would be that you need to wrap the username in a global tuple within the gen_server cast.