How do I start yaws to run in embedded mode? - erlang

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.

Related

supervisor:start_child returns child,undefined

seem to be running in to an issue when calling add_location
-module(test_sup).
-version(1.0).
-behaviour(supervisor).
-export([start_link/0,add_location/3]).
-export([init/1]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local,?SERVER}, ?MODULE, no_args).
add_location(SupPid, Spaces, Occupied) ->
supervisor:start_child(SupPid, child(test_location, [Spaces, Occupied])).
init(no_args) ->
{ok, {{rest_for_one, 5, 2000}, []}}.
child(Module, Args) ->
{Module, {Module, start_link, Args},
permanent, brutal_kill, worker, [Module]}.
I call....
{_, SupPid} = test_sup:start_link().
{Success, LocRef} = test_sup:add_location(SupPid, 1, 0)
But add_location is always returning an error..
{child,undefined,test_location,
{test_location,start_link,[1,0]},
permanent,brutal_kill,worker,
[zc_pickup_location]}}
Any ideas why?
My Error - The method start_link in test_location was returning something the supervisor didnt understand.

Suppressing Erlang “unused function” warnings in header files

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.

Using Beanstalkd in eJabberd module

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.

Erl file not compiling due to syntax errors

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.

Why doesn't spawn/3 (MFA) work in Erlang?

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

Resources