[erlang-questions] Learning OTP - Application, Supervisor, FSM, Server

Jeff Crane jefcrane@REDACTED
Mon Sep 1 09:39:51 CEST 2008


This code is modified from:
http://www.trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles


-module(tcp_server_app).
-author('saleyn@REDACTED').

-behaviour(application).

%% Internal API
-export([start_client/0]).

%% Application and Supervisor callbacks
-export([start/2, stop/1, init/1]).

-define(MAX_RESTART,	5).
-define(MAX_TIME,	60).
-define(DEF_PORT,	6123).

%% A startup function for spawning new client connection handling FSM.
%% To be called by the TCP listener process.
start_client() ->
	supervisor:start_child(tcp_client_sup, []).

%%----------------------------------------------------------------------
%% Application behaviour callbacks
%%----------------------------------------------------------------------
start(_Type, _Args) ->
	ListenPort = get_app_env(listen_port, ?DEF_PORT),
	supervisor:start_link({local, ?MODULE}, ?MODULE, [ListenPort, prechat_fsm]).
	
stop(_S) ->
	ok.

%%----------------------------------------------------------------------
%% Supervisor behaviour callbacks
%%----------------------------------------------------------------------
init([Port, Module]) ->
	{ok,
		{
			_SupFlags = {one_for_one, ?MAX_RESTART, ?MAX_TIME},
			[
				% TCP Listener
				{
					tcp_server_sup,										% Id = internal id
					{tcp_listener,start_link,[Port,Module]},						% StartFun = {M, F, A}
					permanent,											% Restart = permanent | transient | temporary
					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
					worker,											% Type = worker | supervisor
					[tcp_listener]										% Modules  = [Module] | dynamic
				},
				% Client instance supervisor
				{
					tcp_client_sup,										% Id  = internal id
					{supervisor,start_link,[{local, tcp_client_sup}, ?MODULE, [Module]]},	% StartFun = {M, F, A}
					permanent,											% Restart = permanent | transient | temporary
					infinity,											% Shutdown = brutal_kill | int() >= 0 | infinity
					supervisor,											% Type = worker | supervisor
					[]												% Modules  = [Module] | dynamic
				},
				% Chat Server instance
				{
					chat_server_sup,										% Id  = internal id
					{chat_server,start_link, []},								% StartFun = {M, F, A}
					permanent,											% Restart = permanent | transient | temporary
					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
					worker,											% Type = worker | supervisor
					[chat_server]										% Modules  = [Module] | dynamic
				}
			]
		}
	};

init([Module]) ->
	{ok,
		{
			_SupFlags = {simple_one_for_one, ?MAX_RESTART, ?MAX_TIME},
			[
				% TCP Client
				{
					undefined,											% Id = internal id
					{Module,start_link,[]},									% StartFun = {M, F, A}
					temporary,											% Restart = permanent | transient | temporary
					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
					worker,											% Type = worker | supervisor
					[]												% Modules = [Module] | dynamic
				}
			]
		}
	}.

%%----------------------------------------------------------------------
%% Internal functions
%%----------------------------------------------------------------------
get_app_env(Opt, Default) ->
	case application:get_env(application:get_application(), Opt) of
		{ok, Val} -> 
			Val;
		_ ->
			case init:get_argument(Opt) of
				[[Val | _]] -> 
					Val;
				error -> 
					Default
			end
	end.


This doesn't run, but what's worse is that I don't understand why. Let me walk through what I think I know.

In shell, I type:
application:start(tcp_server).

The OTP framework looks for and at the .app file for the argument specified.
It sees tcp_server.app and finds the line:
{mod, {tcp_server_app, []}},

This tells what args (happen to be [] in this case) to send the module tcp_server_app and the Type defaults to the atom "normal" somehow. start is always the default initial function according to the OTP framework.

Putting this altogether, we get the initial function call of:
tcp_server_app:start(normal,[])

Next it pulls the ListenPort out of the define directives.

According to what I've observed, behaviours are equivalent to java interfaces. Unlike java interfaces, you don't need to declare that you implement the interface for another module to call them, another module will simply do a "blind" call, assuming it's there. tcp_server_app implements both application and supervisor behaviours, even though it only declares 1 of them (application). The supervisor behaviour allows the supervisor module to start up a supervisor node via an init/1 fun. The following line:
supervisor:start_link({local, ?MODULE}, ?MODULE, [ListenPort, prechat_fsm]),

...says, spawn module tcp_server_app, function init/1, register the process locally as tcp_server_app, passing the args [ListenPort, prechat_fsm] to the init/1. This results in a spawned process:
tcp_server_app:init([ListenPort, prechat_fsm])

The first init([Port,Module]) matches so it is used.
Next, I had no idea what was going on:
{ok,{_SupFlags,[Arg1,Arg2]}};
until I ran into:
http://www.erlang.org/doc/design_principles/part_frame.html (look for child specifications)

% Child specification consists of...
Arg = 
{	
% unique c.spec identifier
tcp_server_sup,  

% spawn a process of tcp_listener:init([Port,Module]),
{tcp_listener,start_link,[Port,Module]}, 

% Restart always
permanent,

% How long to die after getting a "die" message from supervisor	
2000,	

% Is this child a sub-supervisor, otherwise: worker
worker,						

% No idea what this bad boy is for.
[tcp_listener]

}

My chat_server.erl is a gen_server, and I think that if someone can explain this last bit of the child specification, I might understand why I'm getting this at runtime (startup):
=INFO REPORT==== 1-Sep-2008::00:39:12 ===
    application: tcp_server
    exited: {shutdown,{tcp_server_app,start,[normal,[]]}}
    type: temporary
{error,{shutdown,{tcp_server_app,start,[normal,[]]}}}

Apologies for the inability to read specs, but I just don't understand.


      



More information about the erlang-questions mailing list