Arranque da Aplicação (Server) Design Reference

Keyworks: Arranque Boot Aplicação HttpApplication Bootstrap Bootstrappers Application Services Server Servidor

Conceitos

Servidor

Neste contexto considera-se como servidor todos os layers de uma aplicação que executam no IIS e ASP.NET, respondendo aos pedidos de um cliente (o browser). A aplicação tem uma componente cliente, no browser, uma componente servidor, no Web server que é iniciada quando o “site” IIS é iniciado.

Bootstrapper

Trata-se de um componente que implementa lógica de “boot” (inicialização).

OWIN

O OWIN é uma especificação que define um standard para a interface entre Web servers .NET e aplicações Web. O objetivo do OWIN é separar o Web server da aplicação, permitindo o desenvolvimento de pequenos módulos usados no desenvolvimento .NET para a Web. Sendo apenas uma especificação, mas definindo um “open standard”, permite que toda a comunidade “open source” participe no desenvolvimento desses módulos. O que, por sua vez, permite que esses módulos possam eventualmente ser usados em qualquer plataforma de desenvolvimento Web onde faça sentido a sua utilização.

Esta especificação, entre outras coisas, permite manipular/controlar o pipeline entre a aplicação e o servidor, nomeadamente:

  • Os módulos que devem ser registados.
  • O passo do pipeline em que cada um deles deve ser colocado.
  • O que fazer a seguir a cada um desses passos.
  • Adicionar ou retirar passos de forma muito simples.

Katana

O projeto Katana representa o conjunto de componentes Owin que são desenvolvidos e distribuídos pela Microsoft.

http://www.asp.net/aspnet/overview/owin-and-katana/an-overview-of-project-katana

Historicamente todo o desenvolvimento da Microsoft está assente no ASP.NET e no namespace System.Web, Isso impede uma resposta rápida às novas frameworks de desenvolvimento para a Web por estarem muito presas às versões da plataforma .NET. Estes componentes (pequenos com implementações com um âmbito muito mais restrito) separados da Framework .NET permitem uma evolução muito mais rápida e em separado dos mesmos. A própria Microsoft passou a usar o OWIN (com a sua implementação) para poder interagir com as suas várias plataformas

ASP.NET vNEXT

Esta será a nova framework Microsoft de desenvolvimento para a Web, que, para além dos componentes e implementações de autorização, Web API, self hosted, etc, irá conter também o suporte OWIN para todas as restantes funcionalidades disponíveis no universo Microsoft (exemplo: MVC). Os dados iniciais apontam também para uma grande melhoria de performance nomeadamente ao nível da memória consumida.

Para mais detalhe consultar;

http://www.asp.net/vnext/overview/aspnet-vnext/overview http://blogs.msdn.com/b/dotnet/archive/2014/05/12/the-next-generation-of-net-asp-net-vnext.aspx

Desenho

O processo de arranque da aplicação é responsável por:

  • Inicializar a plataforma em sí.
  • Reconhecer e configurar todos os módulos que fazem parte da aplicação, incluindo o registo dos application services ou outros tipos necessários ao funcionamento de cada módulo.
  • Registar as rotas MVC e Web API, por módulo.

Fluxo de Arranque da Aplicação

O diagrama seguinte representa o fluxo normal de inicialização de uma aplicação Web Elevation:

Arranque da aplicação

  • O processo inicia-se na classe Global (Global.asax) do host da aplicação.
  • Nessa classe, o evento Application_Start ocorre quando a aplicação é iniciada e invoca o classe GlobalApplicationEventsHandler da plataforma (Primavera.Core.Applications).
  • Nesse momento é iniciada uma nova HttpServerApplication que herda de ApplicationBase, onde o processo de inicialização standard está implementado (ver o método Start()).
  • O processo de “booting” da aplicação (ver BootApplication()) começa por detetar a ApplicationDefinition da aplicação em causa e criar uma nova instância dessa classe que contém as definições da aplicação.
  • De seguida o “application bootstrapper” é responsável pelos seguintes passos de inicialização. Esse bootstrapper é implementado por defeito pela classe DefaultApplicationBootstrapper (no método OnInitializing()).
    • Carregar para o app domain todas as assemblies da aplicação (ver AssembliesPreloader).
    • Detetar entre os tipos dessas assemblies todas as classes necessárias ao arranque da aplicação (ver RecognizedTypesFinder).
    • Em primeiro lugar os “component bootstrappers”, responsáveis pela inicialização de componentes da plataforma.
    • De seguida, os “module bootstrappers”, responsáveis pela inicialização dos módulos da aplicação.
    • E ainda os “application services” e “application service providers” publicados tanto pela plataforma como pelos vários módulos.
    • Posto isto, todos os application services são registados automaticamente na ObjectFactory.
    • De seguida, são registados os application service providers.
    • Depois são inicializados os bootstrappers dos componentes, para que estes possam executar as suas próprias inicializações.
    • E finalmente, são inicializados os bootstrappers dos módulos.
  • Posto isto a aplicação estará pronta a receber pedidos.

Component Bootstrappers

Um “component bootstrapper” é responsável pela inicialização de um componente necessário ao funcionamento da plataforma e da aplicação. Podem existir tantos quantos necessários e serão reconhecidos pela plataforma se verificarem as seguintes condições:

  • Devem herdar da classe base ComponentBootstrapperBase.
  • Devem estar anotados com o atributo [ComponentBootstrapper].
  • Devem implementar os métodos Configure() e Initialize().

No arranque da aplicação será invocado primeiro o método Configure(), para que o bootstrapper possa fazer os registos de que necessitar junto da ObjectFactory, e depois o Initialize() para realizar outras operações de inicialização. Em caso algum pode existir um registo na ObjectFactory a partir do Initialize().

/// <summary>
/// Defines the component bootstrapper.
/// The component bootstrapper is the entry point for that component, executed when the application
/// is first initialized, allowing configuration of dependencies and other initialization procedures.
/// </summary>
[ComponentBootstrapper]
internal class DomainBoostrapper : ComponentBootstrapperBase
(...)

Module Bootstrappers

Um “module bootstrapper” é responsável pela inicialização do respetivo módulo da aplicação. Tipicamente existirá um por cada módulo. Estas classes serão reconhecidas pela plataforma nas seguintes condições:

  • Devem herdar da classe base ModuleBootstrapperBase.
  • Devem estar anotados com o atributo [ModuleBootstrapper].
  • Devem implementar os métodos Configure() e Initialize().

Como acontece para os bootstrappers de componentes, primeiro será invocado o método Configure e depois o Initialize().

/// <summary>
/// Base class for the module server bootstrapper.
/// The bootstrapper is double-derived to perform non-optional registrations in the base class
/// and let the developer register other types in the actual bootstrapper.
/// </summary>
public class SimpleBusinessCoreBootstrapperBase : ModuleBootstrapperBase
(...)
/// <summary>
/// Server bootstrapper for the SimpleBusinessCore module.
/// </summary>
[ModuleBootstrapper]
public partial class SimpleBusinessCoreBootstrapper : SimpleBusinessCoreBootstrapperBase
(...)

Application Services e Application Service Providers

A plataforma é capaz de registar junto da ObjectFactory todos os application services e application service providers que encontrar na aplicação. Para esse registo funcionar corretamente, estas classes devem obedecer às seguintes regras.

No caso dos application services:

  • O contrato deve estar anotado com o atributo [ApplicationService].
  • A implementação deve estar anotada com o atributo [ApplicationServiceImplementation].
/// <summary>
/// Application service to handle user authentication.
/// </summary>
[ApplicationService]
public interface IAuthenticationService
(...)

/// <summary>
/// Default implementation for the <see cref="IAuthenticationService"/> application service.
/// </summary>
[ApplicationServiceImplementation(typeof(IAuthenticationService))]
internal class DefaultAuthenticationService : IAuthenticationService, IAuthenticationImpersonationService
(...)

No caso dos application service providers:

  • O contrato deve estar anotado com o atributo [ApplicationServiceProvider].
  • A implementação deve estar anotada com o atributo [ApplicationServiceProviderImplementation].
/// <summary>
/// Application provider to return information about auto numbers.
/// </summary>
[ApplicationServiceProvider]
public interface IAutoNumbersProvider
(...)

/// <summary>
/// Default implementation of the <see cref="IAutoNumbersProvider"/> application service provider.
/// </summary>
[ApplicationServiceProviderImplementation(typeof(IAutoNumbersProvider))]
internal class AutoNumbersProvider : IAutoNumbersProvider
(...)

Inicialização da Plataforma

Como foi referido anteriormente, a inicialização da plataforma é efetuada por “componente boostrappers”. As inicializações descritas nos pontos seguintes são fundamentais ao funcionamento da aplicação.

Inicialização MVC

A plataforma usa “atribute routing” para identificar as rotas MVC. Isso obriga a que exista um momento inicial de configuração, que ocorre no momento de inicialização da plataforma, a partir do WebBoostrapper.

namespace Primavera.Core.Web
{
    /// <summary>
    /// Provides methods to register MVC routes.
    /// </summary>
    public static class RouteConfig
    {
        #region Public Methods

        /// <summary>
        /// Initilizes MVC routes.
        /// </summary>
        /// <param name="routes">The routes that shold be initialized.</param>
        public static void InitializeMvcRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            AreaRegistration.RegisterAllAreas();
            routes.MapMvcAttributeRoutes();
        }

        #endregion
    }
}
namespace Primavera.Core.Web
{
    /// <summary>
    /// Defines the component bootstrapper.
    /// The component bootstrapper is the entry point for that component, executed when the application
    /// is first initialized, allowing configuration of dependencies and other initialization procedures.
    /// </summary>
    [ComponentBootstrapper(BootstrapperRuntimeOption.HttpOnly)]
    internal class WebBoostrapper : ComponentBootstrapperBase
    {
        #region Public Methods

        /// <summary>
        /// This method will be called so that the component can perform initialization operations.
        /// </summary>
        public override void Initialize()
        {
            // Default behavior

            base.Initialize();

            // Initialization

            WebBoostrapper.InitializeMvc();
            WebBoostrapper.InitializeViewEngines();
        }

        #endregion

        #region Private Methods

        private static void InitializeMvc()
        {
            FilterConfig.RegisterMvcGlobalFilters(GlobalFilters.Filters);
            RouteConfig.InitializeMvcRoutes(RouteTable.Routes);
        }

        private static void InitializeViewEngines()
        {
            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(new CustomRazorViewEngine());
        }

        #endregion
    }
}

Esta configuração implica que a partir desse momento cada um dos métodos disponibilizados tenha claramente identificado o URL a que responderão (contemplado já questões como tenant, organization e company) através da anotação RoutePrefix (mas isso fica garantido pela geração automática dos mesmos). Exemplo:

[RoutePrefix("{tenantKey}/{orgKey}/sales/items")]. [Route("{action=index}")]

Esta configuração permite também que os módulos deixem de ter necessidade de efetuar configurações adicionais para a definição das suas rotas.

Inicialização Web API

À semelhança do que acontece com o MVC, a plataforma também usa “atribute routing” para identificar as rotas Web. A inicialização das rotas Web API é efetuada usando Katana (implementação OWIN da Microsoft). Essa especificação obriga à existência de uma StartupClass no namespace da aplicação (de preferência marcada com a anotação conforme exemplo abaixo).

Isto permite usar a definição global de root implementada na classe HttpServiceConfiguration da core e registar as configurações específicas, de cada módulo.

using Microsoft.Owin;
using Owin;
using Primavera.Core.Services;

[assembly: OwinStartup(typeof(BusinessCore.Host.Startup))]

namespace BusinessCore.Host
{
    /// <summary>
    /// Startup class for OWIN implementation.
    /// </summary>
    public class Startup
    {
        /// <summary>
        /// Configurations the specified application.
        /// </summary>
        /// <param name="app">The application.</param>
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpServiceConfiguration();
            app.UseWebApi(config);
        }
    } 
}

Esta configuração implica que a partir daqui cada um dos métodos disponibilizados pela Web API tenha claramente identificado o URL a que responderá (contemplado já questões como tenant, organization e company) através da anotação RoutePrefix (mas isso fica garantido pela geração automática dos mesmos). Exemplo:

[RoutePrefix("api/{tenantKey}/{orgKey}/sales/items")]

Esta configuração permite também que os módulos deixem de ter necessidade de efetuar configurações adicionais para a definição das suas rotas Web API.

Tracing

Todo o processo de arranque da aplicação gera eventos de tracing para permitir diagnosticar erros. São despoletadas mensagens com os seguintes níveis:

  • Fatal
  • Warning
  • Information
  • Verbose

Para obter informação muito detalhada, incluindo os tipos reconhecidos automaticamente, os bootstrappers inicializados e os application services e application service providers registados, deve ativar-se o nível Verbose.

Decisões

XProd

Na versão anterior existia um ficheiro com extensão xprod que era responsável por identificar todos os módulos da aplicação bem como uma série de outros aspetos:

  • Branding.
  • Licenciamento.
  • Versão e nome do produto.
  • Questões relacionadas com a ajuda e novidades.
  • Questões relacionadas com a autenticação.

Foram tomadas as seguintes decisões:

  • Desligar da plataforma o branding do produto passando a existir muito maior liberdade no desenho de estilos para um produto.
  • Colocar numa classe estática (gerada) todos as questão ligadas unicamente a uma release (ou upgrade), tais como o nome da aplicação, produto, idiomas suportados…
  • Continuar a suportar um ficheiro de configuração adicional para definir os links de ajuda e novidades (para permitir alterar os sites sem obrigar a recompilar a aplicação).

WebBootstrapper

Esta classe (em Primavera.Core.Web) implementa lógica de inicialização da aplicação.

OWIN

O OWIN deve ser usado sempre que possível para interferir no processo de comunicação entre a aplicação e o host. Esta especificação está a ser cada vez mais usada pela Microsoft para desligar no ASP.NET e na Framework .NET uma série de novas funcionalidades.

FAQ

  • P: Porque é que apenas se pode invocar a ObjectFactory nos métodos Configure() dos bootstrappers?
    • R: A implementação da ObjectFactory requer que todos os registos sejam efetuados antes do primeiro Get, caso contrário ocorrerá uma exceção. Por isso os boostrappers são inicializados todos em sequência em dois passos. Primeiro o método Configure para todos e depois o método Initialize para todos.
  • P: Há um bootstrapper que não está a ser invocado no arranque da aplicação. Porquê?
    • R: Provavelmente porque não obedece corretamente às regras enumeradas em cima. De qualquer modo pode ativar-se o nível de tracing Verbose para obter informação detalhada sobre o processo de arranque da aplicação.
  • P: Como posso saber que classes estão a ser reconhecidas (sejam component bootstrappers, module bootstrappers, application services ou application service providers) no arranque da aplicação?
    • R: Deve ativar-se o nível de tracing Verbose para obter informação detalhada sobre esse processo.
  • P: Há um application service (ou application service provider) que não está a ser registado. Porquê?
    • R: Provavelmente porque não obedece corretamente às regras enumeradas em cima. De qualquer modo pode ativar-se o nível de tracing Verbose para obter informação detalhada sobre o processo de arranque da aplicação, incluindo o registo de application services.
  • P: A classe de implementação de um application service deve ser pública?
    • R: Não, antes pelo contrário. Sendo interna assegura-se que não é instanciada diretamente e a ObjectFactory funciona na mesma corretamente.