Embedding ejabberd into an Elixir Phoenix Web application

By combining Elixir powerful web framework with ejabberd realtime messaging platform, you can build extremely powerful applications. This tutorial will help you get started.

Here is the screencast showing the whole process. Please read further for detailed step-by-step description and code.

Create a Phoenix application

The first step is to create your Phoenix application as usual.

First, you need to install Elixir 1.0.2+.

From there you can clone and build Phoenix framework:

prompt> git clone https://github.com/phoenixframework/phoenix.git && cd phoenix && git checkout v0.10.0 && mix do deps.get, compile

Finally, you can generate your Phoenix application template:
prompt> mix phoenix.new /Users/mremond/demo/my_app

Please refer to Phoenix web site to learn more about it: Getting started with Phoenix.

Add ejabberd as a dependency for your application

You have two simple changes to perfom in your application mix.exs initial file:

  1. Add ejabberd as a dependency for your application:
...
  defp deps do
    [{:phoenix, "~> 0.10.0"},
     {:phoenix_ecto, "~> 0.1"},
     {:postgrex, ">= 0.0.0"},
     {:cowboy, "~> 1.0"},
     {:ejabberd, ">= 15.03.0", github: "processone/ejabberd"}]
  end
...

  1. Tell mix to start ejabberd when you launch your application:

...
  def application do
    [mod: {Phoenixtest, []},
     applications: [:phoenix, :cowboy, :logger, :ejabberd]] 
  end
...

  1. Download and build all dependencies:
    prompt> mix do deps.get, compile

For reference, here is the complete mix.exs

Before you can start your application, you need to configure ejabberd.

Configure your application and ejabberd

Copy ejabberd.yml example file in application config/ directory. I put that file in a gist online to make that step easier:
prompt> (cd config; wget https://gist.githubusercontent.com/mremond/383666d563025e86adfe/raw/723dfa50c955c112777f3361b4f2067b76a55d7b/ejabberd.yml)

You can tweak ejabberd config file to adapt it to your needs. Please, refer to ejabberd operation guide to configure it properly.

You also need to configure your Elixir application to tell it how to set global ejabberd values like configuration file, log directory and Mnesia file directory. In the file config/config.exs, add specification ejabberd configuration for integration in your application:


...
config :ejabberd,
  file: "config/ejabberd.yml",
  log_path: 'logs/ejabberd.log'
 
# Customize Mnesia directory:
config :mnesia,
  dir: 'mnesiadb/'
...

Make sure the directory where to place ejabberd log file does exist. If this is not the case, it will not be automatically created and no log file will be generated.

prompt> mkdir logs

Start your application

You can now start your application:

prompt> iex -S mix phoenix.server

As you see from the log printout, ejabberd is started along with your Elixir app.

You can create a user by entering the following command from Elixir command line:

iex(1)> :ejabberd_auth.try_register("mickael", "localhost", "mypass")
{:atomic, :ok}

You can connect with those credential from an XMPP client.

You can also connect on Elixir web server on https://localhost:4000/

Creating a page displaying ejabberd information

Let’s get started writing a Phoenix basic page that display ejabberd information.

In your project, edit the file web/router.ex and add a reference to a /ejabberd page in the my_app scope:

get "/ejabberd", EjabberdController, :index

The scope “/” block of our router.ex file should now look like this.


   scope "/", MyApp do
     pipe_through :browser # Use the default browser stack
 
     get "/", PageController, :index
     get "/ejabberd", EjabberdController, :index
   end
 

Let’s then create the file web/controllers/ejabberd_controller.ex:


defmodule MyApp.EjabberdController do
  use MyApp.Web, :controller
 
  # This is used to import the jid record structure from ejabberd:
  require Record
  Record.defrecord :jid, Record.extract(:jid, from: "deps/ejabberd/include/jlib.hrl")
    
  plug :action
  
  def index(conn, _params) do
    # get online jid, parse and extract the user part.
    online_users = :ejabberd_sm.connected_users
                      |> Enum.map &(jid(:jlib.string_to_jid(&1), :user))    
    render conn, "index.html", users: online_users
  end
end

The code doing the heavy duty job the one getting online user by JID and extracting the username. This is a couple of lines of code:


   # get online jid, parse and extract the user part.
   online_users = :ejabberd_sm.connected_users
                     |> Enum.map &(jid(:jlib.string_to_jid(&1), :user))    

We do not need anything fancy in our Phoenix view module and we will use default view placeholder web/views/ejabberd_view.ex:


defmodule MyApp.EjabberdView do
  use MyApp.Web, :view
  
end

Note: I could have put some data conversion code in the view code, but as the code is short, I preferred to have all the relevant ejabberd related code in one place.

Finally, we need the template for the page, named web/templates/ejabberd/index.html.eex:


<div class="jumbotron">
  <h2>Hello World, ejabberd meets Phoenix !</h2>

  <h3>Here is the list of online users:</h3>
  
  <%= for user <- @users do %>
  <p><%= user %></p>
  <% end %>
 
</div>

After starting the Phoenix server (iex -S mix phoenix.server) and connecting to the server using your XMPP client, you should see a page like the following:

ejabberd_embedded_phoenix

Conclusion

Here you are. This is all for this tutorial. You should now have environment properly set to start developing amazing ejabberd and XMPP powered web applications.

As you have seen, in a matter of minutes, you were able to create a powerful web app integrating ejabberd XMPP framework. By merging Phoenix and ejabberd, a whole new set of applications can emerge. We are eager to see what amazing apps you will build with it.

Special thanks to Sonny Scroggin (@scrogson) for the discussion and inspiration for this tutorial and video :)

