September 20, 2011

CTI

Meu negócio é criar software. Gosto de código bem escrito e de arquiteturas claras. E com bons motivos: clareza é fundamental para ter qualidade e riqueza funcional.

Gostaria de compartilhar aqui alguns elementos da arquitetura de um sistema que criamos, as razões por trás dessa arquitetura e as decisões tomadas. Porque gostei do resultado e para abrir uma eventual discussão. Afinal, arquitetura de sistemas se discute.

O Projeto

Novas tecnologias tem aparecido (VoIP, ...), criando uma onda de integração entre sistemas e telefonia (Computer Telephony Integration - CTI). O movimento é liderado pelos call centers e vários grandes atores ja se disputam esse mercado.

Ao mesmo tempo, muitas empresas poderiam se beneficiar de funcionalidades de CTI sem que as soluções desenvolvidas para call centers sejam aplicaveis.

Nasceu então o projeto de criar um middleware de integração de sistemas com telefonia, oferecendo funcionalidades de CTI.

Objetivos Arquiteturais

O primeiro objetivo é, claro, funcional: poder iniciar ligações a partir de virtualmente qualquer sistema (ativo) e receber notificações de chamadas em andamento ou entrando (por exemplo, para o sistema "cliente" poder reagir a chamadas entrando)

Além disso, os objetivos não funcionais eram fundamentais:
    1. Possibilidade de se interfacear com sistemas independentemente de plataformas ou linguagens.
    2. Agnosticismo dos clientes a respeito da implementação dos serviços, de telefonia no caso. O cliente deve poder iniciar uma ligação usando apenas conceitos de alto nível.
    3. Independência do middleware a respeito da topologia da telefonia.
    4. Sistema distribuido, com grande flexibilidade de implantação dos componentes nos nós da rede.
    5. Alta disponibilidade, com possibilidade de replicação/clusterização.
    6. Escalabilidade: possibilidade de usar a solução em clientes pequenos como em soluções de grande porte.
    Implementação

    Para o objetivo 3, o protocolo de sinalização natural para tal sistema é o SIP (Session Initiation Protocol, RFC 3261), por ser amplamente usado, inclusive pelo PBX open source mais usado, o Asterisk, e por ter implementações prontas em Java.

    Trabalhando no mundo Java EE, usamos SIP Servlets (JSR 116) para interagir com o lado de telefonia, usando o Mobicents como container. Baseado no JBoss, essa plataforma permite atender os objetivos 4, 5 e 6.

    Os objetivos 1 e 2 são típicos de arquiteturas orientadas a serviço. Para o objetivo 1 basta expor uma camada de serviços web usando protocolos permitindo a interoperabilidade (por exemplo SOAP). Para o objetivo 2 basta definir a semântica dos serviços em termos de alto nivel que façam sentido para os clientes. Assim, os serviços usam "atendida", "não atendida", "falha" para o status de uma ligação, sem entrar nos detalhes de status SIP.


    Uma importante característica da arquitetura é a sua asincronicidade. Ao contrário do modelo  request/response tradicional em aplicativos web, uma mesma chamada pode ter várias respostas no tempo, eventualmente bem distantes (por exemplo, um INVITE SIP terá várias respostas : primeiro RINGING (chamando) e depois OK (atendida). Essa asincronicidade foi implementada usando queues (JMS).

    O maior desafio do desenvolvimento foram as funcionalidades "passivas". Como transmitir notificações do aplicativo CTI para o sistema cliente na recepção de uma resposta ou de uma nova chamada. Ter um "polling" do cliente é um anti-pattern. Catastrófico em termos de performance e escalabilidade. Ter o servidor CTI chamar o aplicativo cliente é introduzir uma dependência do nosso sistema para o cliente (tudo que não queremos). A solução foi aceitar uma dependência do sistema cliente, mas reduzida para um componente apenas, eventualmente instalado independentemente. Esse componente é o "service adapter", encarregado de transmitir informações para o cliente - e dependendo dele pelo menos na sua configuração. Para implementações simples, esse adaptador é instalado junto com o aplicativo CTI. Para soluções maiores, esse adaptador pode ser colocado em um ESB (Enterprise Service Bus).

    September 05, 2011

    JBoss 7

    Yesterday I tried the brand new JBoss AS 7, deploying an application that uses many JavaEE resources: session beans, JPA, security, JMS, JSF.

    At first sight, it looked impressive. A lot simpler than previous versions (JBoss had turned almost unmanageable). Much simpler configuration (it is now possible to change ports without having to edit 345 cryptic configuration files). More modular, quickier. Great

    However, 4 problems convinced me that JBoss has released too quickly an immature product:
    - Documentation is still incomplete. This is pretty annoying since JBoss 7 configuration is radically different from previous versions
    - XML configuration file is re-written by the server as it runs. With its own format, removing the comments, and so on.
    - Management console offers interesting capabilities but it has bugs (as "field is not writeable" error when trying to edit a datasource JNDI name).

    - Worst of all, i ran into a bug (AS7-1602) ("Missing annotation index to scan entity classes") that prevented from sucessfully deploying my app. Bug that happens in a basical use case.

    Conclusion: very interesting, i'll keep trying new versions. But i'll also keep my apps on JBoss 6 for a while.

    April 19, 2011

    Lançamento da Confraria Empresarial em São Paulo

    O Brasil era o país do futuro. Hoje, ouço com mais frequencia que o futuro chegou. Nessa nova economia brasileira, economia de primeiro mundo, "esquemas", "jeitinhos" e outras "gambiarras" não têm mais espaço. Nesse quadro, jeitos de demonstrar ética e confiabilidade tem valor. A Confraria Empresarial é um deles. Pessoalmente, compartilho plenamente os valores da Confraria ("Não sou cobra").

    o propósito de formar uma rede confiável de relacionamentos de negócios. O principal diferencial da Confraria Empresarial é a sua filosofia, pois além de proporcionar agilidade nas negociações entre os participantes, ela oferece um ambiente confiável para a realização de negócios. Esta confiabilidade advém da forma de ingresso e das regras para a permanência de seus associados.

    Nascida no Sul do país, a Confraria está chegando a São Paulo por ocasão de um happy hour de negócios. Para os interessados em um ambiente diferenciado, a inscrição pode ser feita nesse formulário (as vagas são limitadas).

    March 08, 2011

    JBoss & Apache: Form-based Authentication

    So, configuring Jboss to run behind Apache, we got the right Apache config, we solved JSF/RichFaces issues rewriting generated links, using mod_proxy_hml or using a custom ViewHandler. End of the problems ? Not quite.

    Using form-based authentication raised two issues in our setup.

    Cookie Path Mismatch

    Form-based authentication uses the famous JSESSIONID cookie. Unfortunatley, without any specific configuration, the JSESSIONID cookie path on JBoss does not match the path on the proxy server, causing the cookie not to be sent back appropriately when submitting the login form.

    This might result in HTTP Status 408 (time allowed for the login process has been exceeded).

    Fortunately, a single configuration line in the Apache virtual host solves the issue (see the complete config):

    ProxyPassReverseCookiePath /appA /
    

    Login Redirect

    Another issue is that, after sucessful login, JBoss sends a redirect using the application context path ( /appA ), resulting in HTTP 404 error.

    To solve this, we used Apache URL rewriting capacity, to remove the context path from the requested URL and send a redirect to the modified URL (keeping the rest of the path or request parameters)

    RewriteEngine On
            RewriteRule ^/appA/(.*)$ /$1 [R]
    

    Will all those points solved, we finally managed to have different JBoss applications running in the same JBoss server and mapped to different subdomains.

    End of the story ? Not yet. We still want to (1) use HTTPS and (2) use Apache as a load balancer in front of a JBoss cluster. But this is yet to be tested...

    March 07, 2011

    JBoss & Apache: JSF, RichFaces and ViewHandler

    There is another solution to the problem described in my previous post (JSF, RichFaces and mod_proxy_html): modify the generated links at JBoss level and not at Apache level.

    The solution below has been implemented before finding mod_proxy_uml - but replaced by this one for three reasons:
    • mod_proxy_html is far simpler to use, just one or two lines of Apache configuration;
    • The application at jboss level runs unmodified, without needing to build a different ear for testing or production.
    • It transfers part of the processing to the apache serve.r

    Anyway, we publish the solution because it might be useful in some situations.

    JSF pages are generated by a "view handler" and it is possible to write a custom implementation. Based on [1], we implemented a view handler that replaces the context path by an arbitrary value.

    As we use Facelets, we extend the Facelet ViewHandler instead of the basical JSF one and just override the two relevant methods.

    import javax.faces.application.ViewHandler;
    import javax.faces.context.FacesContext;
    import com.sun.facelets.FaceletViewHandler;
    
    /**
     * Replaces the context path in the links generated
     * by JSF by an arbitrary value
     * configured as context parameter.
     * 
     * The alternative context path should be configured in
     * web.xml with name "br.com.net2tel.PROXY_CONTEXT_PATH"
     *  
     * (this is needed to use the app behind a reverse proxy,
     * with a different context path).
     */
    public class ReverseProxyViewHandler extends FaceletViewHandler {
     
     private ViewHandler defaultHandler;
     
     public ReverseProxyViewHandler(ViewHandler defaultHandler) {
      super(defaultHandler);
      this.defaultHandler = defaultHandler;
     }
    
    
     @Override
     public String getActionURL(FacesContext context, String viewId) {
      
      String actionURL = defaultHandler.getActionURL(context, viewId);
    
      return getProxyiedURL(context, actionURL);
     }
     
     @Override
     public String getResourceURL(FacesContext context, String path) {
      return getProxyiedURL(context, path);
     }
     
    
     /**
      * Actual string replacement. Simply replaces the context path
      * in the passed URL. For the root context, (/) just removes
      * the context path.
      * 
      * @param context Faces context (used to retrieve application context path
      *    and init parameter)
      * @param path Action or resource path to be modified
      * @return URL with changed context path
      */
     private String getProxyiedURL(FacesContext context, String path) {
      
      String contextPath = context.getExternalContext().getRequestContextPath();
      String proxyPath = context.getExternalContext().getInitParameter(
       "br.com.net2tel.PROXY_CONTEXT_PATH");  
      
      // For root proxy context, simply remove context path
      if ("/".equals(proxyPath))
       return path.replaceFirst(contextPath, ""); 
      else
       return path.replaceFirst(contextPath, proxyPath); 
     }
    
    }
    

    Two additional steps are necessary: configure the init parameter in web.xml and register the view handler in

    faces-config.xml
    
        
      br.com.net2tel.portability.ui.viewhandlers.ReverseProxyViewHandler       
     
    

    web.xml
    
     
      br.com.net2tel.PROXY_CONTEXT_PATH/
    


    References:
    [1] "Publishing JSF application from behind a proxy"

    Ok, now we should have everything ok. We faced one more problem, solved in two lines of configuration (already included in "Basical Apache Setup"). This is the covered by the next post: Form-based Authentication

    JBoss & Apache: JSF, RichFaces and mod_proxy_html

    As explained in a previous post (Basical Apache Setup), we configured our Apache server as a reverse proxy to a JBoss AS.

    But the problems continued. Many things in Java web applications are based on the application "context path". Just to remember, we want our web application http://jboss:8080/appA/ (/appA being the context root) to be mapped to the user-friendly url http://domainA.net2tel.com.br/

    Web pages generated by JSF contain actions and resources URLs like image source in img tags. But those links include the context path (in our case. /appA). The same applies to URLs generated by RichFaces. Unfortunately, as the application runs on a completely different URL, those generated links are incorrect and result in broken pages (missing images, css or scripts).

    Fortunately, an Apache module permits to filter the output html and modify it on the fly: mod_proxy_html.

    The following directive rewrites "/appA/" in the the links to "/"
    ProxyHTMLURLMap /appA/ /
    

    Of course, mod_proxy_html module should be installed for this to work. For module version older than 3.1, filtering is turned on using the following directive.
    SetOutputFilter proxy-html
    
    (ProxyHTMLEnable On is used for more recent versions)

    Another solution, using a custom ViewHandler, is described in the next serie's post: "JBoss & Apache: JSF, RichFaces and ViewHandler"

    March 05, 2011

    JBoss & Apache: Basical Apache Setup

    As explained in my previous post, we put our Mobicents application server behind an Apache server, with various web applications mapped to subdomains. Getting things right has been harder than expected and we decided to publish our setup - this might help others.

    For this example we'll have two subdomains, let's say domainA.net2tel.com.br and domainB.net2tel.com.br, both pointing to the same Apache server (same IP actually)). Two applications run on an internal JBoss server, for instance http://jboss:8080/appA and http://jboss:8080/appB

    Environment

    All this serie's posts refer to the following environment: Mobicents 1.5.0 (based on JBoss 5.1.0.GA) / Java 6 / Apache 2.2.3 (mod_proxy_html 2.5) on Linux Debian.

    Configuration

    The configuration to put JBoss behind Apache is actually quite simple. Simple but every single detail counts.

    We use mod_proxy_ajp, available since Apache 2.2. One other possible solution was mod_jk but the setup seemed consideralby more complex, without clear advantage of this module.

    The configuration is documented below. Directives ProxyPassReverseCookiePath and RewriteRule will be explained later, they are a solution to form-based authentication problems. ProxyHTMLURLMap is a solution to another issue, wich will be explained later too.

    It is entirely contained in the httpd.conf (but could be put in a different file, depending ou the setup)

    # This directive is needed in order to use named virtual hosts
    # specify the IP/port on which the server will receive 
    # requests for the name-based virtual hosts
    # Note that the specified address should match exactly the one
    # specified as VirtualHost name
    NameVirtualHost *:80
    
    # Prevents Apache from functioning as a forward proxy server
    # In a typical reverse proxy, set to off (which is the default value)
    ProxyRequests Off
    
    <VirtualHost *:80>
    
            # Define the domain name served by this virtual host
            ServerName domainA.net2telcom.br
    
            # Proxy requests to the java application
            ProxyPass / ajp://jboss:8009/appA/
    
            # Revrites http headers (Location, Content-Location, URI)
            # on http redirect responses to avoid bypassing this gateway
            # in redirects coming from the web application
            ProxyPassReverse / ajp://jboss:8009/appA/
    
            # Adjusts the Path string in Set-Cookie headers
            ProxyPassReverseCookiePath /appA /
    
            # Filters output HTML to convert the context path
            # generated from the proxyied server to the one
            # on this proxy
            SetOutputFilter proxy-html
            ProxyHTMLURLMap /appA/ /
    
            # Redirects requests to the /portability URL to
            # the root. It is needed for java form-based authentication
            # on JBoss (which redirects to the context path after login)
            RewriteEngine On
            RewriteRule ^/appA/(.*)$ /$1 [R]
    
    </VirtualHost>
    
    <VirtualHost *:80>
    
            ServerName domainB.net2tel.com.br
    
            ProxyPass / ajp://jboss:8009/appB/
            ProxyPassReverse / ajp://jboss:8009/appB/
    
            [Rest of the config omitted]
    
    </VirtualHost>
    
    
    <Proxy ajp://jboss:8009>
            Order allow,deny
            Allow from all
    </Proxy>
    
    
    

    Next Step : "JSF, RichFaces and mod_proxy_html"

    March 04, 2011

    JBoss & Apache: Introduction

    Developing SIP and GSM gateway applications at Net2Tel using Mobicents (RedHat SIP solution based on JBoss), we decided to put our application server behind a reverse proxy (Apache).

    Strangely enough, although this setup seemed common and well documented, it took us more than one day and may problems solved to get it working right. The documentation on the net (how-to's and forums) is scattered and we had to collect informations from various sources.

    We decided to document and publish here the complete setup and the list of problems we faced with their solution.

    Goals

    Placing JBoss behind Apache has several reasons:
    • Various Net2Tel systems run on JBoss but we do not want a JBoss instance for each application because this would highly complicates system administration. On the other hand, having various applications on the same server implies that each application has its own application context
    • User-friendly urls and subdomains: we want to map each application to the root of a different subdomain (igmc.net2tel.com.br, direto.net2tel.com.br).
    • Ports: JBoss HTTP service uses port 8080 by default and we want to keep this. In particular we do not want to run it on port 80 for three reasons: (i) we do not want to run JBoss as root, (ii) we want to keep the flexibility to have some web content served from Apache (for example, PHP application), keeping port 80 for this (iii) it would be a configuration nightmare. On the other hand, we want to hide this port from the users (see above))
    • Common and static content served directly from the web server, for obivous performance reasons

    Clustering and load balancing are not (yet) amongst our goals. Our setup is limited to proxying and url rewriting. But load balancing should be easy to add and will be the next step.

    Next step:
    Apache basical setup

    January 10, 2011

    Primeiro dia

    Primeiro dia. Entrevista. Vamos dedicar tempo e energia para a aprendizagem do Eduardo, quero ter o mínimo de certeza da motivação do candidato.

    Acontece que o Eduardo esta motivado por achar que é uma excelente oportunidade profissional. A oportunidade de ter um trabalho qualificado e bem remunerado. Por outro lado, ele não sabe se quer ser desenvolvedor, já que ele não faz a mínima idéia do que é essa profissão.

    Mas ele quer tentar. Vamos la.

    A aprendizagem não vai ser fácil, em todo caso no inicio. Faltam até noções básicas de informática. Mas acredito que, se houver talento e dedicação, nada é impossivel. Ele tem um computador, ele tem São Google, e ele tem nosso apoio.

    Agora a bola esta com ele...

    Diário de um aprendiz

    Começamos a experiencia.

    Hoje, o Eduardo (Dudu) começou conosco. Eudardo é um jovem (16 anos) com muita vontade de aproveitar uma oportunidade profissional. Aluno do ensino medio, Eduardo não sabe nada de informática além do uso básico. Nunca viu um computador rodando Linux, nunca ouviu falar de Java.

    Mas o que o Dudu vai fazer conosco ?

    Dudu vai participar de uma evolução do projeto Perey.

    A idéia na origem do projeto Perey é a seguinte: "Os salários na engenharia de software são altos comparados aos de outras áreas. Por outro lado, existem no Brasil muitas pessoas que, apesar de serem motivadas, não têm condição de seguir os roteiros tradicionais de formação

    Frente a esta situação, a Sofshore fomentou um projeto social de capacitação em engenharia de software, com o objetivo de ajudar candidatos a adquirir um nível que permita a sua empregabilidade.
    "

    Vamos trabalhar de jeito um pouco diferente da idéia inicial. Vamos fazer um intensivo. Para que o Dudu possa começar a participar rapidamente de desenvolvimentos. O trabalho vai ser presencial (e não a distância), com um suporte próximo e uma carga horária consequente.

    Talvez esse projeto seja utópico. Nós geeks sempre imaginamos projetos que vão ajudar o mundo (ver por exemplo o projeto Cauã). Mas acho que vale a pena tentar. Aprender nunca faz mal.