Permalink
Browse files

add soap server concurrency (Andreas Hellstrom)

Add soap_workers param in global conf record, defaulting to 3 workers.  Add
handling of soap_workers config param in yaws_config.  Add
gconf.soap_workers arg in add_yaws_soap_srv/2.  Modify start_link and init
in yaws_soap_srv to receive optional NumOfWorkers arg and start that number
of workers at startup in init_complete().  Send SOAP request/add_wsdl calls
to worker process.  Move some internal functions to new
yaws_soap_srv_worker module. Add yaws_soap_sup to supervise SOAP workers.
Augment documentation to reflect soap_workers config param, and also add
the enable_soap param to the docs since it was missing.
  • Loading branch information...
1 parent 8ed725e commit cdcd5c74f34cad98a18218f4aae6457105d9841f @vinoski vinoski committed May 6, 2013
Showing with 425 additions and 114 deletions.
  1. +12 −3 doc/yaws.tex
  2. +1 −0 include/yaws.hrl
  3. +14 −8 man/yaws.conf.5
  4. +4 −0 src/yaws.erl
  5. +11 −1 src/yaws_config.erl
  6. +81 −100 src/yaws_soap_srv.erl
  7. +213 −0 src/yaws_soap_srv_worker.erl
  8. +79 −0 src/yaws_soap_sup.erl
  9. +1 −1 src/yaws_stats.erl
  10. +9 −1 www/soap_intro.yaws
