Thanks so much for replying with so much detail. I hear what you are saying about documentation.

I'm not sure I understand your dislike of the proplist-style interface. Ok, it's maybe more cumbersome than setting record variables directly, but It's used extensively in OTP and is almost a standard idiom, and it's much cleaner than having to fiddle with things like setting the Yaws gconf flags. Honestly, which of these code snippets is more readable, and easier to do in a run-time (embedded) configuration?

#1: (Imaginary API)
SCs = <setup sconf here>,
GCProps =
[{yaws_auth_log, false}, {yaws_copy_errlog, false}],
GC = yaws_config:make_default_gconf(false, "my_yaws", GCProps),
yaws_api:setconf(GC, [SCs]).

SCs = <setup sconf here>,
GC = yaws_config:make_default_gconf(false, "my_yaws"),
GC1 = GC#gconf{flags = GC#gconf.flags band (bnot (?GC_COPY_ERRLOG bor ?GC_AUTH_LOG)},
yaws_api:setconf(GC1, [SCs]).

SCs = <setup sconf here>,
GC = yaws_config:make_default_gconf(false, "my_yaws"),
GC1 = ?gc_set_auth_log(GC, false),
GC2 = ?gc_set_copy_errlog(GC1, false),
yaws_api:setconf(GC2, [SCs]).

I don't know, maybe it's just my relative lack of Erlang experience talking here.

I didn't want to start Yaws as a separate application when it's really embedded inside my application. It also just didn't feel right, somehow, to have a Web server running on the default port and a default document root until it is reconfigured correctly, even if it is just for a short while.

The way I eventually embedded Yaws was to create a one-for-all supervisor between my top-level supervisor and the Yaws supervisor. This has two child processes; one is obviously the Yaws supervisor, and the other is a gen_server whose only purpose is to receive the Yaws-specific application parameters from its supervisor, and call yaws_api:setconf() with them. It probably seems like an overkill to use a gen_server for this, but my understanding of the OTP supervisor documentation is that a supervisor may only have as child processes other supervisors or OTP-compliant processes, and a gen_server was for me the easiest and least error-prone choice.

The idea behind this organization is that if the Yaws supervision tree dies for any reason, it will be restarted and reconfigured correctly every time. Out of interest, why not have optional parameters to the start() function in the Yaws supervisor that allows one to pass the same arguments as yaws_api:setconf? This would start Yaws in embedded mode, and I think solves problems such as having to set Yaws application environment variables and do a two-phase startup. What do you think of that idea? I'll take a look at it in the meantime and see if I can get it to work.

If there are better ways to do what I have described - other than application:start(yaws) + yaws_api:setconf() -  and there surely must be, I would certainly appreciate learning about them.


On Fri, Aug 8, 2008 at 6:04 PM, Claes Wikstrom <klacke@tail-f.com> wrote:
Edwin Fine wrote:
Something I just thought about. I am uncomfortable with having to know internal details about Yaws in order to use it in embedded mode. For example, knowing the yaws_sup is the top-level supervisor is an internal detail. If the architecture changes later, it will break my application. That's why I was trying to use only yaws:start_embedded. I also don't like having to use the #gconf and #sconf records directly, and having to set  a different application's environment variable. In fact, there's a big, fat warning in the documentation precisely about this:


Use this function only if you know what you are doing, that is, /on your own applications/. It is very application and configuration parameter dependent when and how often the value is read by the application, and careless use of this function may put the application in a weird, inconsistent, and malfunctioning state."

The yaws:start_embedded function tries to hide the internal details by accepting property lists instead of sconf and gconf records, which I think is the right way to do things.

Disagree, the start_embedded/x code was added by tobbe. It's not complete
and I don't like it. Especially it's not documented. (hint to contributors...
document your stuff - and not in the code - in the docs)

Furthermore, a yaws config consist of one #gconf{}
record and and a list of lists of #sconf{} records.
(yes this could be much better documented ....)

The recommended way (by me - and the docs) to start yaws in embedded
mode is to

1. application:start(yaws) - with the embedded flag set to true
2. once your app has figured out how you want to run yaws from your
  own config files - invoke yaws_api:setconf/2

And yes you certainly need to study the #gconf() and the #sconf{}
records before constructing those records - thus the warning.

The idea of proplists is a no goer - IMHO, maybe for the #gconf{}
record yes, but not for all the virthosts.
The second arg to setconf/2 is [vlist()] where
vlist is [#sconf{}] with the additional requirement that each vhost()
actually can be vhosted - that is

each vhost() hast ip/port equal and host different
or if ssl - length(vhost()) == 1

(ssl cannot be virt hosted)

While I work on this, seeing as I have to read the source code anyway,

Here I do agree - we need better support, and yaws;start_embeded()
in combination with yaws:add_server() is a start - needs better docs
though. Especially the yaws_config:make_default_conf/2 and also
yaws_config:make_default_sconf/0 are poorly hidden

I'll see if I can work out a less intrusive way to start Yaws in embedded mode as part of a supervisor tree, even if it means modifying

This should also be plainly documented, e.g. how to get it into your
own supervision tree. Docs should be added to http://yaws.hyber.org/embed.yaws



For every expert there is an equal and opposite expert - Arthur C. Clarke