Bundling ErlyBank as an Application

Posted by on September 15, 2008

This is the fifth article in the otp introduction series. If you haven’t yet, I recommend you start with the first article which talks about gen_server and lays the foundation for our bank system. If you’re a quick learner, you can read the completed erlang files so far: eb_atm.erl, eb_event_manager.erl, eb_server.erl, eb_sup.erl, and eb_withdrawal_handler.erl.

The Scenario: The ErlyBank system is in a nice state right now with everything wrapped up under the supervisor, but ErlyBank is pressuring us to come up with a package which specifies an overall version so the system can be upgraded as a whole and can be controlled easily. They’ve commissioned us to do this task.

The Result: Erlang/OTP has a built-in module for making a set of modules which achieve some set of functionality into an application. An application can be started and stopped as a unit and can easily be reused in other systems.

What is an application?

An application consists of a typical OTP-style callback module and an application resource file which tells the Erlang system how to handle the application. In every Erlang runtime there is an application controller process, which stores the information for every loaded application resource and manages the processes if they have started. Note that the application controller manager is not responsible and will not restart your applications if they terminate. It merely stores process information to know whether or not the application is running.

There are only two callbacks for the application callback module:

  • start/2 - Called when an application is starting to initialize the processes that are part of that application. In a proper Erlang/OTP application, this will usually start a supervisor process which manages the other processes.
  • stop/1 - Called when an application is stopping so that the processes associated with an application can stop. This usually involves stopping the overall supervisor process for the application.

Application Callback Module

A skeleton for an application module can be viewed here. As you can see there are only two methods: the behavior methods.

So save that skeleton as eb_app.erl and let’s get it working with our ErlyBank system!

The Application Start Callback

Based on the skeleton, you’ll probably be able to figure out how to do this on your own, but if you need some guidance, then continue reading this paragraph. Here is the start method for eb_app:

start(_Type, _StartArgs) ->
  case eb_sup:start_link() of
    {ok, Pid} -> 
      {ok, Pid};
    Error ->
      Error
  end.

 

To start the application, all we have to do is start the supervisor. The application behavior requires us to return {ok, Pid} on success so it can verify that this application is running. Type is used to tell us why we’re starting, and is usually just “normal.” If we write a distributed application, then it can also signify that this application is starting due to failover and takeover, but that won’t be covered in this introductory article.

Also, start arguments may be specified in the application resource file, but we won’t be using them for ErlyBank so we mark the variable as unused.

The Application Stop Callback

The stop callback is even easier than the start!

stop(_State) ->
  exit(whereis(eb_sup), shutdown).

 

In the article on supervisors , I made a subtle hint to exit with kill to test that the supervisors worked. The kill message is actually a brutal kill and doesn’t call any graceful termination methods. In the stop method of the applicaton, we use the shutdown message which stops the supervisor gracefully.

There is one last piece to the application before we can actually run it, and that is to create the application resource file. But eb_app.erl is completed and can be viewed here.

Application Resource File Syntax

The application resource file is suffixed with .app but is actually just a file containing some erlang terms. Copied directly from the application documentation:

{application, Application,
  [{description,  Description},
   {id,           Id},
   {vsn,          Vsn},
   {modules,      Modules},
   {maxP,         MaxP},
   {maxT,         MaxT},
   {registered,   Names},
   {included_applications, Apps},
   {applications, Apps},
   {env,          Env},
   {mod,          Start},
   {start_phases, Phases}]}.

             Value                Default
             -----                -------
Application  atom()               -
Description  string()             ""
Id           string()             ""
Vsn          string()             ""
Modules      [Module]             []
MaxP         int()                infinity
MaxT         int()                infinity
Names        [Name]               []
Apps         [App]                []
Env          [{Par,Val}]          []
Start        {Module,StartArgs}   undefined
Phases       [{Phase,PhaseArgs}]  undefined
  Module = Name = App = Par = Phase = atom()
  Val = StartArgs = PhaseArgs = term()

 

As you can see, the application file contains an Erlang tuple which has various information about the application which the Erlang runtime will use to know how to handle the application. All the options, the tuples contained within the list, are optional, and if any are omitted, their respective default value will be used, which are listed above, also.

First, Application is an atom representing the name of the application. This atom must be the same as the filename of the application resource file. So if they resource file is named “erlybank.app,” then the application name is “erlybank.”

Those are a lot of options, but the ones that are important are the options that systools requires, because we will be using that module in the future to handle upgrading and packaging our system! systools requires description, vsn, modules, registered, and applications.

Description is a one line description of the application. That’s it!

vsn is the version number represented as a string. It can be anything you want! There is no standard format.

modules are all modules introduced by this application. These are the modules you wrote specifically for this application, ignoring any modules in any other applications.

registered is a list of names of all registered processes. systools will use this in the future to automatically detect any clashing process names in a system.

applications is a list of applications which must be started before this application. Include the application name of any external modules you use here so systools can create proper boot scripts in the future for the application.

ErlyBank’s Application Resource File

The following is the contents of the application resource file I made for ErlyBank:

{application, erlybank,
  [{description, "ErlyBank system."},
  {vsn, "1.0"},
  {modules, [eb_app, eb_sup, eb_server, eb_atm, eb_event_manager, eb_withdrawal_handler]},
  {registered, [eb_sup, eb_server, eb_atm, eb_event_manager]},
  {applications, [kernel, stdlib]},
  {mod, {eb_app, []}}
]}.

 