View
15 doc/yaws.tex
@@ -2313,15 +2313,24 @@ \section{Global Part}
Fail completely or not if \Yaws\ fails to bind a listen socket
Default is true.
+\item \verb+enable_soap = true | false+ ---
+ If true, a SOAP server will be started when \Yaws\ is
+ started. Default is false.
+
+\item \verb+soap_workers = integer+ ---
+ Number of SOAP worker processes to start. A value larger than 1
+ means that the SOAP server will handle concurrent requests.
+ Default value is 3.
+
\item \verb+soap_srv_mods = ListOfModuleSetting+ ---
- If \verb+enable_soap+ is true, a startup \Yaws\ will invoke
+ If \verb+enable_soap+ is true, at startup \Yaws\ will invoke
\verb+yaws_soap_srv:setup()+ to setup modules set
here. \verb+ModuleSetting+ is either a triad like\\
\verb+<Mod, HandlerFun, WsdlFile>+ or a tetrad like
\verb+<Mod, HandlerFun, WsdlFile, Prefix>+\\ which specifies the
prefix. A prefix will be used as argument of
- \verb+yaws_soap_lib:initModel()+ and then be used as a XML
- namespace prefix. Note, the WsdlFile here should be an
+ \verb+yaws_soap_lib:initModel()+ and will also be used as a XML
+ namespace prefix. Note, the \verb+WsdlFile+ here should be an
absolute-path file in local file systems.
For example, we can specify
View
1 include/yaws.hrl
@@ -91,6 +91,7 @@
id = "default", % string identifying this instance of yaws
enable_soap = false, % start yaws_soap_srv iff true
+ soap_workers = 3, % the number of soap workers to start
%% a list of
%% {{Mod, Func}, WsdlFile, Prefix} | {{Mod, Func}, WsdlFile}
View
22 man/yaws.conf.5
@@ -177,18 +177,24 @@ Fail completely or not if Yaws fails to bind a listen socket Default is
.TP
\fBenable_soap = true | false\fR
-If true, a soap server will be started at startup of Yaws. Default is
+If true, a SOAP server will be started at startup of Yaws. Default is
\fIfalse\fR.
.TP
+\fBsoap_workers = integer\fR
+Number of SOAP worker processes to start. A value larger than 1 means that
+the SOAP server will handle concurrent requests. Default value is \fI3\fR.
+
+.TP
\fBsoap_srv_mods = ListOfModuleSetting\fR
-If enable_soap is true, a startup Yaws will invoke \fIyaws_soap_srv:setup()\fR
-to setup modules set here. ModuleSetting is either a triad like \fI<Mod,
-HandlerFunc, WsdlFile>\fR or a quadruple form like \fI<Mod, HandlerFunc,
-WsdlFile, Prefix>\fR which specifies the \fIprefix\fR. A \fIprefix\fR will be
-used as argument of \fIyaws_soap_lib:initModel()\fR and then be used as a XML
-namespace prefix. Note, the \fIWsdlFile\fR here should be an absolute-path file
-in local file systems.
+If \fIenable_soap\fR is true, at startup Yaws will invoke
+\fIyaws_soap_srv:setup()\fR to setup modules set here. \fIModuleSetting\fR
+is either a triad like \fI<Mod, HandlerFunc, WsdlFile>\fR or a quadruple
+form like \fI<Mod, HandlerFunc, WsdlFile, Prefix>\fR which specifies the
+\fIprefix\fR. A \fIprefix\fR will be used as argument of
+\fIyaws_soap_lib:initModel()\fR and then be used as a XML namespace prefix.
+Note, the \fIWsdlFile\fR here should be an absolute-path file in local file
+systems.
For example, we can specify
View
4 src/yaws.erl
@@ -26,6 +26,7 @@
gconf_mnesia_dir/1, gconf_log_wrap_size/1, gconf_cache_refresh_secs/1,
gconf_include_dir/1, gconf_phpexe/1, gconf_yaws/1, gconf_id/1,
gconf_enable_soap/1, gconf_soap_srv_mods/1, gconf_ysession_mod/1,
+ gconf_soap_workers/1,
gconf_acceptor_pool_size/1, gconf_mime_types_info/1]).
-export([sconf_port/1, sconf_flags/1, sconf_redirect_map/1, sconf_rhost/1,
@@ -235,6 +236,7 @@ gconf_yaws (#gconf{yaws = X}) -> X.
gconf_id (#gconf{id = X}) -> X.
gconf_enable_soap (#gconf{enable_soap = X}) -> X.
gconf_soap_srv_mods (#gconf{soap_srv_mods = X}) -> X.
+gconf_soap_workers (#gconf{soap_workers = X}) -> X.
gconf_ysession_mod (#gconf{ysession_mod = X}) -> X.
gconf_acceptor_pool_size (#gconf{acceptor_pool_size = X}) -> X.
gconf_mime_types_info (#gconf{mime_types_info = X}) -> X.
@@ -508,6 +510,8 @@ setup_gconf(GL, GC) ->
enable_soap = lkup(enable_soap, GL, GC#gconf.enable_soap),
soap_srv_mods = lkup(soap_srv_mods, GL,
GC#gconf.soap_srv_mods),
+ soap_workers = lkup(soap_workers, GL,
+ GC#gconf.soap_workers),
ysession_mod = lkup(ysession_mod, GL,
GC#gconf.ysession_mod),
acceptor_pool_size = lkup(acceptor_pool_size, GL,
View
12 src/yaws_config.erl
@@ -90,7 +90,8 @@ add_yaws_soap_srv(GC) when GC#gconf.enable_soap == true ->
add_yaws_soap_srv(_GC) ->
[].
add_yaws_soap_srv(GC, false) when GC#gconf.enable_soap == true ->
- [{yaws_soap_srv, {yaws_soap_srv, start_link, [GC#gconf.soap_srv_mods]},
+ [{yaws_soap_srv, {yaws_soap_srv, start_link,
+ [GC#gconf.soap_srv_mods, GC#gconf.soap_workers]},
permanent, 5000, worker, [yaws_soap_srv]}];
add_yaws_soap_srv(GC, true) when GC#gconf.enable_soap == true ->
Spec = add_yaws_soap_srv(GC, false),
@@ -795,6 +796,15 @@ fload(FD, globals, GC, C, Cs, Lno, Chars) ->
{error, ?F("~s at line ~w", [Str, Lno])}
end;
+ ["soap_workers", '=', Int] ->
+ case catch list_to_integer(Int) of
+ I when is_integer(I) ->
+ fload(FD, globals, GC#gconf{soap_workers = I},
+ C, Cs, Lno+1, Next);
+ _ ->
+ {error, ?F("Expect integer at line ~w", [Lno])}
+ end;
+
["max_connections", '=', Int] ->
case (catch list_to_integer(Int)) of
I when is_integer(I) ->
View
181 src/yaws_soap_srv.erl
@@ -8,8 +8,9 @@
-behaviour(gen_server).
%% API
--export([start_link/0, start_link/1,
+-export([start_link/0, start_link/1, start_link/2,
setup/1, setup/2, setup/3,
+ worker/1,
handler/4
]).
@@ -25,14 +26,20 @@
%% State
-record(s, {
- wsdl_list = [] % list of {Id, WsdlModel} tuples, where Id == {M,F}
+ num_of_workers = 0,
+ workers = [], % list of Pids
+ busy_workers = [], % list of {Pids, From} pairs.
+ queue = [], % list of waiting jobs
+ wsdl_list = [] % list of {Id, WsdlModel} tuples, where Id == {M,F}
}).
-define(OK_CODE, 200).
-define(BAD_MESSAGE_CODE, 400).
%% -define(METHOD_NOT_ALLOWED_CODE, 405).
-define(SERVER_ERROR_CODE, 500).
+-define(DEFAULT_NUM_OF_WORKERS, 3).
+
%%====================================================================
%% API
%%====================================================================
@@ -42,7 +49,13 @@
%%--------------------------------------------------------------------
start_link() ->
start_link([]).
+%%
+start_link(N) when is_integer(N) ->
+ start_link([], N);
start_link(L) ->
+ start_link(L, ?DEFAULT_NUM_OF_WORKERS).
+%%
+start_link(L, N) ->
%% We are dependent on erlsom
case code:ensure_loaded(erlsom) of
{error, _} ->
@@ -51,7 +64,7 @@ start_link(L) ->
[?MODULE, Emsg]),
{error, Emsg};
{module, erlsom} ->
- gen_server:start_link({local, ?SERVER}, ?MODULE, L, [])
+ gen_server:start_link({local, ?SERVER}, ?MODULE, {L, N}, [])
end.
%%% To be called from yaws_rpc.erl
@@ -89,7 +102,9 @@ setup(Id, WsdlFile, PrefixOrOptions) when is_tuple(Id),size(Id)==2 ->
Wsdl = yaws_soap_lib:initModel(WsdlFile, PrefixOrOptions),
gen_server:call(?SERVER, {add_wsdl, Id, Wsdl}, infinity).
-
+%% Send message to worker
+worker(X) ->
+ gen_server:cast(?MODULE, {worker, X, self()}).
%%====================================================================
%% gen_server callbacks
@@ -102,11 +117,12 @@ setup(Id, WsdlFile, PrefixOrOptions) when is_tuple(Id),size(Id)==2 ->
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
-init(L) -> %% [ {{Mod,Handler}, WsdlFile} ]
- WsdlList = lists:foldl( fun( SoapSrvMod, OldList) ->
- setup_on_init( SoapSrvMod, OldList )
- end,[],L),
- {ok, #s{wsdl_list = WsdlList}}.
+init({L, N}) -> % { [ {{Mod,Handler}, WsdlFile} ] , NumOfWorkers }
+ WsdlList = lists:foldl(fun(SoapSrvMod, OldList) ->
+ setup_on_init( SoapSrvMod, OldList )
+ end,[],L),
+ gen_server:cast(?MODULE, complete_init),
+ {ok, #s{wsdl_list = WsdlList, num_of_workers = N}}.
setup_on_init( {Id, WsdlFile}, OldList ) when is_tuple(Id),size(Id) == 2 ->
Wsdl = yaws_soap_lib:initModel(WsdlFile),
@@ -126,19 +142,46 @@ setup_on_init( {Id, WsdlFile, Prefix}, OldList ) when is_tuple(Id),
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({add_wsdl, Id, WsdlModel}, _From, State) ->
+ yaws_soap_sup:setup({Id, WsdlModel}),
NewWsdlList = uinsert({Id, WsdlModel}, State#s.wsdl_list),
{reply, ok, State#s{wsdl_list = NewWsdlList}};
%%
-handle_call( {request, Id, Payload, SessionValue, SoapAction}, _From, State) ->
- Reply = request(State, Id, Payload, SessionValue, SoapAction),
- {reply, Reply, State}.
+handle_call(Req = {request, _Id, _Payload, _SessionValue, _SoapAction}, From, State) ->
+ {noreply, call_worker({int_request, Req, From}, State)};
+handle_call(_, _, State) ->
+ {noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
+handle_cast(complete_init, State) ->
+ {ok, _} = supervisor:start_child(yaws_sup, yaws_soap_sup:child_spec()),
+ yaws_soap_sup:start_children(State#s.num_of_workers),
+ {noreply, State};
+%%
+handle_cast({worker, started, Pid}, State = #s{busy_workers = Busy}) ->
+ erlang:monitor(process, Pid),
+ case State#s.wsdl_list of
+ [] -> ok;
+ Wsdls -> yaws_soap_srv_worker:setup(Pid, init, Wsdls)
+ end,
+ {noreply, State#s{busy_workers = [{Pid, dummy} | Busy]}};
+%%
+handle_cast({worker, done, Pid}, State) ->
+ #s{workers = Workers,
+ busy_workers = Busy,
+ queue = Queue} = State,
+ State1 = State#s{workers = [Pid | Workers],
+ busy_workers = lists:keydelete(Pid, 1, Busy)},
+ State2 = case Queue of
+ [] -> State1;
+ [Q | Qs] -> call_worker(Q, State1#s{queue = Qs})
+ end,
+ {noreply, State2};
+%%
handle_cast(_Msg, State) ->
{noreply, State}.
@@ -148,6 +191,18 @@ handle_cast(_Msg, State) ->
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
+handle_info({'DOWN', _, process, Pid, Info}, State) ->
+ #s{workers = Workers,
+ busy_workers = Busy} = State,
+ case lists:keysearch(Pid, 1, Busy) of
+ {value, {Pid, From}} ->
+ gen_server:reply(
+ From, srv_error(f("Process termination: ~p", [Info])));
+ _ -> ok
+ end,
+ {noreply, State#s{workers = lists:delete(Pid, Workers),
+ busy_workers = lists:keydelete(Pid, 1, Busy)}};
+%%
handle_info(_Info, State) ->
{noreply, State}.
@@ -172,102 +227,28 @@ code_change(_OldVsn, State, _Extra) ->
%%% Internal functions
%%--------------------------------------------------------------------
-request(State, {M,F} = Id, {Req, Attachments}, SessionValue, Action) ->
- {ok, Model} = get_model(State, Id),
- %%error_logger:info_report([?MODULE, {payload, Req}]),
- case catch yaws_soap_lib:parseMessage(Req, Model) of
- {ok, Header, Body} ->
- %% call function
- result(Model, catch apply(M, F, [Header, Body,
- Action, SessionValue,
- Attachments]));
- {error, Error} ->
- cli_error(Error);
- OtherError ->
- srv_error(io_lib:format("Error parsing message: ~p", [OtherError]))
- end;
-request(State, {M,F} = Id, Req, SessionValue, Action) ->
- %%error_logger:info_report([?MODULE, {payload, Req}]),
- {ok, Model} = get_model(State, Id),
- Umsg = (catch erlsom_lib:toUnicode(Req)),
- case catch yaws_soap_lib:parseMessage(Umsg, Model) of
- {ok, Header, Body} ->
- %% call function
- result(Model, catch apply(M, F, [Header, Body,
- Action, SessionValue]));
- {error, Error} ->
- cli_error(Error);
- OtherError ->
- srv_error(io_lib:format("Error parsing message: ~p", [OtherError]))
- end.
-
-%%% Analyse the result and produce some output
-result(Model, {ok, ResHeader, ResBody, ResCode, SessVal}) ->
- return(Model, ResHeader, ResBody, ResCode, SessVal, undefined);
-result(Model, {ok, ResHeader, ResBody}) ->
- return(Model, ResHeader, ResBody, ?OK_CODE, undefined, undefined);
-result(Model, {ok, ResHeader, ResBody, Files}) ->
- return(Model, ResHeader, ResBody, ?OK_CODE, undefined, Files);
-result(_Model, {error, client, ClientMssg}) ->
- cli_error(ClientMssg);
-result(_Model, false) -> % soap notify !
- false;
-result(_Model, Error) ->
- srv_error(io_lib:format("Error processing message: ~p", [Error])).
-
-return(#wsdl{model = Model}, ResHeader, ResBody, ResCode, SessVal, Files) ->
- return(Model, ResHeader, ResBody, ResCode, SessVal, Files);
-return(Model, ResHeader, ResBody, ResCode, SessVal, Files)
- when not is_list(ResBody) ->
- return(Model, ResHeader, [ResBody], ResCode, SessVal, Files);
-return(Model, ResHeader, ResBody, ResCode, SessVal, Files) ->
- %% add envelope
- Header2 = case ResHeader of
- undefined -> undefined;
- _ -> #'soap:Header'{choice = ResHeader}
- end,
- Envelope = #'soap:Envelope'{'Body' = #'soap:Body'{choice = ResBody},
- 'Header' = Header2},
- case catch erlsom:write(Envelope, Model) of
- {ok, XmlDoc} ->
- case Files of
- undefined ->
- {ok, XmlDoc, ResCode, SessVal};
- _ ->
- DIME = yaws_dime:encode(XmlDoc, Files),
- {ok, DIME, ResCode, SessVal}
- end;
- {error, WriteError} ->
- srv_error(f("Error writing XML: ~p", [WriteError]));
- OtherWriteError ->
- error_logger:error_msg("~p(~p): OtherWriteError=~p~n",
- [?MODULE, ?LINE, OtherWriteError]),
- srv_error(f("Error writing XML: ~p", [OtherWriteError]))
- end.
-
f(S,A) -> lists:flatten(io_lib:format(S,A)).
-cli_error(Error) ->
- error_logger:error_msg("~p(~p): Cli Error: ~p~n",
- [?MODULE, ?LINE, Error]),
- Fault = yaws_soap_lib:makeFault("Client", "Client error"),
- {error, Fault, ?BAD_MESSAGE_CODE}.
-
srv_error(Error) ->
error_logger:error_msg("~p(~p): Srv Error: ~p~n",
[?MODULE, ?LINE, Error]),
Fault = yaws_soap_lib:makeFault("Server", "Server error"),
{error, Fault, ?SERVER_ERROR_CODE}.
-
-
-
-get_model(State, Id) ->
- case lists:keysearch(Id, 1, State#s.wsdl_list) of
- {value, {_, Model}} -> {ok, Model};
- _ -> {error, "model not found"}
- end.
-
uinsert({K,_} = E, [{K,_}|T]) -> [E|T];
uinsert(E, [H|T]) -> [H|uinsert(E,T)];
uinsert(E, []) -> [E].
+
+call_worker(Req = {int_request, _, From},
+ State = #s{workers = [Worker | Workers],
+ busy_workers = Busy})->
+ case catch yaws_soap_srv_worker:call(Worker, Req) of
+ ok ->
+ State#s{workers = Workers,
+ busy_workers = [{Worker, From} | Busy]};
+ {'EXIT',{noproc,_}} ->
+ call_worker(Req, State#s{workers = Workers})
+ end;
+%%
+call_worker(Req, State = #s{queue = Queue}) ->
+ State#s{queue = [Req | Queue]}.
View
213 src/yaws_soap_srv_worker.erl
@@ -0,0 +1,213 @@
+%%%-------------------------------------------------------------------
+%%% File : yaws_soap_srv_worker.erl
+%%% Author : Jan Nyström <JanHenryNystrom@gmail.com>
+%%% Andreas Hellström <andreas.hellstrom@teliasonera.com>
+%%% Desc : Handling of soap workers.
+%%% Created : 4 Mar 2008 by Jan Nyström
+%%%-------------------------------------------------------------------
+-module(yaws_soap_srv_worker).
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0, call/2, setup/3]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-include("../include/yaws_api.hrl").
+-include("../include/yaws.hrl").
+-include("../include/soap.hrl").
+
+-define(SERVER, ?MODULE).
+
+-record(state, {wsdl_list = []}).
+
+-define(OK_CODE, 200).
+-define(BAD_MESSAGE_CODE, 400).
+-define(SERVER_ERROR_CODE, 500).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start_link() -> gen_server:start_link(?MODULE, no_args, []).
+
+setup(Server, Type, Wsdls) -> gen_server:cast(Server, {wsdl, Type, Wsdls}).
+
+call(Server, Request) -> gen_server:call(Server, Request).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init(no_args) ->
+ yaws_soap_srv:worker(started),
+ yaws_soap_srv:worker(done),
+ {ok, #state{}}.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call({int_request,
+ {request, Id, Payload, SessionValue, SoapAction}, OrigFrom}, From, State) ->
+ gen_server:reply(From, ok),
+ Reply = request(Id, Payload, SessionValue, SoapAction, State),
+ gen_server:reply(OrigFrom, Reply),
+ yaws_soap_srv:worker(done),
+ {noreply, State};
+handle_call(_Request, _, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast({wsdl, init, Wsdls}, State) ->
+ {noreply, State#state{wsdl_list = Wsdls}};
+handle_cast({wsdl, add, W = {Id, _}}, State = #state{wsdl_list = List}) ->
+ {noreply, State#state{wsdl_list = [W | lists:keydelete(Id, 1, List)]}};
+handle_cast(_, State) ->
+ {noreply, State}.
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+request({M,F} = Id, {Req, Attachments}, SessionValue, Action, State) ->
+ Model = (catch get_model(State, Id)),
+ %%error_logger:info_report([?MODULE, {payload, Req}]),
+ case catch yaws_soap_lib:parseMessage(Req, Model) of
+ {ok, Header, Body} ->
+ %% call function
+ result(Model, catch apply(M, F, [Header, Body,
+ Action, SessionValue,
+ Attachments]));
+ {error, Error} ->
+ cli_error(Error);
+ OtherError ->
+ srv_error(io_lib:format("Error parsing message: ~p", [OtherError]))
+ end;
+request({M,F} = Id, Req, SessionValue, Action, State) ->
+ %%error_logger:info_report([?MODULE, {payload, Req}]),
+ Model = (catch get_model(State, Id)),
+ Umsg = (catch erlsom_lib:toUnicode(Req)),
+ case catch yaws_soap_lib:parseMessage(Umsg, Model) of
+ {ok, Header, Body} ->
+ %% call function
+ result(Model, catch apply(M, F, [Header, Body,
+ Action, SessionValue]));
+ {error, Error} ->
+ cli_error(Error);
+ OtherError ->
+ srv_error(io_lib:format("Error parsing message: ~p", [OtherError]))
+ end.
+
+%%% Analyse the result and produce some output
+result(Model, {ok, ResHeader, ResBody, ResCode, SessVal}) ->
+ return(Model, ResHeader, ResBody, ResCode, SessVal, undefined);
+result(Model, {ok, ResHeader, ResBody}) ->
+ return(Model, ResHeader, ResBody, ?OK_CODE, undefined, undefined);
+result(Model, {ok, ResHeader, ResBody, Files}) ->
+ return(Model, ResHeader, ResBody, ?OK_CODE, undefined, Files);
+result(_Model, {error, client, ClientMssg}) ->
+ cli_error(ClientMssg);
+result(_Model, false) -> % soap notify !
+ false;
+result(_Model, Error) ->
+ srv_error(io_lib:format("Error processing message: ~p", [Error])).
+
+return(#wsdl{model = Model}, ResHeader, ResBody, ResCode, SessVal, Files) ->
+ return(Model, ResHeader, ResBody, ResCode, SessVal, Files);
+return(Model, ResHeader, ResBody, ResCode, SessVal, Files)
+ when not is_list(ResBody) ->
+ return(Model, ResHeader, [ResBody], ResCode, SessVal, Files);
+return(Model, ResHeader, ResBody, ResCode, SessVal, Files) ->
+ %% add envelope
+ Header2 = case ResHeader of
+ undefined -> undefined;
+ _ -> #'soap:Header'{choice = ResHeader}
+ end,
+ Envelope = #'soap:Envelope'{'Body' = #'soap:Body'{choice = ResBody},
+ 'Header' = Header2},
+ case catch erlsom:write(Envelope, Model) of
+ {ok, XmlDoc} ->
+ case Files of
+ undefined ->
+ {ok, XmlDoc, ResCode, SessVal};
+ _ ->
+ DIME = yaws_dime:encode(XmlDoc, Files),
+ {ok, DIME, ResCode, SessVal}
+ end;
+ {error, WriteError} ->
+ srv_error(f("Error writing XML: ~p", [WriteError]));
+ OtherWriteError ->
+ error_logger:error_msg("~p(~p): OtherWriteError=~p~n",
+ [?MODULE, ?LINE, OtherWriteError]),
+ srv_error(f("Error writing XML: ~p", [OtherWriteError]))
+ end.
+
+f(S,A) -> lists:flatten(io_lib:format(S,A)).
+
+cli_error(Error) ->
+ error_logger:error_msg("~p(~p): Cli Error: ~p~n",
+ [?MODULE, ?LINE, Error]),
+ Fault = yaws_soap_lib:makeFault("Client", "Client error"),
+ {error, Fault, ?BAD_MESSAGE_CODE}.
+
+srv_error(Error) ->
+ error_logger:error_msg("~p(~p): Srv Error: ~p~n",
+ [?MODULE, ?LINE, Error]),
+ Fault = yaws_soap_lib:makeFault("Server", "Server error"),
+ {error, Fault, ?SERVER_ERROR_CODE}.
+
+get_model(State, Id) ->
+ {value, {_, Model}} = lists:keysearch(Id, 1, State#state.wsdl_list),
+ Model.
+
+%% utf8_encode(UnicodeString) ->
+%% binary_to_list(unicode:characters_to_binary(UnicodeString)).
View
79 src/yaws_soap_sup.erl
@@ -0,0 +1,79 @@
+%%%-------------------------------------------------------------------
+%%% File : yaws_soap_sup.erl
+%%% Author : Jan Nyström <JanHenryNystrom@gmail.com>
+%%% Andreas Hellström <andreas.hellstrom@teliasonera.com>
+%%% Desc : Supervisor for soap workers.
+%%% Created : 4 Mar 2008 by Jan Nyström
+%%%-------------------------------------------------------------------
+-module(yaws_soap_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0, child_spec/0, start_children/1, setup/1]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the supervisor
+%%--------------------------------------------------------------------
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+setup(WsdlModel) ->
+ lists:foreach(fun(Child) ->
+ yaws_soap_srv_worker:setup(Child, add, WsdlModel)
+ end,
+ children()).
+
+start_children(Number) -> start_children(0, Number).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
+%% ignore |
+%% {error, Reason}
+%% Description: Whenever a supervisor is started using
+%% supervisor:start_link/[2,3], this function is called by the new process
+%% to find out about restart strategy, maximum restart frequency and child
+%% specifications.
+%%--------------------------------------------------------------------
+init([]) ->
+ {ok,{{simple_one_for_one, 1000, 3600},
+ [child(worker, yaws_soap_srv_worker)]}}.
+
+child_spec() -> child(supervisor, ?MODULE).
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: child(Type, Module) -> ChildSpec
+%% Description: Creates a child specification
+%%--------------------------------------------------------------------
+child(worker, Module) ->
+ {Module, {Module, start_link, []}, permanent, 500, worker, [Module]};
+child(supervisor, Module) ->
+ {Module, {Module, start_link, []},
+ permanent, infinity, supervisor, [Module]}.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% Description:
+%%--------------------------------------------------------------------
+children() ->
+ lists:map(fun({_, Child, _, _}) -> Child end,
+ supervisor:which_children(?MODULE)).
+
+start_children(N, N) -> ok;
+start_children(N, Limit) ->
+ supervisor:start_child(?MODULE, []),
+ start_children(N + 1, Limit).
View
2 src/yaws_stats.erl
@@ -9,7 +9,7 @@
-behaviour(gen_server).
--include("yaws.hrl").
+-include("../include/yaws.hrl").
%% API
-export([start_link/0, stop/1]).
View
10 www/soap_intro.yaws
@@ -398,6 +398,15 @@ out(A) ->
"There you have it! "
},
+ {h2, [], "Handling concurrent SOAP requests"},
+
+ {p,[],
+ "The Yaws SOAP server will start N worker processes to handle SOAP requests. Historically, the value
+ of N was 1, meaning that older versions of Yaws handled SOAP requests serially. In newer versions of Yaws,
+ starting with 1.97, the configuration file parameter <code>soap_workers</code> defines how many
+ worker processes are started. The default is 3. A value larger than 1 means that the SOAP server
+ will handle concurrent requests."},
+
{ssi, "END2",[],[]}
]}}].
@@ -406,4 +415,3 @@ out(A) ->
</erl>
-

0 comments on commit cdcd5c7

Please sign in to comment.