References


Let us know what you think 💬


16 thoughts on “Embedding ejabberd into an Elixir Phoenix Web application

  1. I get tis error when I try to start the phoenix application

    ** (Mix) Could not start application ejabberd: exited in: :ejabberd_app.start(:normal, [])

    ** (EXIT) {:aborted, {:no_exists, :passwd, :attributes}}

    any ideas?

  2. Just jumping into this array of technologies – this was useful, so thanks :-)

    Can I ask, architecturally, if i wanted to use eJabberd to provide a real time chat component to a web api I am writing using Phoenix, would I expose the websockets via at the Phoenix layer or go directly to eJabberd? I’m sure I could probably do both but as i’m just learning i’m not sure of the implications.

    • hey Steven, I really hope you solved this back in 2015 :) Currently tasing almost the same issue and some architectural advise would be really helpful! :) thank you!

  3. Is it possible to have same user credentials for ejabberd and phoenix web app, is there a plug-in for ejabbrd to authenticate against users in the web app.>

  4. Hi Sir,

    Thanks for the helpful post

    But, I am getting error compiling the application using (mix do deps.get, compile)

    Error is

    ** (Mix) Invalid requirement >= 17.07.0 for app ejabberd

    Please help me to resolve this issue.

    Thanks

  5. iex -S mix phx.server
    ===> Compiling jiffy
    ‘.’ is not recognized as an internal or external command,
    operable program or batch file.
    ===> Hook for compile failed!

    ** (Mix) Could not compile dependency :jiffy, “escript.exe “c:/Users/Shaun Chua/.mix/rebar3” bare compile –paths “c:/Users/Shaun Chua/hello/_build/dev/lib/*/ebin”” command failed. You can recompile this dependency with “mix deps.compile jiffy”, update it with “mix deps.update jiffy” or clean it with “mix deps.clean jiffy”

  6. Hi! Thanks for this introduction to embedding ejabberd.

    It looks like “deps/ejabberd/include/jlib.hrl” no longer exists in ejabberd ~> 19.8.

    Is there another recommended record structure to do this jid-to-username conversion? Thanks!

  7. I get the following error in Phoenix 1.7:
    (RuntimeError) error parsing file deps/ejabberd/include/jlib.hrl, got: {:error, :enoent}

    Stacktrace:
    │ (elixir 1.14.4) lib/record/extractor.ex:84: Record.Extractor.read_file/2
    │ (elixir 1.14.4) lib/record/extractor.ex:50: Record.Extractor.extract_record/2
    │ lib/chat_web/controllers/ejabberd_controller.ex:5: (module)

    I think a lot has changed. An updated tutorial to the most recent version of Elixir/Phoenix will be very hepful.
    Thanks for a you do.

Leave a Reply to mossplix Cancel Reply


This site uses Akismet to reduce spam. Learn how your comment data is processed.