I am trying to figure out what is wrong with this code, cause it is giving me errors preventing it from compiling properly into a beam file. I do not see what is wrong with the syntax. Is there an IDE which could help me out?
These are the errors:
parallels#parallels-Parallels-Virtual-Platform:/var/backend/ejabberd_modules# erlc -I /var/tmp/ejabberd/src/ mod_stanza_ack.erl
./mod_stanza_ack.erl:97: syntax error before: '.'
./mod_stanza_ack.erl:98: syntax error before: Body
./mod_stanza_ack.erl:16: function route/3 undefined
./mod_stanza_ack.erl:3: Warning: behaviour gen_mod undefined
./mod_stanza_ack.erl:111: Warning: function strip_bom/1 is unused
./mod_stanza_ack.erl:114: Warning: function send_presence/3 is unused
./mod_stanza_ack.erl:120: Warning: function echo/3 is unused
./mod_stanza_ack.erl:123: Warning: function send_message/4 is unused
This is the code:
-module(mod_stanza_ack).
-behavior(gen_server).
-behavior(gen_mod).
-export([start_link/2]).
-export([start/2,
stop/1,
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-export([route/3]).
-include("ejabberd.hrl").
-define(PROCNAME, ejabberd_mod_stanza_ack).
-define(BOTNAME, stanza_ack).
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc,
{?MODULE, start_link, [Host, Opts]},
temporary,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:call(Proc, stop),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
init([Host, Opts]) ->
?DEBUG("ECHO_BOT: Starting echo_bot", []),
% add a new virtual host / subdomain "echo".example.com
MyHost = gen_mod:get_opt_host(Host, Opts, "echo.#HOST#"),
ejabberd_router:register_route(MyHost, {apply, ?MODULE, route}),
{ok, Host}.
handle_call(stop, _From, Host) ->
{stop, normal, ok, Host}.
handle_cast(_Msg, Host) ->
{noreply, Host}.
handle_info(_Msg, Host) ->
{noreply, Host}.
terminate(_Reason, Host) ->
ejabberd_router:unregister_route(Host),
ok.
code_change(_OldVsn, Host, _Extra) ->
{ok, Host}.
% Checks a presence /subscription/ is a part of this.
% we may want to impliment blacklisting / some kind of
% protection here to prevent malicious users
%route(From, #jid{luser = ?BOTNAME} = To, {xmlelement, "presence", _, _} = Packet) ->
route(From, To, {xmlelement, "presence", _, _} = Packet) ->
case xml:get_tag_attr_s("type", Packet) of
"subscribe" ->
send_presence(To, From, "subscribe");
"subscribed" ->
send_presence(To, From, "subscribed"),
send_presence(To, From, "");
"unsubscribe" ->
send_presence(To, From, "unsubscribed"),
send_presence(To, From, "unsubscribe");
"unsubscribed" ->
send_presence(To, From, "unsubscribed");
"" ->
send_presence(To, From, "");
"unavailable" ->
ok;
"probe" ->
send_presence(To, From, "");
_Other ->
?INFO_MSG("Other kind of presence~n~p", [Packet])
end,
ok;
%route(From, #jid{luser = ?BOTNAME} = To, {xmlelement, "message", _, _} = Packet) ->
route(From, To, {xmlelement, "message", _, _} = Packet) ->
case xml:get_subtag_cdata(Packet, "body") of
"" ->
ok.
Body ->
case xml:get_tag_attr_s("type", Packet) of
"error" ->
?ERROR_MSG("Received error message~n~p -> ~p~n~p", [From, To, Packet]);
_ ->
echo(To, From, strip_bom(Body))
end
end,
ok.
%% HELPER FUNCTIONS
strip_bom([239,187,191|C]) -> C;
strip_bom(C) -> C.
send_presence(From, To, "") ->
ejabberd_router:route(From, To, {xmlelement, "presence", [], []});
send_presence(From, To, TypeStr) ->
ejabberd_router:route(From, To, {xmlelement, "presence", [{"type", TypeStr}], []}).
echo(From, To, Body) ->
send_message(From, To, "chat", Body).
send_message(From, To, TypeStr, BodyStr) ->
XmlBody = {xmlelement, "message",
[{"type", TypeStr},
{"from", jlib:jid_to_string(From)},
{"to", jlib:jid_to_string(To)}],
[{xmlelement, "body", [],
[{xmlcdata, BodyStr}]}]},
ejabberd_router:route(From, To, XmlBody).
./mod_stanza_ack.erl:97: syntax error before: '.' is saying the error is in line 97.
At line 97 change ok. to ok;. This will fix the issue.
Erlide plugin for eclipse is a good IDE to try out.
On line 97 in the function route/3 the first clause of the case is
"" ->
ok.
The . here ends the function which is illegal in the middle of a case, it should be a ;. This also means that the parser assumes that the variable Body on the next line starts a new function which is also illegal as a function name cannot be a variable.
Seeing the function route/3 has not been defined it cannot be exported which is the cause of the undefined function error on line 16.
Sometimes the compiler error messages are a bit cryptic but the line numbers usually help.
Related
Now Im playing with the gen_server
I have two modules - one is Gen Server mod, second - logic module
and would like to send the message to the PID through the gen_server:call
here is the snip of code:
lookup_by_date(FromTime, ToTime) ->
gen_server:call({global, ?MODULE}, {lookup_by_date,FromTime,ToTime}).
here is the handle_call func:
handle_call({lookup_by_date, FromTime, ToTime}, _From, _State) ->
FromSec = calendar:datetime_to_gregorian_seconds(FromTime),
ToSec = calendar:datetime_to_gregorian_seconds(ToTime),
Pid = spawn(fun()-> logic:handler() end),
{reply, Pid !{lookup_by_date, FromSec, ToSec}, _State};
aand the logic mod code:
lookup_by_date(FromTime, ToTime) -> lookup_by_date(FromTime, ToTime, ets:first(auth), []).
lookup_by_date(_FromTime, _ToTime, '$end_of_table', Acc) -> {reply, Acc, ok};
lookup_by_date(FromTime, ToTime, Key, Acc) ->
case ets:lookup(auth, Key) of
[{Login, Pass, TTL, Unix, Unix2}] ->
F = calendar:datetime_to_gregorian_seconds(Unix2),
T = calendar:datetime_to_gregorian_seconds(Unix2),
if
F >= FromTime, T =< ToTime -> NewAcc = [{Login, Pass, TTL, Unix, Unix2}|Acc],
N = ets:next(auth, Key),
lookup_by_date(FromTime, ToTime, N, NewAcc);
true -> N = ets:next(auth, Key),
lookup_by_date(FromTime, ToTime, N, Acc)
end
end.
handler() ->
receive
{lookup_by_date, FromTime, ToTime}->
lookup_by_date(FromTime, ToTime),
handler();
Other->
io:format("Error message for ~p~n" ,[Other]),
handler()
end.
but i am getting the error (actually not an error)
2> c(cache_server).
{ok,cache_server}
3> c(logic).
{ok,logic}
4> cache_server:start([{ttl, 15000}]).
{ok,<0.73.0>}
5> cache_server:insert(test, root, 15000).
{auth,test,root,15000,1484309726435,
{{2017,1,13},{14,15,11}}}
6> cache_server:lookup_by_date({{2017,1,13},{14,15,11}},{{2017,1,13},{14,15,11}}).
{lookup_by_date,63651536111,63651536111}
I am receiving data from - {reply, Pid !{lookup_by_date, FromSec, ToSec}, _State};
but dont receive data from the "logic:lookup_by_date" function
Is there anyway you show me the right direction because Im stuck a little bit.
Thx...
In your code, the reply to the gen_server call is:
Pid !{lookup_by_date, FromSec, ToSec}
In Erlang messages are asynchronous, they are just sent to the process, so this code doesn't wait for a response, and it simply returns, immediatly, the message you are sending. It is why you get the reply {lookup_by_date, FromSec, ToSec}.
In your case you don't have to spawn a process, but simply call the lookup_by_date function:
handle_call({lookup_by_date, FromTime, ToTime}, _From, _State) ->
FromSec = calendar:datetime_to_gregorian_seconds(FromTime),
ToSec = calendar:datetime_to_gregorian_seconds(ToTime),
{reply, logic:lookup_by_date(FromSec, ToSec), _State};
Note: Your gen_server doesn't use the result, its state is not modified by the request, so you could directly call the function lookup_by_date and include the time conversion in it.
I've spent several hours trying to troubleshoot this issue using the yaws documentation and web searches. The existing threads here haven't helped me.
I am new to erlang and I am trying to run yaws in embedded mode using the sample code provided on http://yaws.hyber.org/embed.yaws. I am missing something because I cannot get it to work. I have four files:
ybed.app
{application, ybed_app,
[
{description, "Yaws Embedded Application Test"},
{vsn, "0.1.0"},
{registered, []},
{applications, [kernel, stdlib, yaws]},
{mod, {ybed_app, []}},
{env, []}
]}.
ybed_app.erl
-module(ybed_app).
-behaviour(application).
%% Application callbacks
-export([start/2,
stop/1]).
start(_StartType, _StartArgs) ->
case ybed_sup:start_link() of
{ok, Pid} ->
{ok, Pid};
Other ->
{error, Other}
end.
stop(_State) ->
ok.
ybed_sup.erl
-module(ybed_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
YBed = {ybed, {ybed,start,[]},
permanent,2000,worker,[ybed]},
{ok,{{one_for_all,0,1}, [YBed]}}.
ybed.erl
-module(ybed).
-compile(export_all).
start() ->
{ok, spawn(?MODULE, run, [])}.
run() ->
Id = "embedded",
GconfList = [{id, Id}],
Docroot = "/tmp",
SconfList = [{port, 8000},
{servername, "foobar"},
{listen, {127,0,0,1}},
{docroot, Docroot}],
{ok, SCList, GC, ChildSpecs} =
yaws_api:embedded_start_conf(Docroot, SconfList, GconfList, Id),
[supervisor:start_child(ybed_sup, Ch) || Ch <- ChildSpecs],
yaws_api:setconf(GC, SCList),
{ok, self()}.
When I compile it (successfully) and try to start the application, I get a return value:
{error,{not_loaded,yaws}}
When I try to run the compiled ybed.erl, ybed:run(), I get:
** exception error: undefined function yaws_api:embedded_start_conf/4
in function ybed:run/0 (src/ybed.erl, line 16)
If I start yaws before I start the application, it still doesn't work.
I am not trying to build releases yet, just compile and test yaws in embedded mode. Can anyone tell me what I'm missing?
Thanks in advance
When you get error
** exception error: undefined function yaws_api:embedded_start_conf/4
in function ybed:run/0 (src/ybed.erl, line 16)
You clearly don't have yaws_api.beam in your code server search path. Start your erl with proper -pa argument or call code:add_patha/1 in your application initialization if you are not planning use embedded mode.
BTW there is a neat way how to start yaws under your own supervisor described in yaws documentation but without complete code so here we go in one module and with neat debugging resource and preparation for REST servicing.
-module(ybed_yaws).
-behaviour(supervisor).
-include_lib("yaws/include/yaws_api.hrl").
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%% Internal functions export
-export([init_yaws/1, out/1]).
%%%===================================================================
%%% Defaults
%%%===================================================================
default_global() ->
#{id => "yaws", logdir => "log"}.
default_server() ->
#{port => 9900,
listen => {0,0,0,0},
docroot => "www",
appmods => [{"/", ?MODULE}]}.
%%%===================================================================
%%% API functions
%%%===================================================================
start_link() ->
supervisor:start_link(?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
init([]) ->
{ok,
{{one_for_all, 0, 1},
[#{id => init_yaws,
start => {?MODULE, init_yaws, [self()]},
restart => transient}]
}}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
init_yaws(Sup) ->
{ok, proc_lib:spawn_link(fun() -> config(Sup) end)}.
ensure_dir(Dir) ->
{ok, App} = application:get_application(),
D = filename:join([code:priv_dir(App), Dir])
filelib:ensure_dir(filename:join([D, "foo"])),
D.
config(Supervisor) ->
#{id := Id} = GCMap = default_global(),
#{docroot := DR} = SCMap = default_server(),
Docroot = ensure_dir(DR),
{ok, SC, GC, ChildSpecs} =
yaws_api:embedded_start_conf(
Docroot,
maps:to_list(SCMap#{docroot => Docroot}),
maps:to_list(GCMap),
Id),
[supervisor:start_child(Supervisor, Ch) || Ch <- ChildSpecs],
yaws_api:setconf(GC, SC),
ok.
-compile({inline, [h/1, f/2]}).
h(A) when is_atom(A) -> h(atom_to_binary(A, latin1));
h(S) -> yaws_api:htmlize(S).
f(Fmt, Args) -> yaws_api:f(Fmt, Args).
box(Str) ->
{'div',[{class,"box"}],
{pre, [], h(Str)}}.
out(A) ->
PathString = case A#arg.pathinfo of
undefined -> "";
P -> P
end,
Path = string:tokens(PathString, "/"),
Method = A#arg.req#http_request.method,
out(A, Method, Path).
out(A, Method, Path) ->
{ehtml,
{html, [],
[{head},
{body, [],
[
{h5, [], "Paths:"},
{hr},
box(f("Method = ~p~n"
"Path = ~p~n"
"A#arg.querydata = ~p~n",
[Method,
Path,
A#arg.querydata])),
{h5, [], "Headers:"},
{hr},
{ol, [], yaws_api:reformat_header(
A#arg.headers,
fun(H, V)->
{li, [], [h(H), ": ", {code, [], h(V)}]}
end
)}
]}
]}
}.
Note the way how is yaws initialised in OTP compliant transient process but without gen_server.
Add {yaws, [{embedded, true}]} to your .config file to keep yaws application common services from starting. It will work even without it but it will not be fully embedded.
I have a header file with some functionality in it.
It's meant to be for expandibility ;)
In this header file I use -spec for my functions.
Because my header file is included in more than one erl file and has some functionality which are not used by all of the erl files, I get warnings of functions not being used (which are being used but in another erl file).
For example:
I get for this example a warning for module a that function headerExecute is not being used and for module c that function headerCount is not being used.
%% Shell %%
BPid = b:create(),
APid = a:create(BPid),
CPid = c:create(BPid).
-module(a).
-export([create/1, count/2, stop/1]).
-export([loop/1]).
-include("header.hrl").
create(Id) ->
spawn(?MODULE, loop, [Id]).
count(Pid, Counter) ->
Pid!{count, Ref = make_ref(), self(), Counter},
receive
{Ref, Result} -> Result
end.
stop(Pid) ->
Pid!{stop}.
loop(Id) ->
receive
{count, Ref, From, Counter}
Result = headerCount(Id, Counter),
From!{Ref, Result},
?MODULE:loop(Id);
{stop} ->
ok;
_ ->
?MODULE:loop(Id)
end.
-define(header, header).
-spec headerCount(pid(), integer()) -> integer().
-spec headerExecute(pid(), atom()) -> no_return().
headerCount(Pid, Counter) ->
Pid!{count, Ref = make_ref(), self(), Counter},
receive
{Ref, Result} -> Result
end.
headerExecute(Pid, Cmd) ->
Pid!{execute, Cmd}.
-module(b).
-export([create/0, stop/1]).
-export([loop/0]).
create() ->
spawn(?MODULE, loop, []).
stop(Pid) ->
Pid!{stop}.
loop() ->
receive
{count, Ref, From, Counter} ->
From!{Ref, Counter + 1},
?MODULE:loop();
{execute, Cmd} ->
%% Execute something with the command Cmd %%
?MODULE:loop();
{stop} ->
ok;
_ ->
?MODULE:loop()
end.
-module(c).
-export([create/1, execution/2, stop/1]).
-export([loop/1]).
-include(header).
create(Id) ->
spawn(?MODULE, loop, [Id]).
execution(Pid, Cmd) ->
Pid!{execution, Cmd}.
stop(Pid) ->
Pid!{stop}.
loop(Id) ->
receive
{execution, Cmd} ->
headerExecute(Id, Cmd),
?MODULE:loop(Id);
{stop} ->
ok;
_ ->
?MODULE:loop(Id)
end.
For scalability it's not an option to export these in the erl files and exporting these functions in a header file result in errors.
How can I discard or get rid off these warnings?
EDIT: I put this functionality in the headers so that new erl modules can easily be added to the code and adjust the existing erl modules without touching the other erl modules.
Thanks in advance.
Use nowarn_unused_function compiler parameter in your header file.
-compile({nowarn_unused_function, [headerCount/2, headerExecute/2]})
But it doesn't have anything with scalability. You are using this word wrong.
I'm using eJabberd as my XMPP server. I'm developing a module that will filter messages with type=queue, then will add them to work queue Beanstalkd.
Problem: Every time i get message with type="queue", i get connected to the beanstalkd.I believe this is incorrect.
Approach: I tried to put
{ok, Q} = beanstalk:connect(), in start(_Host, _Opt), but since put(Pid, Data) -> put(Pid, Data,[]) requires Pid, i have no idea how to pass Pid, from start to on_filter.
%% name of module must match file name
-module(mod_beanstalkd).
%% Every ejabberd module implements the gen_mod behavior
%% The gen_mod behavior requires two functions: start/2 and stop/1
-behaviour(gen_mod).
%% public methods for this module
-export([start/2, stop/1, on_filter_packet/1]).
%% included for writing to ejabberd log file
-include("ejabberd.hrl").
-include("jlib.hrl").
start(_Host, _Opt) ->
?INFO_MSG("starting mod_beanstalkd", []),
ejabberd_hooks:add(filter_packet, global, ?MODULE, on_filter_packet, 120).
stop(_Host) ->
?INFO_MSG("stopping mod_beanstalkd", []),
ejabberd_hooks:delete(filter_packet, global, ?MODULE, on_filter_packet, 120).
on_filter_packet({From, To, XML} = Packet) ->
?INFO_MSG("on_beanstalkd ~p~n", [Packet]),
Type = xml:get_tag_attr_s(<<"type">>, XML),
case Type =:= <<"service">> andalso DataTag =:= false of
true ->
?INFO_MSG("on_beanstalkd service = True", []),
{ok, Q} = beanstalk:connect(),
JSON = xml:get_tag_cdata(xml:get_subtag(XML, <<"body">>)),
{inserted, ID} = beanstalk:put(Q, JSON),
Return = Packet,
false ->
Return = Packet,
?INFO_MSG("on_beanstalkd ELSE Return ~p~n", [Return])
end,
Return.
Here is a simple server in Erlang. I am trying to spawn using spawn/3 but I have to resort to using spawn/1 in order to get it to work.
-module(server_by_name).
-export([start/0]).
start() ->
{ok, Listen} = gen_tcp:listen(1234, [binary, {packet, 0}, {reuseaddr, true}, {active, true}]),
io:format("Server running."),
% --> Why doesn't this work in place of the line below? --> spawn(server_by_name, connect, [Listen]).
spawn(fun() -> connect(Listen) end).
connect(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
spawn(fun() -> connect(Listen) end),
io:format("Connection accepted on ~p Pid ~p~n", [Socket, self()]),
receive
{tcp, Socket, Bin} ->
Request = parse_request(Bin),
io:format("File requested: ~p~n", [Request]),
LoadHtml = file:read_file(Request),
case LoadHtml of
{ok, Reply} ->
Header = "HTTP/1.1 200 OK \r\n Content-Type:HTML\r\n\r\n",
ConnectCheck = gen_tcp:send(Socket, Header ++ Reply),
io:format("Connection?? ~p~n", [ConnectCheck]);
{error, Reason} ->
io:format("Error in loading html file: ~n~p~n", [Reason]);
end;
{tcp_closed, Socket} ->
io:format("Server socket closed~n")
after 5000 ->
io:format("Client unresponsive~n")
end.
parse_request(Bin) when is_binary(Bin)->
parse_request(binary_to_list(Bin));
parse_request("GET /" ++ RestStr) ->
get_request([], RestStr);
parse_request([_H|T]) ->
parse_request(T).
get_request(Request, [32|_RestStr]) ->
lists:reverse(Request);
get_request(Request, [H|RestStr]) ->
get_request([H|Request], RestStr);
get_request(Request, []) ->
io:format("Unexpected result in server_by_name:get_request()"),
list:reverse(Request).
I have included all the code so you can copy/paste it and try it yourself if need be. You just need to have a file that can be served in the same directory and request it by name. Obviously, it has no security precautions, etc.
spawn/3 requires the function to be exported. Your solution is perfectly reasonable, but you could also export connect/1:
-export([connect/1]). %% do not use, exported for spawn