All the systools required options are included in addition to mod, which specifies the module that is used to start the application.

As application dependencies I explicitly list kernel and stdlib, but even if I left that list blank, they would be started anyways, as they’re required by the Erlang system. I just like to make everything as explicit as possible so when I come back to look at code later, I know exactly what is happening.

Save the above code as erlybank.app and we’re ready to test run it!

Loading and Running the Application From the Shell

First, you can use the loaded_applications method to check what applications are already loaded into the Erlang system. This is the output from my shell:

14> application:loaded_applications().
[{kernel,"ERTS  CXC 138 10","2.12.3"},
 {stdlib,"ERTS  CXC 138 10","1.15.3"}]

 

As you can see, it’s not loaded yet. But actually, if you run the start method and application sees that that application spec is not loaded yet, it will attempt to load it itself. But to show you how this works, I will run it explicitly:

1> application:load(erlybank).
ok
2> application:loaded_applications().
[{kernel,"ERTS  CXC 138 10","2.12.3"},
 {erlybank,"ErlyBank system.","1.0"},
 {stdlib,"ERTS  CXC 138 10","1.15.3"}]

 

The load method finds the app file locally and loads it. It does not load any of the actual Erlang code for ErlyBank, yet. After loading the spec, I ran loaded_applications again and you can easily see that the ErlyBank application is listed.

To start the ErlyBank system, just run application:start(erlybank) and to stop it, run the stop method with erlybank as the parameter. Play around with the bank system just to make sure it’s working. :)

Final Notes

In this article I talked about application. I explained what an application is, the files that are required for an application, how to load an application, and how to start and stop an application. I breezed over the options required by systools, which we will use in a future article.

And that is the end of article five in the Erlang/OTP introduction series. The sixth article will be published in an another few days and will cover release management, including topics such as creating boot scripts, packaging applications, and more.

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. [...] running Simplish - but I was subscribed to the feed before I left. Mitchell’s post about grouping program parts together into single-management-point units with Erlang’s applicationĀ … was enlightening, and like a lot I learn about Erlang, describes runtime features that certainly [...]

  2. Harish Mallipeddi Sep 16, 2008 01:59

    Excellent series Mitchell. Keep them coming!

    Just one suggestion - can you dump your code at github.com or just offer a zip file at the end of each article? Your examples can serve as templates for all my future OTP projects!

  3. Mitchell Sep 16, 2008 07:44

    Harish,

    I have another article coming on Wednesday and it has a ZIP file attached to it. If I have time I’ll do the same with this one, but I just wanted to let you know that I have noticed that the number of source files is getting a bit large to keep pasting them into pages. :)

  4. Doug Oct 01, 2008 10:37

    Do you know why it happens?
    When I tried to load the app, it threw an error complaining the sasl module is not loaded:

    3> application:loaded_applications().
    [{kernel,"ERTS CXC 138 10","2.12.4"},
    {stdlib,"ERTS CXC 138 10","1.15.4"}]
    4> application:load(erlybank).
    ok
    5> application:loaded_applications().
    [{kernel,"ERTS CXC 138 10","2.12.4"},
    {erlybank,"ErlyBank system.","1.0"},
    {stdlib,"ERTS CXC 138 10","1.15.4"}]
    6> application:start(erlybank).
    {error,{not_started,sasl}}
    7> application:start(erlybank).
    {error,{not_started,sasl}}

    And I tried to modified the erlybank.app to remove that sasl module this is what get

    2> application:loaded_applications().
    [{kernel,"ERTS CXC 138 10","2.12.4"},
    {stdlib,"ERTS CXC 138 10","1.15.4"}]
    3> application:load(erlybank).
    ok
    4> application:loaded_applications().
    [{kernel,"ERTS CXC 138 10","2.12.4"},
    {erlybank,"ErlyBank system.","1.0"},
    {stdlib,"ERTS CXC 138 10","1.15.4"}]
    5> application:start(erlybank).
    {error,{bad_return,{{eb_app,start,[normal,[]]},
    {’EXIT’,{undef,[{eb_app,start,[normal,[]]},
    {application_master,start_it_old,4}]}}}}}

    =INFO REPORT==== 1-Oct-2008::13:34:36 ===
    application: erlybank
    exited: {bad_return,
    {{eb_app,start,[normal,[]]},
    {’EXIT’,
    {undef,
    [{eb_app,start,[normal,[]]},
    {application_master,start_it_old,4}]}}}}
    type: temporary
    6>

    Thanks

  5. Witold Baryluk Nov 14, 2008 08:58

    Actully this should be:

    start(_Type, _StartArgs) ->
    eb_sup:start_link().

    stop(_State) -> % where _State is return value of start, or M:prep_stop/1 if exists
    ok.

    This exactly the same. Additionaly stop/1 is called by application master AFTER the application was already stoped.

    maxP is deprecated, and is ignored. (it was maximal number of allowed processes).

    maxT is maximal time in ms that app is allowed to run. after it it will be terminated.

    There is also very important callback M:config_change/3 used in upgrades, but it is anothere story.

    Anyway nice article!

  6. snaky Jun 19, 2009 01:11

    Is there a way to automatically generate the Application Resource file? What if some modules will be removed from a project and the resource file isn’t correspondingly updated? It’s rather inconvenient to write a list of all included modules manually.

Comments

Comments: