Microsoft .NET Application Blocks 2003 / 2004 980304 André Coutinho Departamento de Engenharia Informática Setembro 2004 Orientador ISEP: Paulo Sousa Microsoft .NET – Application Blocks Agradecimentos Muito obrigado ao meu orientador o Engº. Paulo Sousa, pela ajuda na realização deste relatório. I Microsoft .NET – Application Blocks Índice Agradecimentos .....................................................................................................I Índice ....................................................................................................................II Índice de figuras..................................................................................................VI Índice de Tabelas ...............................................................................................VII Glossário .......................................................................................................... VIII 1. Introdução ......................................................................................................... 1 1.1. O que são os application blocks? ................................................................... 1 1.1.1. Código redundante ...................................................................................... 1 1.1.2. Encapsulamento .......................................................................................... 1 1.2. Para que servem? ........................................................................................... 1 1.3. Benefícios de usar os application blocks ....................................................... 2 1.4. Estrutura do Relatório.................................................................................... 2 2. Data Access Application Block ........................................................................ 3 2.1. Introdução ...................................................................................................... 3 2.2. Classes principais do DAAB ......................................................................... 4 2.2.1. Classe SqlHelper......................................................................................... 4 2.2.1.1. Os métodos da classe SqlHelper .............................................................. 5 2.2.1.2. Detalhes de implementação ..................................................................... 7 2.2.1.3. Opções para passagem de parâmetros...................................................... 7 2.2.1.4. Funções Privadas ..................................................................................... 8 2.2.2. Classe SqlHelperParameterCache............................................................... 9 2.2.2.1. Os métodos da classe SqlHelperParameterCache.................................. 10 2.2.2.2. Detalhes de implementação ................................................................... 10 2.2.3. Usando o DAAB para executar comandos SQL....................................... 10 2.2.4. Usando o DAAB para executar Stored Procedures.................................. 11 3. Logging Application Block ............................................................................ 13 3.1. Introdução .................................................................................................... 13 3.1.1. Enterprise Instrumentation Framework .................................................... 13 3.2. Logging........................................................................................................ 14 3.2.1. Formatação da informação dos eventos.................................................... 15 3.2.2. Configuração dos níveis de log................................................................. 17 3.2.3. EnterpriseInstrumentation.config.............................................................. 19 4. Exception Management Application Block .................................................... 21 4.1. Introdução .................................................................................................... 21 4.2. Classes do EMAB ........................................................................................ 22 4.2.1. Classe BaseApplicationException ............................................................ 22 4.2.2. Classe ExceptionManagerSectionHandler................................................ 23 4.2.3. Classe ExceptionManager......................................................................... 23 4.2.4. Classe ExceptionManagerInstaller ........................................................... 24 4.2.5. Classe Default Publisher........................................................................... 24 4.2.6. A interface IExceptionPublisher............................................................... 24 4.2.6.1. Métodos da interface IExceptionPublisher ............................................ 24 4.2.7. A interface IExceptionXmlPublisher........................................................ 25 4.2.7.1. Métodos de IExceptionXmlPublisher.................................................... 26 5. Configuration Management Application Block.............................................. 28 5.1. Introdução .................................................................................................... 28 5.2. Elementos constituintes do CMAB.............................................................. 28 5.3. Objectivos .................................................................................................... 30 II Microsoft .NET – Application Blocks 5.4. Vantagens..................................................................................................... 30 5.5. Porquê a criação do CMAB ......................................................................... 32 5.6. Criação de um CSP de leitura ...................................................................... 33 5.7. Criação de um CSP de leitura/escrita .......................................................... 33 5.8. Criação de um DPP...................................................................................... 33 6. Updater Application Block ............................................................................. 35 6.1. Introdução .................................................................................................... 35 6.2. Arquitectura do UAB................................................................................... 36 6.2.1. Classes do UAB ........................................................................................ 40 6.2.1.1. Classe ServerApplicationInfo ................................................................ 40 6.2.1.1.1. Objectivos ........................................................................................... 40 6.2.2. Os ficheiros manifest ................................................................................ 41 7. Caching Application Block............................................................................. 42 7.1. Introdução .................................................................................................... 42 7.2. Funcionamento............................................................................................. 42 7.3. O que são os Service Agents........................................................................ 42 7.4. A interface IServiceAgent............................................................................ 43 7.4.1. Métodos da interface IServiceAgent......................................................... 43 7.5. O método PersistCallback............................................................................ 43 7.6. Término de um Service Agent ..................................................................... 43 7.7. Usando o CAB com outros application blocks ............................................ 43 7.8. Componentes primários do CAB................................................................. 44 7.8.1. Classe CacheManager............................................................................... 44 7.8.1.1. Métodos da classe CacheManager ......................................................... 44 7.8.1.2. Propriedades da classe CacheManager .................................................. 45 7.8.2.A classe CacheService ............................................................................... 45 7.8.2.1. Métodos da classe CacheService ........................................................... 45 7.8.3. A interface ICacheStorage ........................................................................ 46 7.8.3.1. Métodos da interface ICacheStorage ..................................................... 47 7.8.3.2. Propriedades da interface ICacheStorage .............................................. 47 7.9. Ficheiros de Configuração ........................................................................... 47 7.10. Protecção de dados..................................................................................... 49 7.10.1. A interface IDataProtection .................................................................... 49 7.10.1.1. Métodos do interface IDataProtection ................................................. 49 7.11. Expiração ................................................................................................... 50 7.11.1. Classe AbsoluteTime .............................................................................. 50 7.11.1.1. Construtores da classe AbsoluteTime.................................................. 50 7.11.1.2. Métodos da classe AbsoluteTime ........................................................ 50 7.11.2. Classe ExtendedFormatTime .................................................................. 50 7.11.3. Classe FileDependency........................................................................... 51 7.11.3.1. Construtores da classe FileDependency .............................................. 51 7.11.3.2. Métodos da classe FileDependency..................................................... 51 7.11.3.3. Eventos da classe FileDependency ...................................................... 51 7.11.4. Classe SlidingTime ................................................................................. 51 7.11.4.1. Métodos da classe SlidingTime ........................................................... 52 7.11.4.2. Eventos da classe SlindingTime .......................................................... 52 7.12. Exploração ................................................................................................. 53 7.12.1. A interface IScavengingAlgorithm ......................................................... 53 7.12.1.1. Métodos do interface IScavengingAlgorithm...................................... 53 7.13. Armazenamento ......................................................................................... 54 III Microsoft .NET – Application Blocks 7.13.1. Como escolher uma das opções de armazenamento ............................... 54 7.13.2. Classe MemoryMappedFileStream......................................................... 54 7.13.2.1. Construtores da classe MemoryMappedFileStream ............................ 55 7.13.2.2. Métodos da classe MemoryMappedFileStream................................... 55 7.13.2.3. Propriedades da classe MemoryMappedFileStream............................ 56 8. Aggregation Application Block ...................................................................... 57 8.1. Introdução .................................................................................................... 57 8.2. Funcionamento............................................................................................. 57 8.3. Usando o AAB com outros Application Blocks .......................................... 57 8.4. Design do AAB............................................................................................ 58 8.5. Classe AggregationRequest ......................................................................... 59 8.5.1. Construtor da classe AggregationRequest ................................................ 59 8.5.2. Métodos da classe AggregationRequest ................................................... 59 8.6. A estrutura AggregatedResult...................................................................... 59 8.6.1. Propriedades da estrutura AggregatedResult ............................................ 59 8.7. Ficheiro de configuração.............................................................................. 60 9.1. Introdução .................................................................................................... 64 9.2. Funcionamento............................................................................................. 64 9.3. Utilização do AIAB com outros application blocks .................................... 64 9.4. Arquitectura do AIAB ................................................................................. 65 9.4.1. O subsistema Request ............................................................................... 65 9.4.1.1. Classe AsyncRequestBacth.................................................................... 66 9.4.1.1.1. Construtor da classe AsyncRequestBatch........................................... 66 9.4.1.1.2. Métodos da classe AsyncRequestBatch.............................................. 66 9.4.1.1.3. Propriedades da classe AsyncRequestBatch....................................... 66 9.4.2. O subsistema Dispatcher........................................................................... 67 9.4.2.1. Classe AsyncROHServices.................................................................... 68 9.4.2.1.1. Construtor da classe AsyncROHService ............................................ 68 9.4.2.1.2. Métodos da classe AsyncROHService ............................................... 68 9.4.2.2. Classe AsyncDispatcher ........................................................................ 68 9.4.2.2.1. Propriedades da classe AsyncDispatcher............................................ 68 9.4.2.3. Classe Processor .................................................................................... 69 9.4.2.3.1. Métodos da classe Processor............................................................... 69 9.4.2.4. Classe ServiceAgentReqDetails ............................................................ 69 9.4.2.4.1. Construtor público da classe ServiceAgentReqDetails ...................... 69 9.4.2.4.2. Métodos da classe ServiceAgentReqDetails....................................... 69 9.4.2.4.3. Propriedades da classe ServiceAgentReqDetails................................ 70 9.4.2.5. Classe ResultManager............................................................................ 70 9.4.2.5.1. Métodos da classe ResultManager...................................................... 70 9.4.3. O subsistema Monitor............................................................................... 71 9.4.3.1. Classe RecoveryMonitor........................................................................ 72 9.4.3.1.1. Construtor da classe RecoveryMonitor............................................... 72 9.4.3.1.2. Métodos da classe RecoveryMonitor.................................................. 72 9.4.3.2. Classe GarbageCollection...................................................................... 72 9.4.3.2.1. Construtor da classe GarbageCollection............................................. 72 9.4.3.2.2. Métodos da classe GarbageCollection ................................................ 72 9.4.4. Design da base de dados ........................................................................... 72 9.4.5. Ficheiros de configuração ......................................................................... 73 10. User Interface Process Application Block .................................................... 77 10.1. Introdução .................................................................................................. 77 IV Microsoft .NET – Application Blocks 10.2. Descrição da solução.................................................................................. 77 10.3. Classes Factory .......................................................................................... 78 10.3.1. Classe GenericFactory ............................................................................ 79 10.3.1.1. Métodos da classe GenericFactory ...................................................... 79 10.3.2. Classe ControllerFactory ........................................................................ 79 10.3.2.1. Métodos da classe ControllerFactory .................................................. 79 10.3.3. Classe StatePersistenceFactory............................................................... 79 11. Conclusão...................................................................................................... 80 12. Bibliografia ................................................................................................... 82 V Microsoft .NET – Application Blocks Índice de figuras Figura 1: Arquitectura do DAAB ......................................................................... 4 Figura 2: A arquitectura EIF ............................................................................... 14 Figura 3: Arquitectura do Logging Application Block....................................... 15 Figura 4: Como funciona o EMAB..................................................................... 21 Figura 5: A arquitectura do EMAB .................................................................... 22 Figura 6: Relação entre os elementos do CMAB (arquitectura)......................... 28 Figura 7: Mantendo aplicações actualizada com o UAB.................................... 35 Figura 8: Arquitectura do UAB .......................................................................... 36 Figura 9: Mapeamento do ficheiro de configuração do updater da aplicação (application updater) .......................................................................................... 38 Figura 10: Arquitectura de validação do lado do cliente .................................... 38 Figura 11: Arquitectura de validação do lado do servidor.................................. 39 Figura 12: Arquitectura de post processing do UAB ......................................... 40 Figura 13: Mapeamento da classe ServerApplicationInfo num ficheiro manifest ............................................................................................................... 41 Figura 14: Arquitectura do AAB ........................................................................ 58 Figura 15: Arquitectura do AIAB ....................................................................... 65 Figura 16: Base de dados usada pelo AIAB ....................................................... 73 Figura 17: Funcionamento do Controller ........................................................... 78 VI Microsoft .NET – Application Blocks Índice de Tabelas Tabela 1: As diferentes assemblies do CAB....................................................... 44 VII Microsoft .NET – Application Blocks Glossário AAB - Aggregation Application Block. AIAB - Asynchronous Invocation Information Application Block. BITS - Background Intelligent Transfer Service. CAB - Caching Application Block. CMAB – Configuration Management Application Block. CSP – Configuration Section Provider. DAAB – Data Access Application Block. DPAPI – Data Protection API. DPP – Data Protection Provider. EIF – Enterprise Instrumentation Framework. EMAB – Exception Management Application Block. FSP – Foreign Service Provider. LAB – Logging Application Block. MAC – Message Authentication Code. MMF – Memory-mapped file MVC – Model View Controller. RAD – Rapid Application Development. UAB – Updater Application Block. UIPAB – User Interface Process Application Block. WMI – Windows Management Instrumentation. XML – Extensible Markup Language. XSLT – Extensible Stylesheet Language. VIII Microsoft .NET – Application Blocks 1. Introdução 1.1. O que são os application blocks? A Microsoft disponibiliza application building blocks para o Framework .NET, que encapsulam certas áreas chave, facilitando a construção de aplicações ao fornecerem implementações out of the box de serviços comuns tais como acesso a dados, configuração e tratamento de excepções. Os application building blocks promovem o desenvolvimento de aplicações mais rápido e mais correcto, fazendo uso de boas práticas de programação. A seguir são apresentados os principais motivos que levaram à criação dos .NET application blocks. 1.1.1. Código redundante A redundância de código é um problema grave que afecta a dimensão, eficiência, manutenção e correcção de erros de uma aplicação. Determinar a redundância é útil para reduzir a programação “copy/paste” que se acumulou num projecto. Os problemas gerados por este tipo de atitude ao programar, são os seguintes: • • • • • • • Um qualquer erro num determinado algoritmo terá de ser corrigido em todas as aplicações em que foi introduzido. Tornar o programa mais genérico. Alertar o programador que está a inventar algo já existente. Descobrir cópias. Falta de encapsulamento, isto torna a manutenção e extensão bastante complexas. Durante a manutenção de programas é frequente termos de reescrever código, para o tornar mais claro, mais genérico, mais eficiente e menos redundante. A reescrita é perigosa, pois pode não preservar a semântica do código original (introduz bugs). 1.1.2. Encapsulamento A falta de encapsulamento também é um problema, que afecta a manutenção e extensão da aplicação, e aumenta a sua complexidade. Sem encapsulamento, as informações são globais e podem ser acedidas directamente, criando confusão sobre quem definiu o quê e quando. A ideia subjacente ao encapsulamento é proteger a informação (código, dados), por trás de uma barreira, para evitar a utilização indevida de determinado objecto ou classe que possa corromper a informação a proteger. 1.2. Para que servem? Os application blocks encapsulam uma optimização das boas práticas e técnicas de programação para trabalhar no Framework .NET, como já foi dito. São reutilizáveis, e permitem reduzir grande parte do código redundante (aquele André Coutinho 1 Microsoft .NET – Application Blocks código reescrito cada vez que queremos implementar uma determinada funcionalidade). Considerando, por exemplo, uma aplicação que requer acesso a uma base de dados, verifica-se que se usa repetidamente muitos blocos de código. Um application block encapsula o código mais utilizado, fornecendo portanto uma forma simples de reutilizar código eficientemente. Outra vantagem dos application blocks consiste na camada que formam sobre o Framework .NET. Usando os application blocks é possível reter os métodos mais expostos/usados dos application blocks e internamente lidar com as mudanças na Framework .NET. A aplicação fica protegida das mudanças que poderão ocorrer nas bibliotecas .NET, e assim, aumentar a capacidade da manutenção. 1.3. Benefícios de usar os application blocks Sem dúvida que o maior benefício de usar os application blocks é a capacidade que estes têm de tornar o desenvolvimento mais rápido e modular de programas. Uma vez que muito do código está encapsulado, isto vai permitir mais concentração na “business logic” da aplicação. 1.4. Estrutura do Relatório Este relatório está estruturado de acordo com o tema que levou à sua realização, os application blocks .NET da Microsoft, ou seja, existe um capítulo por application block (um block, um capítulo). Além destes, foram também incluídos um capítulo de introdução, conclusão e bibliografia. Tendo sido estudados nove application blocks, cada capítulo dedicado a um determinado application block começa por fazer um pequena introdução e seguidamente são descritas as suas principais classes, métodos e interfaces, com exemplos, bem como detalhes de implementação nos casos em que isto se justificou. Anteriormente à descrição das classes, métodos e interfaces, apresentam-se ainda os objectivos da criação de cada application block, bem como as suas vantagens. São ilustrados também, alguns casos de utilização. André Coutinho 2 Microsoft .NET – Application Blocks 2. Data Access Application Block 2.1. Introdução O Data Access Application Block é um componente disponível para a Framework .NET, que contém código de acesso a dados, optimizado, e que facilita a execução de Stored Procedures e comandos SQL, sobre uma base de dados Microsoft SQL Server. A sua implementação é centrada à volta da classe SqlHelper (responsável pelo encapsular das tarefas mais comuns de acesso e gestão de dados) que possui métodos estáticos para os serviços mais comuns de acesso a dados (ex., efectuar um select para obter um DataSet). O facto destes métodos serem estáticos significa que podem ser chamados sem existir uma instância da classe. Por exemplo, o método ExecuteReader() (classe SqlHelper) pode ser chamado usando simplesmente SqlHelper.ExecuteReader, não é necessário instanciar um objecto da classe SqlHelper. Estes métodos estáticos do DAAB (como daqui em diante será referido) podem ser utilizados para devolver objectos do tipo SqlDataReader, DataSet e XmlReader contendo a informação desejada. É ainda possível especificar parâmetros para os Stored Procedures, e a classe está preparada para lidar com transacções. Especificamente o DAAB ajuda a: • Executar Stored Procedures e comandos de texto SQL, mais facilmente. • Especificar detalhes dos parâmetros. • Retornar objectos do tipo SqlDataReader, Dataset, XmlReader, ou valores isolados. • Usar tabelas e campos strong typed. • Suportar caching de parâmetros, quando necessários. • Permitir a adição de novas tabelas, através da passagem de Datasets préexistentes. • Actualizar um DataSet, com comandos de update especificados pelo utilizador. • Criar objectos SqlCommand. • Permitir que DataRows sejam passados como parâmetros. A versão analisada neste capítulo é a versão 2 deste application block. Relativamente ao anterior foram acrescentadas as seguintes funcionalidades: • Suporte para strongly typed Datasets com o método FillDataSet. • Suporte à realização de updates sobre um Dataset, para a base de dados. • Métodos helper adicionais com suporte para parâmetros do tipo DataRow. André Coutinho 3 Microsoft .NET – Application Blocks Os principais elementos do DAAB estão ilustrados na Figura 1. Figura 1: Arquitectura do DAAB 2.2. Classes principais do DAAB 2.2.1. Classe SqlHelper A classe SqlHelper é o componente central do DAAB. Fornece métodos que permitem implementar soluções de acesso a dados para bases de dados SQL Server. Os principais objectivos desta classe são: • Encapsular os métodos mais comuns de acesso a dados, minimizando o código customizado a usar. • Permitir que as aplicações cliente executem updates a bases de dados, ou que recebam valores, objectos SqlDataReader, objectos DataSet, ou objectos XmlReader, usando um conjunto consistente de métodos. • Permitir que as aplicações cliente executem comandos SQL ou Stored Procedures, usando o mesmo método. André Coutinho 4 Microsoft .NET – Application Blocks • • • • • • A especificação por parte das aplicações cliente da string de conexão ou o objecto SqlConnection para determinar a origem dos dados. Permitir que a aplicação cliente especifique parâmetros se necessário, que serão passados como um array de objectos SqlParameter, ou ainda, permitir a passagem de parâmetros para Stored Procedures. Especificar uma transacção ADO.NET, por parte dos clientes, e poder introduzir um comando na transacção. Possibilitar o preenchimento de um DataSet. Possibilitar a persistência do update feito a um DataSet para uma base de dados física. Suportar DataSets strong type. 2.2.1.1. Os métodos da classe SqlHelper Para satisfazer os objectivos da classe SqlHelper, esta classe possui vários métodos com overload, para cada tarefa de acesso a dados. Estes métodos fornecem múltiplas implementações de uma mesma função (overload), de modo a aumentar a flexibilidade em termos de informação sobre a conexão, parâmetros e transacções. A seguir apresentam-se os métodos constituintes desta classe, juntamente com um dos seus overloads, para cada um dos métodos. Uma vez que cada método tem vários overloads, decidiu-se apresentar apenas um overload de cada um. Os métodos da classe SqlHelper são então os seguintes: • ExecuteNonQuery. Este método é usado para executar comandos que não devolvem valores ou linhas de uma tabela. É geralmente utilizado para executar updates de bases de dados, mas também para devolver parâmetros de saída de Stored Procedures. public static int ExecuteNonQuery( string connectionString, CommandType commandType, string commandText) • ExecuteReader. Método utilizado para devolver objectos SqlDataReader contendo o resultado devolvido por um comando SQL executado. private static SqlDataReader ExecuteReader( SqlConnection connection, SqlTransaction transaction, CommandType commandType, string commandText, SqlParameter[] commandParameters, SqlConnectionOwnership connOwnership) • ExecuteDataSet. Este método retorna um objecto DataSet contendo o resultado da execução de um comando. public static DataSet ExecuteDataset( string connectionString, CommandType commandType, string commandText) André Coutinho 5 Microsoft .NET – Application Blocks • ExecuteScalar. Retorna apenas um valor. Este valor é sempre a primeira coluna, da primeira linha devolvida pela execução do comando SQL. public static object ExecuteScalar( string connectionString, CommandType commandType, string commandText) • ExecuteXmlReader. Devolve um fragmento XML, de um inquérito FOR XML. public static XmlReader ExecuteXmlReader( SqlConnection connection, CommandType commandType, string commandText) • FillDataSet. Método similar ao ExecuteDataSet, mas que em vez de devolver um novo objecto DataSet, adiciona informação ao DataSet existente. public static void FillDataset( string connectionString, CommandType commandType, string commandText, DataSet dataSet, string[] tableNames) • UpdateDataSet. Propaga as mudanças efectuadas num DataSet, para a base de dados física. public static void UpdateDataset( SqlCommand insertCommand, SqlCommand deleteCommand, SqlCommand updateCommand, DataSet dataSet, string tableName) • CreateCommand. Este método permite especificar os argumentos para o comando a ser usado pelo UpdateDataSet. public static SqlCommand CreateCommand( SqlConnection connection, string spName, params string[] sourceColumns) • ExecuteNonQueryTypedParams. É similar ao ExecuteNonQuery, no entanto os parâmetros de saída obtidos vêm de um DataRow. public static int ExecuteNonQueryTypedParams( String connectionString, String spName, DataRow dataRow) André Coutinho 6 Microsoft .NET – Application Blocks • ExecuteDataSetTypedParams. Este método é idêntico ao ExecuteDataset, mas os parâmetros de saída obtidos vêm de um DataRow. public static DataSet ExecuteDatasetTypedParams( string connectionString, String spName, DataRow dataRow) • ExecuteReaderTypedParams. Idêntico ao método ExecuteReader, com a excepção dos parâmetros obtidos vêm de um DataRow. public static SqlDataReader ExecuteReaderTypedParams( String connectionString, String spName, DataRow dataRow) • ExecuteScalarTypedParams. Este método é idêntico ao ExecuteScalar, mas os parâmetros de saída obtidos vêm de um DataRow. public static object ExecuteScalarTypedParams( String connectionString, String spName, DataRow dataRow) • ExecuteXmlReaderTypedParams. Este ExecuteXmlReader, mas os parâmetros DataRow. método é idêntico ao de saída obtidos vêm de um public static XmlReader ExecuteXmlReaderTypedParams( SqlConnection connection, String spName, DataRow dataRow) 2.2.1.2. Detalhes de implementação A classe SqlHelper foi desenhada para encapsular as funcionalidades de acesso a dados, através de uma série de métodos estáticos. Como não foi criada para ser herdada ou instanciada, esta classe é declarada como não herdável com um construtor privado. Cada um dos métodos implementados consiste num conjunto de overloads consistente. Isto providencia um padrão bem definido para execução de comandos usando esta classe, enquanto dá aos programadores a flexibilidade para escolherem a forma de acesso aos dados. Os overloads de cada método suportam diferentes argumentos, de modo a ser possível decidir como é passada a informação sobre parâmetros, conexão e transacções. 2.2.1.3. Opções para passagem de parâmetros Uma das principais opções aquando da chamada de um método da classe SqlHelper é como fazer a passagem de parâmetros para o comando a executar. As opções são: • Usar um overload que não aceite parâmetros para o comando. André Coutinho 7 Microsoft .NET – Application Blocks • • • Esta opção é útil quando, por exemplo, há necessidade de executar um Stored Procedure que não leva qualquer parâmetro. Usar um overload que aceite objectos SqlParameter (commandParameters). Este overload que aceita objectos SqlParameter, também aceita um array dos mesmos objectos. Este array é marcado com a palavra chave ParamArray. Isto significa que existem duas abordagens a estes overloads. É possível passar um array contendo os parâmetros para o comando, ou então passar objectos SqlParameter individualmente para cada parâmetro do comando. Usar um overload para StoredProcedures que aceitem uma lista de valores de parâmetros (parameterValues). Internamente, estes parameterValues são atribuídos a um array de objectos SqlParameter usando a função AssignParameter. Embeber parâmetros num DataRow e chamar um dos métodos *TypedParams, como por exemplo, o ExecuteNonQueryTypedParams. 2.2.1.4. Funções Privadas Além dos métodos públicos, a classe SqlHelper inclui funções privadas, usadas para gerir parâmetros e preparar comandos para execução. Independentemente da implementação de determinado método usada, todos os comandos são executados usando um objecto SqlCommand. Antes deste objecto ser executado, quaisquer parâmetros devem ser adicionados à sua colecção de objectos Parameter. As propriedades Connection, CommandType, CommandText, e Translation também devem ser definidas correctamente. O principal objectivo destas funções é fornecer uma forma consistente de execução de comandos sobre uma base de dados SQL Server, independentemente da implementação do método usada. Os métodos privados são: AttachParameters. Os clientes podem passar a esta função um array de objectos SqlParameter, com os parâmetros associados ao comando a executar. Antes de um objecto SqlCommand ser usado para executar o comando, cada parâmetro do array deve ser adicionado à colecção de objectos Parameters do objecto SqlCommad. AssignParameter. Esta função é utilizada quando um Stored Procedure é chamado, usando uma lista de parâmetros separados por vírgulas (Param1, Param2, ...Paramn). A função executa um ciclo sobre um array de objectos SqlParameters, contendo os parâmetros para o Stored Procedure e atribui o valor correspondente a partir do array de parâmetros passados pela aplicação cliente. Depois destes parâmetros estarem atribuídos, o array de objectos SqlParameters pode ser passado à função para ser adicionado ao objecto SqlCommand. Existe um overload desta função que recebe um DataRow como argumento. André Coutinho 8 Microsoft .NET – Application Blocks PrepareCommand. Esta função serve para definir as propriedades do objecto SqlCommand usado para executar o Stored Procedure ou comando especificado pela aplicação cliente. A função PrepareCommand faz a conexão à base de dados e atribui as propriedades Connection e CommandType, verificando também se alguma transacção foi especificada, e define a propriedade Transaction, no caso de ter sido. Finalmente define a propriedade CommandText e executa o comando. ExecuteReader. O ExecuteReader permitir que o DataReader seja aberto usa um método interno privado, para com o CommandBehavior apropriado para gerir o tempo de vida da conexão foi passado pelo cliente ou criado pela classe SqlHelper a partir de uma string de conexão. Um enum privado é usado para definir a origem do objecto de conexão. FillDataSet. Os overloads públicos do método FillDataSet são implementados por uma chamada a um overload interno e privado do mesmo método. 2.2.2. Classe SqlHelperParameterCache A classe SqlHelperParameterCache providencia funcionalidades para o armazenamento (caching), e retorno de parâmetros utilizados pelos comandos e Stored Procedures executados pelas aplicações. É comum as aplicações executarem um determinado comando por diversas vezes. A vantagem que esta classe traz, é que torna desnecessária a recriação dos parâmetros usados por um qualquer comando, para cada execução do mesmo. Existem dois problemas na gestão de parâmetros que a classe SqlHelperParameterCache resolve. Estes problemas são: • Os parâmetros que um Stored Procedure necessite, podem ter de ser identificados dinamicamente. • O armazenamento e recuperação de parâmetros para posteriores execuções. Não só é benéfico descobrir parâmetros em run time (ou seja dinamicamente), porque permite simplificar em muito o código, como também é importante armazenar os parâmetros usados. Abaixo são descritos os objectivos da classe: • Os parâmetros devem ser descobertos e mapeados pela sua posição, em run time, com o mínimo de impacto possível na performance da aplicação. • Deverá ser possível para o utilizador armazenar e recuperar arrays de objectos SqlParameter. • Quando é recuperado um array de objectos SqlParameter, as mudanças feitas aos parâmetros desse array não deverão afectar os valores em cache. • Os utilizadores deverão conseguir recuperar dinamicamente um array de parâmetros para um determinado Stored Procedure. Os parâmetros neste array deverão ter o nome correcto, bem como o tipo de dados, tamanho e direcção. André Coutinho 9 Microsoft .NET – Application Blocks 2.2.2.1. Os métodos da classe SqlHelperParameterCache Esta classe do DAAB tem três métodos (públicos) que podem ser utilizados para gerir parâmetros. Estes métodos são os seguintes: • CacheParameterSet. SqlParameter. Usado para armazenar um array de objectos public static void CacheParameterSet( string connectionString, string commandText, params SqlParameter[] commandParameters) • GetCachedParameterSet. Método utilizado para recuperar uma cópia de um array de parâmetros anteriormente armazenado. public static SqlParameter[] GetCachedParameterSet( string connectionString, string commandText) • GetSpParameterSet. Este método pode ser usado para recuperar os parâmetros apropriados para a execução de um determinado Stored Procedure, questionando a base de dados uma vez e guardando os resultados para futuros inquéritos à base de dados. public static SqlParameter[] GetSpParameterSet( string connectionString, string spName) 2.2.2.2. Detalhes de implementação Os arrays de parâmetros são armazenados numa tabela de hash privada. Internamente os parâmetros recuperados da cache são copiados para que a aplicação cliente possa mudar os valores dos parâmetros, sem afectar os valores já guardados. A função privada CloneParameter, é usada para fazer isto. Um array de objectos SqlParameter pode ser guardado em cache usando o método CacheParameterSet. Este método cria uma chave, concatenando a string de conexão e o comando SQL passado para o método. O método GetSpParameterSet, que possui dois overloads, permite recuperar os parâmetros para um Stored Procedure específico a partir da cache. Se os parâmetros não estão em cache, serão recuperados a partir da base de dados, usando uma função privada, DiscoverSpParameterSet, que por sua vez usa o método DeriveParameter da classe CommandBuilder do ADO.NET, para determinar quais os parâmetros adequados para o Stored Procedure em questão. 2.2.3. Usando o DAAB para executar comandos SQL Para ilustrar as vantagens do uso do DAAB, abaixo está um exemplo do código necessário para criar um objecto SqlDataReader ligado a um objecto DataGrid sem usar o DAAB. Geralmente, para devolver um DataReader é necessário André Coutinho 10 Microsoft .NET – Application Blocks estabelecer uma conexão à base de dados, criar um comando SQL e executá-lo sobre a base de dados. O objecto SqlDataReader resultante pode então ser ligado ao objecto DataGrid: //create the connection string and sql to be executed string strConnTxt = "Server=(local);Database=Northwind;Integrated Security=True;"; string strSql = "select * from products where categoryid = 1"; //create and open the connection object SqlConnection objConn = new SqlConnection(strConnTxt); objConn.Open(); //Create the command object SqlCommand objCmd = new SqlCommand(strSql, objConn); objCmd.CommandType = CommandType.Text; //databind the datagrid by calling the ExecuteReader() method DataGrid1.DataSource = objCmd.ExecuteReader(); DataGrid1.DataBind(); //close the connection objConn.Close(); Agora vejamos o mesmo exemplo, mas usando o método ExecuteReader da classe SqlHelper: //create the connection string and sql to be executed string strSql = "select * from products where categoryid = 1"; string strConnTxt = "Server=(local);Database=Northwind;Integrated Security=True;"; DataGrid4.DataSource = SqlHelper.ExecuteReader(strConnTxt, CommandType.Text, strSql); DataGrid4.DataBind(); Como se pode ver há consideravelmente menos código do que no exemplo anterior. Para executar um comando SQL e obter um SqlDataReader, o método ExecuteReader() requer apenas a string usada para estabelecer a conexão, o tipo de comando a executar e o comando SQL que se quer executar. 2.2.4. Usando o DAAB para executar Stored Procedures O método ExecuteReader tem vários overloads, permitindo executar Stored Procedures e transacções. A seguir está um exemplo deste método para a execução de um Stored Procedure: DataGrid5.DataSource = SqlHelper.ExecuteReader( strConnTxt, CommandType.StoredProcedure, "getProductsByCategory", new SqlParameter( André Coutinho 11 Microsoft .NET – Application Blocks "@CategoryID", 1) ); DataGrid5.DataBind() Para executar um Stored Procedure e devolver um objecto SqlDataReader, chama-se outra variante do método ExecuteReader(). Para chamar um Stored Procedure, em vez de passar um comando SQL, passa-se o nome do Stored Procedure e um objecto SqlParameter. Veja-se mais uma vantagem em usar o DAAB. Neste exemplo, é devolvido um DataSet contendo o resultado da execução de um Stored Procedure (getProductsByCategory) que usa apenas um parâmetro (CategoryID). Começa-se por ver novamente o código necessário para fazer isto sem usar o DAAB. // Open a connection to Northwind SqlConnection objConn = new SqlConnection("Server=(local);Database=Northwind;Integrated Security=True;"); objConn.Open(); //Create the stored procedure command object SqlCommand objCmd = new SqlCommand("getProductsByCategory", objConn); objCmd.CommandType = CommandType.StoredProcedure; //create the parameter object for the stored procedure parameter objCmd.Parameters.Add("@CategoryID", SqlDbType.Int); objCmd.Parameters["@CategoryID"].Value = 1; //create our DataAdapter and DataSet objects SqlDataAdapter objDA = new SqlDataAdapter(objCmd); DataSet objDS = new DataSet("Category_Results"); //fill the dataset objDA.Fill(objDS); //databind the datagrid DataGrid1.DataSource = objDS; DataGrid1.DataBind(); //close connection objConn.Close(); A seguir apresenta-se o código necessário para fazer o mesmo mas utilizando o método ExecuteDataSet da classe SqlHelper: string strConn = "Server=(local);Database=Northwind;Integrated Security=True;"; DataSet objDS = SqlHelper.ExecuteDataset(strConn, CommandType.StoredProcedure, "getProductsByCategory", new SqlParameter("@CategoryID", 1) ); DataGrid2.DataSource = objDS; DataGrid2.DataBind(); O código foi reduzido de 12 para 4 linhas, como se pode ver. André Coutinho 12 Microsoft .NET – Application Blocks Usar o DAAB vai aumentar a rapidez com que uma aplicação é desenvolvida, porque encapsula o código necessário para executar Stored Procedures, comandos SQL, especificar parâmetros e devolver objectos SqlDataReader, DataSet e XmlReader. O facto de reduzir o código a escrever, leva a que apareçam menos erros, também. 3. Logging Application Block 3.1. Introdução 3.1.1. Enterprise Instrumentation Framework O Logging Application Block depende do Microsoft Enterprise Instrumentation Framework (EIF) para funcionar; assim, é portanto muito importante, compreender o EIF antes de passar ao Logging Application Block. O EIF permite simplificar o desenvolvimento de aplicações instrumentadas. Fornece gestão de eventos e serviços de tracing e diagnóstico para aplicações. Para exemplificar como o EIF simplifica o logging, considere-se o aparecimento de um evento numa aplicação. Esta acção usando o EIF requer apenas uma linha de código, como se pode verificar abaixo: EventSource.Application.Raise(adminMessageEvent); O EIF permite que os programadores façam uma instrumentação consistente das aplicações e expõe o funcionamento interno de uma aplicação, permitindo monitorizar e diagnosticar falhas nas aplicações, por exemplo. Também é possível configurar de que forma a informação publicada pelo EIF, será encaminhada para os diferentes logs existentes. As seguintes funcionalidades são fornecidas pelo EIF: • • • • Um modelo unificado de programação. Um esquema de eventos Windows Management Instrumentation (WMI), estruturado que promove o trabalho em conjunto das equipas de desenvolvimento, teste e operações, para o suporte à aplicação. Uma camada de configuração scriptable, que permite configurar como os eventos surgem numa aplicação e como são registados em log. Suporte para eventos e logging, usando WMI, Windows Event Log, e Windows Event Tracing. A figura seguinte mostra a arquitectura EIF: André Coutinho 13 Microsoft .NET – Application Blocks Figura 2: A arquitectura EIF A arquitectura EIF consiste de cinco elementos principais: • Event schema. O EIF fornece o esquema dos eventos que podem surgir numa aplicação. Este Event schema também descreve os dados que cada tipo de evento pode conter. • Event sources. Qualquer aplicação que interaja com o EIF vai necessitar de um ou mais objectos para lançar/gerar os eventos. Estes objectos são conhecidos como Event Sources. • Instrumentation API. O EIF tem um API unificado que providencia informação sobre a instrumentação das tecnologias existentes. Os eventos que vão surgindo na aplicação são passados ao Instrumentation API do EIF, que depois trata dos pormenores da comunicação com o resto da arquitectura EIF. Isto vai permitir a modificação de como os eventos são reportados sem alterar a aplicação que os gerou. • Event sinks. O EIF possui três repositórios de eventos, o WMI, o Windows Event Log, e o Windows Trace Log. Estes repositórios recebem os eventos gerados pela aplicação e garantem a persistência destes no log apropriado. • XML Configuration file. O EIF usa um ficheiro XML para configurar como os eventos são encaminhados para o Event sink correcto. Estes ficheiros XML podem ser usados para configurar o registo de eventos e da sua proveniência, além de configurar qual o Event sink que os vai receber. 3.2. Logging O Logging application block fornece então, uma extensão à arquitectura EIF, facilitando a implementação dos cenários mais comuns de logging. Estes cenários são: André Coutinho 14 Microsoft .NET – Application Blocks • • • • • • • • • Formatação da informação dos eventos. Configuração dos níveis de log. Logging assíncrono. Logging de confiança (reliable logging). Logging centralizado. Tracing para serviços Web. Informação sobre os eventos publicados. Metering/Medição para serviços Web. Usar o publisher da arquitectura EIF para o EMAB. Abaixo está a arquitectura do Logging Application Block, onde se vê como este application block extende o EIF. Figura 3: Arquitectura do Logging Application Block Seguidamente encontram-se exemplos de alguns dos cenários mais significativos de logging já apresentados. 3.2.1. Formatação da informação dos eventos O exemplo seguinte ilustra a formatação da informação de eventos. É possível ver duas formas de formatar estas informações. Na primeira especifica-se o Xslt (Extensible Stylesheet Language Transformations) utilizado na formatação, como parâmetro para o sink usado. Na segunda forma é também especificado o Xslt usado, mas como propriedade do evento a ser publicado. Em ambos os casos é importante referir que o parâmetro enableFormatting do ficheiro EnterpriseInstrumentation.config, terá de estar a true. André Coutinho 15 Microsoft .NET – Application Blocks Nos exemplos abaixo é usado o Windows Event Log para armazenar o evento formatado. using System; using System.IO; using Microsoft.EnterpriseInstrumentation; using Microsoft.ApplicationBlocks.Logging.Schema; namespace Microsoft.ApplicationBlocks.Logging.Samples.Formatting { public class FormattingSample { private String formatterName; /// <summary> /// Default constructor. /// </summary> public FormattingSample() { formatterName = "eventSpecifiedXslt"; } /// <summary> /// Method responsible for publishing event by specifying the /// Xslt on the event as a property. /// </summary> public void FormatWithEventXsltSpecified() { AuditMessageEvent publishedEvent = new AuditMessageEvent(); publishedEvent.FormatterName = formatterName; publishedEvent.EventPublishLogLevel = (int) LogLevel.Always; EventSource.Application.Raise(publishedEvent); } /// <summary> /// Method responsible for publishing the event information /// with the formatting done using the Xslt specified on the /// event sink. /// </summary> public void FormatWithSinkXsltSpecified() { AuditMessageEvent publishedEvent = new AuditMessageEvent(); publishedEvent.EventPublishLogLevel = (int) LogLevel.Always; EventSource.Application.Raise(publishedEvent); } /// /// /// /// /// André Coutinho <summary> The main entry point for the sample. </summary> <param name="args"> Arguments passed to the sample in this case 16 Microsoft .NET – Application Blocks /// there are no arguments. /// </param> public static void Main(string[] args) { FormattingSample sample = new FormattingSample(); sample.FormatWithEventXsltSpecified(); sample.FormatWithSinkXsltSpecified(); } } } 3.2.2. Configuração dos níveis de log Este exemplo ilustra como os níveis de log podem ser configurados para conrolar os eventos que são publicados, e os que não são. Há três conceitos importantes a descrever, de modo a compreender como os vários níveis de log podem ser usados. Este conceitos são os seguintes: • EventPublishLogLevel. Este propriedade (no evento a publicar) indica o nível de log a partir do qual o evento pode ser publicado. • ApplicationLogLevel. Esta propriedade (no evento a publicar) permite saber qual o nível de log para o qual a aplicação está configurada. Isto é obtido a partir do Application config ou do Web config. • Supported Log Levels. Os vários níveis de log possíveis: o Logging Level: None (nenhum). Descrição: Nenhum tipo de logging é feito neste nível. Valor (inteiro) associado: 0. o Logging Level: Always (sempre). Descrição: Este é o nível de logging por defeito para todos os eventos em que o logging level não foi especificado. Eventos de auditoria (audit) ou de metering, podem especificar o uso deste nível. Valor (inteiro) associado: 1. o Logging Level: Error (erro). Descrição: Neste nível todos os erros e excepções da aplicação são armazenados em log. Valor (inteiro) associado: 2. o Logging Level: Warning (aviso). Descrição: Este nível pode ser utilizado para troubleshooting, para identificação de problemas de funcionamento na aplicação. Valor (inteiro) associado: 3. o Logging Level: Informational (informativo). Descrição: As mensagens que fornecem informação adicional sobre o fluxo de controlo são armazenadas em log como informação adicional que poderá ser útil. Isto poder ser utilizado no arranque do sistema, por exemplo, apenas para André Coutinho 17 Microsoft .NET – Application Blocks mostrar informação possivelmente útil para confirmar que o sistema arrancou com as configurações correctas. Valor (inteiro) associado: 4. o Logging Level: Debug. Descrição: Neste nível todas as mensagens são armazenados em log. Este nível só deve ser utilizado para o debugging de aplicações. Valor (inteiro) associado: 5. Se a condição applicationLogLevel>=eventPublishLogLevel for satisfeita então o evento será publicado, caso contrário isso não ocorrerá. using System; using Microsoft.ApplicationBlocks.Logging.Schema; using Microsoft.EnterpriseInstrumentation; namespace Microsoft.ApplicationBlocks.Logging.Samples { class LogLevelSample { /// <summary> /// Default constructor. /// </summary> public LogLevelSample() { } /// <summary> /// Method responsible for Publishing Events. /// </summary> public void PublishEvents() { // This event has the Event Publish Log Level set at // Always and the Application Log Level is set at // Error. In this case the event will be published. AdminMessageEvent adminMessageEvent = new AdminMessageEvent(); adminMessageEvent.Message = "Admin Message Event to be published."; adminMessageEvent.EventPublishLogLevel = (int)LogLevel.Always; EventSource.Application.Raise(adminMessageEvent ); // This event has the Event Publish Log Level set at // Informational and the Application Log Level is set at // Error. In this case the event will not be published André Coutinho 18 Microsoft .NET – Application Blocks // as the event publish log level is higher that application // log level. // You can change the application log level parameter to // "informational" and this event will be published. AuditOperationEvent auditOperationEvent = new AuditOperationEvent(); auditOperationEvent.Operation = "ApproveOrder"; auditOperationEvent.Reason = "The user is authorized to approve order."; auditOperationEvent.EventPublishLogLevel = (int)LogLevel.Informational; EventSource.Application.Raise(auditOperationEve nt); // This event has the Event Publish Log Level set at // Error and the Application Log Level is set at // Error. In this case the event will be published as // the log levels match. ErrorMessageEvent errorMessageEvent = new ErrorMessageEvent(); errorMessageEvent.Severity = 2; errorMessageEvent.Message = "ErrorMessageEvent"; errorMessageEvent.EventPublishLogLevel = (int)LogLevel.Error; EventSource.Application.Raise(errorMessageEvent ); } /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main(string[] args) { LogLevelSample sample = new LogLevelSample(); sample.PublishEvents(); } } } 3.2.3. EnterpriseInstrumentation.config A seguir apresenta-se um pequeno excerto do ficheiro de configuração EnterpriseInstrumentation.config, onde é possível ver o parâmetro enableFormatting a true, usado na formatação da informação dos eventos. <eventSink name="logSink" description="Outputs events to the Windows Event Log." type="Microsoft.ApplicationBlocks.Logging.EventSinks.LogEventSi nk, Microsoft.ApplicationBlocks.Logging.EventSinks, André Coutinho 19 Microsoft .NET – Application Blocks Version=1.0.0.0, Culture=neutral, PublicKeyToken=3010be203f86630a"> <parameter name="entryTypeFieldName" value="EventLogEntryTypeID" /> <parameter name="defaultEntryType" value="Information" /> <parameter name="enableFormatting" value="false" /> <parameter name="machineName" value="." /> <parameter name="formatterName" value="logSinkXslt" /> </eventSink> André Coutinho 20 Microsoft .NET – Application Blocks 4. Exception Management Application Block 4.1. Introdução O Exception Management Application Block (EMAB) é um componente disponível para a Framework .NET e fornece uma extensão simples mas flexível para as excepções de logging. É possível também, a criação de componentes customizados para tratamento das excepções de logging. Este application block pode ser usado como mais um building block numa aplicação .NET. Utilizá-lo ajuda a reduzir a quantidade de código a escrever, testar e manter para o tratamento de erros. Como resultado a aplicação ficará mais robusta e mais facilmente depurável. Especificamente o EMAB ajuda a: • Gerir excepções de forma eficiente e consistente. • Isolar o código que gere as excepções do resto do código. • Gerir as excepções de log com um mínimo de código. Este application block ainda: • Garante que os detalhes das excepções são capturados com um grupo de informação, consistente, sobre o contexto. Esta consistência é fornecida por uma classe que assegura que todas as excepções fornecem apenas um nível mínimo de informação contextual. É possível criar classes customizadas apartir desta. • É flexível porque permite configurar as suas funcionalidades através de um simples ficheiro de configuração .NET XML. • Suporta um mecanismo extensível de lançamento de excepções. A Figura seguinte mostra o funcionamento do EMAB. Figura 4: Como funciona o EMAB André Coutinho 21 Microsoft .NET – Application Blocks 4.2. Classes do EMAB Neste sub capítulo são apresentadas as classes e interfaces mais importantes, deste application block. A Figura seguinte ilustra como estão organizados estes componentes dentro do EMAB. Figura 5: A arquitectura do EMAB 4.2.1. Classe BaseApplicationException O primeiro passo para implementar uma gestão de excepções consistente, está em assegurar que a informação sobre o contexto é capturada para cada excepção que surge numa determinada aplicação. Para facilitar esta tarefa existe a classe BaseApplicationException. Esta classe é a base para garantir que as excepções apenas fornecem o nível mínimo de informação contextual. É possível criar classes customizadas apartir desta. Existem dois problemas principais que esta classe resolve. Estes problemas são: • As excepções devem automaticamente capturar a informação mais importante sobre o ambiente. • Deverá existir uma estrutura lógica para a hierarquia de excepções de uma determinada aplicação. Os objectivos da classe BaseApplicationException são: • Ser uma classe base para a gestão de excepções, a partir da qual todas as classes de excepção da aplicação deverão derivar. • Conter apenas informação pertinente para todas as excepções de uma aplicação. André Coutinho 22 Microsoft .NET – Application Blocks • • Implementar as funcionalidades base de captura de informação contextual, que serão herdadas pelas classes customizadas. Deve ser possível aceder à classe remotamente. Os métodos mais importantes desta classe são os seguintes: • GetObjectData. Preenche um objecto SerializationInfo com os dados da excepção necessários para a serialization. public override void GetObjectData( SerializationInfo info, StreamingContext context); • InitializeEnvironmentInformation. Função de inicialização que reune informação de forma segura. private void InitializeEnvironmentInformation() 4.2.2. Classe ExceptionManagerSectionHandler Esta classe é utilizada para devolver os parâmetros de gestão de excepções, do ficheiro de configuração da aplicação. 4.2.3. Classe ExceptionManager Para gerir o logging e o tratamento de excepções, consistemente, o EMAB inclui esta classe. A classe ExceptionManager foi criada para solucionar as seguintes questões: • Os programadores necessitam de uma forma mais fácil para gerir excepções imprevistas, com um mínimo de código. • A solução deve ser flexível de modo a que permita modificações “on the fly”, sem ser preciso recodificar, recompilar ou parar a aplicação. • A solução deve ser extensível. • A solução deve gerir as excepções que possam ocorrer durante o processo de publicação de uma excepção. Os objectivos da classe ExceptionManager são: • O componente deve necessitar de um mínimo de código criado pelo programador para se integrar numa aplicação. • O comportamento deve ser baseado em parâmetros de configuração que possam ser modificados sem recodificação, recompilação, e no caso de aplicações ASP.NET, sem recomeçar a aplicação. • As excepções internas lançadas por custom publishers devem ser capturadas e registadas em log. • Providenciar uma implementação própria dos objectos XmlSerializer. Os métodos mais importantes desta classe são os seguintes: • Publish. Publica uma excepção. • SerizalizeToXml. Passa um objecto Exception para XML. André Coutinho 23 Microsoft .NET – Application Blocks 4.2.4. Classe ExceptionManagerInstaller Esta classe é usada quando o EMAB é instalado e cria os dois Event Sources usados pelo publisher por defeito quando é necessário escrever no Windows Event Log. 4.2.5. Classe Default Publisher Esta classe implementa a interface IExceptionPublisher e fornece funcionalidades de escrita para o registo de detalhes de excepções no Windows Event Log. Na ausência de classes customizadas, baseadas nesta, é esta classe a usada por defeito. É possível configurar o publisher por defeito, criando um elemento <publisher> no ficheiro de configuração. Isto permite definir qual o Event Source e o nome do log a usar. A seguir são apresentadas as interfaces IExceptionPublisher e IExceptionXmlPublisher. 4.2.6. A interface IExceptionPublisher Esta interface possui um único método, chamado Publish (apresentado anteriormente), que permite que os publishers recebam detalhes sobre as excepções a publicar (publishing). public interface IExceptionPublisher 4.2.6.1. Métodos da interface IExceptionPublisher • Publish. Fornece detalhes sobre as excepções relativas às excepções a serem publicadas. void Publish( Exception exception, NameValueCollection additionalInfo, NameValueCollection configSettings); O exemplo seguinte mostra uma implementação, simples, desta interface. using using using using using System; Microsoft.ApplicationBlocks.ExceptionManagement; System.Text; System.IO; System.Collections.Specialized; namespace CustomExceptionPublishers { public class CustomPublisher : IExceptionPublisher { private string m_LogName = @"C:\TestExceptionLog.txt"; void IExceptionPublisher.Publish( Exception exception, NameValueCollection additionalInfo, NameValueCollection configSettings) André Coutinho 24 Microsoft .NET – Application Blocks { // Load Config values if they are provided. if (configSettings != null) { if (configSettings["fileName"] != null && configSettings["fileName"].Length > 0) m_LogName = configSettings["fileName"]; } // Create StringBuilder to maintain publishing information. StringBuilder strInfo = new StringBuilder(); // Record the contents of the additionalInfo collection. if (additionalInfo != null) { // Record General information. strInfo.AppendFormat("------- Exception Log Entry ------ {0}", Environment.NewLine); strInfo.AppendFormat("{0}General Information{0}", Environment.NewLine); strInfo.AppendFormat("{0}Additional Info:", Environment.NewLine); foreach (string i in additionalInfo) { strInfo.AppendFormat("{0}{1}: {2}", Environment.NewLine, i, additionalInfo.Get(i)); } } // Append the exception text if (exception != null) { strInfo.AppendFormat("{0}Exception Information {1}", Environment.NewLine, exception.Message.ToString()); } else { strInfo.AppendFormat("{0}{0}No Exception.{0}", Environment.NewLine); } // Write the entry to the event log. FileStream fs = File.Open(m_LogName,FileMode.Append,FileAccess.Write); StreamWriter sw = new StreamWriter(fs); sw.Write(strInfo.ToString()); sw.Close(); fs.Close(); } } } 4.2.7. A interface IExceptionXmlPublisher André Coutinho 25 Microsoft .NET – Application Blocks A interface IExceptionXmlPublisher tal como o IExceptionPublisher também só tem um método Publish, neste caso para receber documentos XML contendo os detalhes das excepções. public interface IExceptionXmlPublisher 4.2.7.1. Métodos de IExceptionXmlPublisher • Publish. Fornece documentos XML que contêm os detalhes das excepções a serem publicadas. void Publish( XmlDocument exceptionInfo, NameValueCollection configSettings); O exemplo seguinte mostra uma possível implementação desta interface. using using using using using using System; Microsoft.ApplicationBlocks.ExceptionManagement; System.Xml; System.Text; System.IO; System.Collections.Specialized; namespace CustomXMLExceptionPublishers { public class CustomXMLPublisher : IExceptionXmlPublisher { void IExceptionXmlPublisher.Publish(XmlDocument ExceptionInfo, NameValueCollection ConfigSettings) { string filename = @"C:\TestExceptionLog.xml"; if (ConfigSettings != null) { if (ConfigSettings["fileName"] != null && ConfigSettings["fileName"].Length > 0) { filename = ConfigSettings["fileName"]; } } XmlDocument logDoc = new XmlDocument(); if (File.Exists(filename) == false) // create the log file { FileStream f = File.Open(filename, FileMode.Create, FileAccess.Write); StreamWriter sw = new StreamWriter(f); sw.Write("<?xml version='1.0'?><ExceptionLog><ExceptionLog>"); sw.Close(); f.Close(); } // Add this exception to the existing XML document logDoc.Load(filename); André Coutinho 26 Microsoft .NET – Application Blocks XmlNode xNode = logDoc.ImportNode(ExceptionInfo.DocumentElement, true); logDoc.DocumentElement.AppendChild(xNode); File.Delete(filename); logDoc.Save(filename); } } } André Coutinho 27 Microsoft .NET – Application Blocks 5. Configuration Management Application Block 5.1. Introdução Todas as aplicações requerem que de alguma forma se configure a informação que adquirem, gerem e armazenam. Onde e como armazenar a informação de configuração de uma aplicação, sempre foi um problema para os programadores. As soluções mais comuns são: • Ficheiros de configuração XML ou .ini (windows) • Os registos do window • Base de dados (por exemplo Microsoft SQL Server) Cada uma destas alternativas tem pontos fracos e pontos fortes, nenhuma solução é perfeita para um determinada situação. Mesmo numa determinada aplicação, poderá ser preciso adoptar mais do que uma forma de configuração da informação que a aplicação armazena. A necessidade de uma aplicação correr em ambientes diferentes também poderá fazer com que seja preciso implementar múltiplas soluções de configuração. Outros factores são a capacidade de escrever (não só de ler) dados de configuração, de segurança e integridade, e o impacto na performance da aplicação que a solução de configuração provoca. O Configuration Management application block (CMAB) atende a estes factores e proporciona uma solução simples e flexível para a configuração de dados e aplicações. 5.2. Elementos constituintes do CMAB A Figura que se encontra abaixo mostra a estrutura do CMAB, a relação entre os elementos chaves que serão descritos brevemente e os pontos de interacção com entidades externas. Figura 6: Relação entre os elementos do CMAB (arquitectura) André Coutinho 28 Microsoft .NET – Application Blocks Os elementos chave ou principais do CMAB são seis: • Configuration sections. Uma configuration section é a principal unidade de dados processadas pelo CMAB. Representa um bloco de dados de configuração que é lido ou escrito numa única operação. O CMAB identifica cada uma destas secções usando um nome. É necessário declarar cada configuration section usada pela aplicação, nos parâmetros de configuração do CMAB. A declaração de um configuration section define qual CSH (Configuration Section Handler), CSP (Configuration Section Providers) e DPP (Data Protection Provider), o CMAB usará quando ler ou escrever essa mesma configuration section, e especifica se o caching está activado para esse configuration section. • A classe ConfigurationManager. Esta classe é o elemento central do CMAB, e a única classe necessária para aceder a configuration sections. A classe ConfigurationManager encapsula a complexidade associada com a escrita/leitura de dados de configuração, e é responsável por: o Fazer o parsing do ficheiro de configuração da aplicação e instanciar as CSPs e DPPs para suportar cada configuration section declarada. o Fornecer a interface através da qual o código da aplicação vai ler e escrever dados da configuration section. o Gerir o fluxo interno de dados entre a aplicação e o CSP, incluindo a instanciação do CSH correcto. o Estabelecimento e gestão dos serviços de caching. • Serviços de caching de dados. O CMAB providencia caching de dados baseado em memória, para reduzir o overhead associado às leituras repetidas de dados de configuração do sítio onde estão armazenados, especialmente se estão distribuídos numa rede. Se o caching for activado para uma configuration section, sempre que a classe ConfigurationManager precisar de ler a configuration section, tentará primeiro verificar se a configuration section está em cache. • Configuration Section Handlers (CSH). Há dois tipos de CSH. CSH de leitura e CSH de leitura/escrita. A CSH de leitura implementa a interface IConfigurationSectionHandler e transforma um nó XmlNode na sua estrutura correcta. A CSH de leitura/escrita implementa o interface IConfigurationSectionHandlerWriter (que extende o IConfigurationSectionHandler) e transforma os dados em ambas as direcções. • Configuration Storage Provider (CSP). Um CSP é usado pelo CMAB para ler/escrever dados de configuração da aplicação de/para um armazém de dados específico, por exemplo, Microsoft SQL Server, ou ficheiros XML. Existem dois tipos de CSP. Um só de leitura e outro de leitura/escrita. O primeiro implementa a interface IConfigurationStorageReader e só é capaz de ler uma configuration section apartir do armazém de dados. O segundo implementa a interface IConfigurationStorageWriter (que extende a IConfigurationStorageReader) e pode ler e escrever configuration sections. André Coutinho 29 Microsoft .NET – Application Blocks • Data Protection Provider (DPP). Um DPP fornece serviços de encriptação e MAC (Message Authentication Code), que podem ser usados para melhorar a segurança e integridade das configuration sections guardadas. Se se configurar um CSP para usar um DPP, o CSP chama o DPP para fazer a encriptação e assinar os dados antes de os armazenar e desencripta os dados antes de os passar para a aplicação. O DPP implementa a interface IDataProtection. 5.3. Objectivos Os pontos seguintes são os objectivos do CMAB: • O CMAB deve fornecer um interface standard para a leitura e escrita de dados de configuração, fácil de usar e independente do armazenamento físico dos dados. • Este application block tem que permitir que os programadores trabalhem com dados de configuração usando a estrutura de dados mais apropriadas. • Deve ser fácilmente configurável usando ficheiros de configuração standard do .NET. • O CMAB deve suportar caching opcional dos dados de configuração independentemente do armazenamento dos dados e ser transparente para a aplicação que está a usar o CMAB. • Suportar encriptação opcional dos dados de configuração da aplicação para melhorar a segurança dos dados. O uso de encriptação deve ser controlado pelos parâmetros de configuração, independentemente do armazenamento dos dados, e ser transparente para a aplicação que está a usar o CMAB. • Suportar a assinatura opcional dos dados de configuração para melhorar a integridade dos dados. O uso das assinaturas deve ser controlado pelos parâmetros de configuração, e ser transparente para a aplicação que está a usar o CMAB. • O CMAB deve fornecer funcionalidades que suportem a maioria das situações de dados de configuração. Isto inclui suporte dos pares nome/valor, caching de dados baseado em memória, os armazéns de dados mais comuns,e protecção de dados. • Deve também ser extensível, permitindo aos programadores, adicionar suporte a diferentes armazéns de dados, diferentes métodos de encriptação e algoritmos de assinaturas de dados. 5.4. Vantagens O CMAB traz as seguintes vantagens: Simplicidade O CMAB apresenta uma interface simples e consistente que suporta a leitura e escrita de informação de configuração para a aplicação. Usando o CMAB é muito simples devolver por exemplo a string de conexão à base de dados, que temos configurada, ou ainda escrever uma nova configuração. André Coutinho 30 Microsoft .NET – Application Blocks string conString = (string)ConfigurationManager.Items["connection_string "]; Escrever configurações é também muito fácil, como se vê abaixo. ConfigurationManager.Items["connection_string"] = "Some String"; Um modelos de dados flexível O uso dos pares nome/valor é uma metodologia comum para armazenar dados de configuração. No entanto o CMAB não usa este método. É possível com este application block ler/escrever parâmetros de configuração, ou grupos de elementos de configuração chamados configuration sections. O CMAB é também extensível e pode suportar dados de configuração de qualquer tipo, fazendo uso de um CSH (Configuration Section Handler) customizado. Esta flexibilidade permite escolher qual a melhor representação para os dados de configuração, e também usar os métodos standard do CMAB para armazenar estes dados. Independência do local de armazenamento No CMAB a capacidade de ler/escrever dados de configuração é transparente em relação à forma como estes estão armazenados fisicamente. Um CSP (Configuration Storage Provider) implementa as funcionalidades necessárias para aceder ao meio físico de armazenamento, e o mapeamento entre as configuration sections e o CSP é definido no ficheiro de configuração da aplicação. Uma vez que usando o CMAB estão a ser utilizados interfaces standard para ler e escrever dados de configuração independentemente do local e forma de armazenamento usados, é possível mudar o local destes dados de configuração sem alterar do código já escrito. O CMAB inclui também CSPs que suportam armazenamento de dados de configuração em ficheiros XML (incluindo os ficheiros .config standard do .NET), Windows Registry e SQL Server. É possível extender o CMAB com CSPs customizados para suportar outros tipos de armazenamento de dados. Integridade e segurança dos dados Os dados de configuração de uma aplicação incluem muitas vezes informações sensíveis, tais como strings de conexão e localizações de serviços. O CMAB providencia serviços de protecção de dados sob a forma de um DPP (Data Protection Provider) para minimizar os acessos não autorizados a dados de configuração sensíveis. Um DPP fornece o MAC (Message Authentication Code) e funcionalidades de encriptação através de um interface bem definido que pode ser configurado para uso conjunto com um CSP. Este application block fornece duas implementações do DPP. Ambas geram, MACs SHA1 e encriptam os dados usando o algoritmo de encriptação 3-DES. A principal diferença entre as duas implementações, é que uma usa DPAPI André Coutinho 31 Microsoft .NET – Application Blocks (Windows Data Protection API) para simplificar a gestão de chaves, enquanto que a outra requer que a gestão das chaves encriptadas seja feita usando classes para o efeito. É possível extender o CMAB com implementações customizadas do DPP para suportar outros MACs e modos de encriptação. Performance O impacto causado pelo acesso a dados de configuração, na performance da aplicação, depende do local onde os dados estão armazenados e do método de acesso. Para assegurar que a descida de performance é mínima, o CMAB tem uma opção que permite usar serviços de caching baseados em memória que podem ser activados para qualquer CSP. O serviço de caching suporta parâmetros de Time-to-live para os dados em cache e quando indicado pelo CSP suporta também a capacidade de refrescar os dados em cache, se estes foram alterados. Extensibilidade O CMAB é extensível. As suas interfaces permitem desenvolver CSPs, DPPs e CSHs customizados. Como foi dito anteriormente também é possível mudar a forma ou tipo de armazenamento físico dos dados de configuração, sendo isto transparente para o CMAB, bastando para tal alterar os ficheiros de configuração da aplicação. 5.5. Porquê a criação do CMAB O uso de ficheiros de configuração .NET apenas fornece acesso de leitura aos dados de configuração armazenados em ficheiros XML, o que em muitas aplicações é muito restritivo. Muitas vezes os programadores têm de implantar soluções específicas para o armazenamento de dados de configuração, para cada aplicação. Uma vez que são consideradas secundárias, estas soluções são implementadas com alguma negligência, e funcionalidades como a segurança e integridade dos dados são deixadas para segundo plano. Além disto, estas soluções criam dependências entre a aplicação e o armazenamento dos dados, afectando a instalação da aplicação. O código seguinte exemplifica como guardar a configuration section por defeito. Hashtable configData; // Write the default configuration section (must be Hashtablebased) ConfigurationManager.Write(configData); O exemplo abaixo mostra como se podem ler e escrever items individualmente na Configuration Section por defeito. // Write a data item to the default configuration section ConfigurationManager.Items["Size"] = "large"; // Read a data item from the default configuration section André Coutinho 32 Microsoft .NET – Application Blocks string size = (string)ConfigurationManager.Items["Size"]; 5.6. Criação de um CSP de leitura Para um CSP (Configuration Storage Provider) de leitura, deve ser criada uma classe que implemente a interface IConfigurationStorageReader, como se vê abaixo. [ComVisible(false)] public interface IConfigurationStorageReader { void Init( string sectionName, ListDictionary configStorageParameters, IDataProtection dataProtection ); XmlNode Read(); event ConfigurationChanged ConfigChanges; bool Initialized{ get; } } 5.7. Criação de um CSP de leitura/escrita Se houver necessidade de usar este application block para fazer administração, será necessário criar um CSP customizado. Se já existir um CSP só de leitura (ver exemplo anterior), a forma mais fácil de criar a versão de leitura/escrita será criar uma segunda classe que acrescente estas funcionalidades às de leitura. Todas as implementações de CSPs fornecidas com o CMAB possuem as funcionalidades de leitura/escrita. A interface usada é a IConfigurationStorageWriter que extende a IConfigurationStorageReader. [ComVisible(false)] public interface IConfigurationStorageWriter : IConfigurationStorageReader { void Write( XmlNode value ); } 5.8. Criação de um DPP Existem duas abordagens para a implementação de um DPP. É possível inicializar e instanciar tudo com o método Init. Isto significa não só a criação dos objectos para encriptação e hashing, como também a obtenção das várias chave e armazenamento das mesmas. Esta abordagem não é segura, uma vez que as chaves (secretas) estão na memória durante o tempo de vida do CMAB. A outra abordagem passa pela retenção dos detalhes do que será criado e onde obter a chaves. No entanto, a obtenção das chaves e instanciação de objectos criptográficos é feita de cada vez que um dos métodos Encrypt, Decrypt ou ComputeHash, são chamados. Esta abordagem é mais segura mas diminui a performance. O CMAB tem implementações destas duas abordagens, a partir das quais é possível criar implementações customizadas, conforme as necessidades. André Coutinho 33 Microsoft .NET – Application Blocks [ComVisible(false)] public interface IDataProtection : IDisposable { void Init( ListDictionary dataProtectionParameters ); byte[] Encrypt( byte[] plainText ); byte[] Decrypt( byte[] cipherText ); byte[] ComputeHash( byte[] plainText ); } André Coutinho 34 Microsoft .NET – Application Blocks 6. Updater Application Block 6.1. Introdução Em organizações de média a grande dimensão, é comum necessitar das actualizações da última versão de uma determinada aplicação, quer seja dos seus executáveis, ou bibliotecas, quer seja de outros ficheiros (ex., configuração). Idealmente, os administradores da rede preferem instalar os ficheiros de update de uma aplicação apenas uma vez, para um servidor central, e seguidamente ter os ficheiros replicados automaticamente para cada desktop existente na rede. O UAB é um componente disponível para a Framework .NET que fornece uma solução do tipo “pull model” que automaticamente faz o download dos updates de determinada aplicação a partir de um sítio centralizado. Este application block foi desenhado para organizações que necessitam de aplicações que combinem as funcionalidades dos Windows Forms, com a flexibilidade e facilidade de gestão das aplicações Web, e que não possuam as infraestruturas necessárias à implementação de uma solução do tipo “push model”, como o MS Systems Management Server. Usando o UAB para fazer o download de updates para as aplicações, é possível ultrapassar a limitação imposta pelos downloads de updates para as aplicações baseadas em Windows Forms. Para além disso, utilizando o UAB também se mantém o controlo e segurança sobre o processo de update da aplicação. Figura 7: Mantendo aplicações actualizada com o UAB O UAB é um componente .NET usado para detectar, validar, e fazer download de updates para aplicações instaladas num sítio centralizado. Fazendo uso do UAB, é possível manter aplicações actualizadas com o mínimo (ou até mesmo sem) intervenção do utilizador. Ainda é possível extender o uso do UAB e criar classes customizadas para fazer download e validar ficheiros, para executar André Coutinho 35 Microsoft .NET – Application Blocks tarefas de configuração após instalação, e também para controlar o processo de update. Especificamente, o UAB permite: • Implementar uma solução de update do tipo “pull” para aplicações .NET. • Usar técnicas de validação criptográfica para verificar a autenticidade dos updates das aplicações, antes de estes serem aplicados. • Executar tarefas de configuração pós-instalação, sem intervenção do utilizador. • Escrever aplicações que fazem update automaticamente. 6.2. Arquitectura do UAB Figura 8: Arquitectura do UAB A Figura acima mostra a arquitectura do UAB: 1. Geração de um ficheiro de manifest descrevendo os updates disponíveis para cada aplicação 2. Uma aplicação controller é usada para começar e parar o update da aplicação 3. O updater da aplicação inicia periodicamente o processo de update. 4. O updater da aplicação carrega o validator apropriado e valida os ficheiros descarregados 5. Se um post processor foi especificado no ficheiro manifest, o updater da aplicação carrega-o e executa-o Nos parágrafos seguintes, explica-se um pouco mais detalhe cada um dos passos anteriores. André Coutinho 36 Microsoft .NET – Application Blocks 1 – No servidor, usando a aplicação Manifest Utility, gera-se um ficheiro manifest para o update de cada aplicação. Faz uma lista de todos os ficheiros incluídos no update, uma assinatura de validade para cada ficheiro (incluindo-se a si próprio) e opcionalmente um post processor para execução no cliente para depois do download e validação dos ficheiros. 2 – uma aplicação controller é usada para começar e parar o update da aplicação. O ficheiro de configuração da aplicação associado ao controller é utilizado para determinar três parâmetros fundamentais de configuração para o processo de update: • As aplicações a serem actualizadas, incluindo o local do ficheiro de configuração e o ficheiro de manifest a ser usado para determinar a última versão da aplicação no cliente e no servidor. Existem três fontes de informação indispensáveis ao updater de uma aplicação: o O ficheiro de configuração do updater da aplicação. É um ficheiro que contém informação sobre o intervalo de polling, o local de log, downloader e validator a serem usados pelo updater, também inclui detalhes sobre a aplicação a ser actualizada. Para cada aplicação o ficheiro inclui parâmetros para o cliente e para o servidor. o Ficheiros de configuração do cliente. Estes ficheiros incluem informação necessária para identificar a versão correntemente instalada e o local do executável a ser lançado quando um utilizador lança o application launcher. o Manisfests. Cada vez que um update para uma determinada aplicação é posto num servidor, deve ser criado um ficheiro manifest para esse mesmo update. Este ficheiro contém informação sobre a versão do update, sobre os ficheiros incluídos no update, e sobre qualquer post processor a ser executado. Os ficheiros manifest também incluem assinaturas de validação para cada ficheiro e para os próprios ficheiros manifest, permitindo que o updater da aplicação valide o update e implemente protecção contra updates não autorizados ou ataques do tipo “man in the middle”. André Coutinho 37 Microsoft .NET – Application Blocks Figura 9: Mapeamento do ficheiro de configuração do updater da aplicação (application updater) • O ficheiro de configuração do updater da aplicação é mapeado para a classe UpdaterConfiguration, cada ficheiro de configuração de cada cliente é mapeado para a classe ClientApplicationInfo, e cada manifest é mapeado para a classe ServerApplicationInfo tal como se vê na figura. O downloader a utilizar quando se copiam ficheiros. Estes componentes implementam o interface IDownloader definido no UAB. O UAB inclui um downloader que o usa o BITS (Background Intelligent Transfer Service) para copiar ficheiros. Adicionalmente é possível desenvolver downloaders customizados. A arquitectura do downloader pode ser vista abaixo. Figura 10: Arquitectura de validação do lado do cliente A Figura mostra o seguinte processo: o A classe ApplicationUpdateManager usa a propriedade Instance da classe UpdaterConfiguration para ler a secção do <validator> do ficheiro de configuração do updater da aplicação. Esta secção indica o validator a usar. o A classe ValidatorFactory é usada para instanciar o validator especificado, que deve derivar da interface IValidator. Esta classe também passa a secção de configuração do <validator> ao método Init do validator. Esta secção tem a chave a usar pelo validator para validar assinaturas. o Finalmente a classe ApplicationUpdateManager instancia uma classe DownloaderManager para cada aplicação a ser actualizada e passa-a ao validator. A classe DownloaderManager usa então periodicamente, o validator para validar as assinaturas dos manifest e outros ficheiros copiados. André Coutinho 38 Microsoft .NET – Application Blocks O UAB ainda inclui uma classe chamada BITSDownloader, que usa o BITS para copiar ficheiros. É possível utilizar esta classe our criar uma customizada que implemente a interface IDownloader. • O validator que será usado para validar os ficheiros descarregados (downloaded). Estes componentes implementam a interface IValidator definida no UAB. Este application block inclui dois validators, um baseado em chaves simétricas e outro baseado em chaves privadas e públicas. Também podem ser desenvolvidos validators customizados. Abaixo está uma imagem que ilustra a arquitectura de validação, mas do lado do servidor. Figura 11: Arquitectura de validação do lado do servidor Na Figura da arquitectura da validação vemos os seguintes aspectos: o A ManifestUtility instancia o validator especificado pelo utilizador. Todos os validators derivam da interface IValidator. O utilizador também especifica uma chave, que é passada ao validator. o O validator gera uma assinatura para cada ficheiro no directório de update de determinada aplicação, no servidor. Os valores gerados são escritos no ficheiro manifest. o Finalmente, o validator calcula a assinatura para o ficheiro de manifest. 3 – O updater da aplicação inicia periodicamente o processo de update. Quando isto ocorre, o updater usa o downloader especificado, para copiar o ficheiro André Coutinho 39 Microsoft .NET – Application Blocks manifest para cada aplicação especificada no ficheiro de configuração, para o cliente. Se existem updates disponíveis, o downloader copia os ficheiros para um directório temporário no cliente. 4 – O updater da aplicação carrega o validator apropriado e valida os ficheiros descarregados. Se forem válidos, são então copiados para o directório correcto da aplicação. 5 – Se um post processor foi especificado no ficheiro manifest, o updater da aplicação carrega-o e executa-o. Esta arquitectura incluída no UAB permite aos programadores criar post processors para serem executados quando um update é feito com sucesso. Os post processors são classes .NET que implementam a interface IPostProcessor e são utilizados tarefas de pós instalação, tais como escrever dados no registo, criar filas de mensagens, ou qualquer outra tarefa difícil de realizar pela simples cópia dos ficheiros da aplicação. A arquitectura de post processing do UAB está ilustrada na Figura seguinte. Figura 12: Arquitectura de post processing do UAB 6.2.1. Classes do UAB 6.2.1.1. Classe ServerApplicationInfo Esta classe é usada para entregar a informação dos ficheiros manifest ao updater de uma aplicação num formato Object Oriented (OO). 6.2.1.1.1. Objectivos • A classe ServerApplicationInfo deve expor os settings de uma aplicação no ficheiro manifest. André Coutinho 40 Microsoft .NET – Application Blocks • Uma instância desta classe deverá ser passada a todos os eventos expostos pela classe ApplicationUpdaterManager à excepção do evento DownloadCompleted. A classe ServerApplicationInfo é mapeada para o ficheiro manifest como se vê abaixo. Figura 13: Mapeamento da classe ServerApplicationInfo num ficheiro manifest 6.2.2. Os ficheiros manifest Cada vez que um update para uma determinada aplicação é colocado num servidor, um ficheiro manifest deve ser criado para o update. Este ficheiro inclui informação sobre a versão do update, os ficheiros incluídos no update e qualquer post processor a executar depois de a operação de update estar concluída. Também inclui assinatura de validação para cada ficheiro e para o próprio ficheiro manifest, permitindo a validação do update e protecção contra updates não autorizados, ou ataques do tipo “man in the middle”. Os ficheiros manifest são simples ficheiros XML. É possível criá-los manualmente, ou usando o Manifest Utility. A seguir encontra-se um exemplo de um ficheiro Manifest. <ServerApplicationInfo signature="16d~#d9&9"> <availableVersion>2.0.0.0</availableVersion> <updateLocation>http://appserver/appupdates/2.0.0.0</updateLocat ion> <files> <file name="post\Microsoft.ApplicationBlocks.ApplicationUpdater.Interf aces.dll" signature="25kdsj*7" /> <file name="post\MyPostProcessor.dll" signature="25sd][{*h" /> <file name="data\App1Data.mdb" signature="6ydh8i]+8#" <file name="App1.exe" signature="25+)8j&h" /> <file name="App1.exe.config" signature="19huio%g" /> </files> <postProcessor type="MyPostProcessor.MyAppProcessor" assembly="MyPostProcessor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="post\MyPostProcessor.dll" /> </ServerApplicationInfo> André Coutinho 41 Microsoft .NET – Application Blocks 7. Caching Application Block 7.1. Introdução O Caching Application Block (CAB) é um componente disponível para a Framework .NET, que permite armazenar facilmente dados provenientes de várias fontes, como service providers, por exemplo. Fornece também um ambiente RAD (Rapid Application Development) que auxilia a construção e desenvolvimento de aplicações escaláveis usando o Visual Studio .NET e é fornecido como uma biblioteca de classes .NET. 7.2. Funcionamento Um cliente ou Service Agent (explicado em seguida) faz um pedido ao Caching Framework, pedindo dados em cache. Se os dados pedidos estão em cache, são enviados ao cliente. Se pelo contrário, os dados não estão armazenados, um Service Agente pode ser usado para obter os dados pedidos. O Cache Manager adiciona metadados aos dados em cache, de modo que o Framework possa monitorizar os dados (saber o último acesso aos dados, ou a “idade” dos dados, por exemplo). Os metadados permitem estabelecer condições para libertação de recursos e para determinar, por exemplo, qual a data de expiração dos dados. Os dados podem ser armazenados em vários formatos, incluindo bases de dados Microsoft SQL Server 2000, ficheiros mapeados em memória, ou objectos. O CAB também inclui classes para caching, exploração, expiração e protecção de dados. 7.3. O que são os Service Agents Os Service Agents são objectos usados para obter dados dos Service providers. Na sua forma mais básica um Service Agent é criado declarando: • Uma classe a usar para a criação do Service Agent, que implementa a interface IServiceAgent. • Um método Execute, com um PersistCallback e parâmetros de input. • Um construtor. Depois de criar um Service Agent, é necessário ServiceAgentMaster e ServiceAgentMap na base de dados. adicioná-lo ao O método PersistCallback é usado para devolver resultados. Os parâmetros de input a passar ao Service Agent são aqueles que o utilizador achar necessários. Quando o método Execute do Service Agent é chamado, os parâmetros de input são passados como argumento. É este o método responsável por contactar os Service providers. Aquando da obtenção de resultados, o Service Agent não os passa directamente para o cliente. Em vez disso, os resultados são devolvidos ao Framework. O cliente deverá pedir os resultados usando o requestID apropriado. André Coutinho 42 Microsoft .NET – Application Blocks 7.4. A interface IServiceAgent Esta interface é utilizada pelas classes dos Service Agents criados. Tem um único método, o método Execute. public interface IServiceAgent 7.4.1. Métodos da interface IServiceAgent Execute. Executa um Service Agent. 7.5. O método PersistCallback Este método é usado para obter os resultados da execução de um Service Agent. public delegate void PersistCallBack( ServiceAgentResult result, bool isCompleted); 7.6. Término de um Service Agent Um Service Agent é marcado como Failed na base de dados se: • O FinishTime (tempo de vida, ou Time-to-live) é inferior à data actual. • O RetryCount (número de tentativas) é inferior a zero. • Um pedido é cancelado. 7.7. Usando o CAB com outros application blocks O CAB incorpora o EMAB (Exception Management Application Block) para tratamento de excepções, mas é também possível usar técnicas de tratamento de excepções customizadas. O DAAB (Data Access Application Block) também está incluído para minimizar o código de acesso a dados a escrever. O código do CAB pode ser incluído num qualquer projecto e costumizado de acordo com as necessidades do programador, também é possível usar este application block sozinho, como uma biblioteca de classes. Também foi desenhado para cooperar com o AIAB (Asynchronous Invocation Information Application Block) e o AAB (Aggregation Application Block). O CAB é composto por várias assemblies, que fornecem flexibilidade, extensibilidade e facilidade para o armazenamento de dados. Incorporando o CAB numa aplicação distribuída, é possível melhorar a performance dessa aplicação, além da sua escalabilidade e disponibilidade. A tabela seguinte mostra as diferentes assemblies do CAB. Assembly Descrição Microsoft.ApplicationBlocks.Cache Contém as classes que fornecem as funcionalidades de caching. Microsoft.ApplicationBlocks.Data André Coutinho Contém as classes que fornecem 43 Microsoft .NET – Application Blocks o acesso a dados, a bases de dados SQL Server 2000. Microsoft.ApplicationBlocks.ExceptionManagement Contém classes que fornecem tratamento de excepções. Microsoft.ApplicationBlocks.ExceptionMangement.Interfaces Contém interfaces para tratamento de excepções. Microsoft.ApplicationBlocks.MemoryMappedFile Contém classes trabalhar para com criar e ficheiros mapeados em memória. Tabela 1: As diferentes assemblies do CAB 7.8. Componentes primários do CAB Este application block possui três componentes primários que cooperam mutuamente: 7.8.1. Classe CacheManager A classe CacheManager fornece a interface de acesso à própria cache. Esta classe fornece funcionalidades de adição, recuperação e remoção de items e metadados de e para a cache. public sealed class CacheManager 7.8.1.1. Métodos da classe CacheManager • Add. Adiciona um item à cache. public void Add ( string key, object keyData ) • Flush. Limpa a cache. public void Flush() • GetCacheManager. Devolve a instância única do objecto CacheManager. public static CacheManager GetCacheManager() • GetData. Devolve o item em cache, correspondente à chave especificada. public object GetData ( string key ) • GetItem. Devolve o item, e o seus metadados, em cache, correspondente à chave especificada. André Coutinho 44 Microsoft .NET – Application Blocks • public CacheItem GetItem ( string key ) Remove. Remove o item da cache, correspondente à chave especificada. public void Remove ( string key ) 7.8.1.2. Propriedades da classe CacheManager • CacheStorage. Obtém a interface ICacheStorage. public static ICacheStorage CacheStorage {get;} • Item. Obtém o item em cache, correspondente à chave especificada. public object this[ string key ] {get;} Esta classe é uma classe sealed, portanto não pode ser herdada. 7.8.2.A classe CacheService A classe CacheService é responsável pela gestão dos mteadados da cache. O cache service pode ser instalado no mesmo AppDomain do CacheManager ou não, dependendo do espaço necessário para armazenamento e do seu alcance. public sealed class CacheService : MarshalByRefObject 7.8.2.1. Métodos da classe CacheService • Add. Adiciona um item com os metadados especificados ao cache service. public void Add ( string keyVal, ICacheItemExpiration[] expirations, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback ) • BeginAdd. Inicia um pedido assíncrono para adicionar um item ao cache service. public void BeginAdd ( string key, ICacheItemExpiration[] expirations, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback, AsyncCallback callback, object asyncState ) • BeginFlush. Inicia um pedido assíncrono para limpar todos os items do cache service. public void BeginFlush ( André Coutinho 45 Microsoft .NET – Application Blocks AsyncCallback callback, object asyncState ) • BeginNotify. Inicia um pedido assíncrono para notificação de que um item foi usado recentemente. public void BeginNotify ( string key, System.AsyncCallback callback, object asyncState ) • BeginRemove. Inicia um pedido assíncrono para remover um item. public void BeginRemove( string key, System.AsyncCallback callback, object asyncState ) • CacheService. Construtor para iniciar os processos de exploração e expiração. public CacheService() • Flush. Limpa todos os items do cache service. public void Flush() • GetData. Devolve o item do cache service, correspondente à chave especificada. public CacheItem GetData ( string key ) • InitializeLifetimeService. Obtém um objecto lifetime service para controlo da política de tempo de vida para esta instância. public override object InitializeLifetimeService() • Notify. Notifica que o item correspondente à chave especificada, foi usado recentemente. public void Notify ( string key ) • Remove. Remove os metadados do cache service. public void Remove ( string key ) 7.8.3. A interface ICacheStorage Um storage provider que implemente o interface ICacheStorage consegue tratar a inserção, recuperação e eliminação de dados em cache e para a cache. André Coutinho 46 Microsoft .NET – Application Blocks O CAB fornece três implementações de armazenamento em cache nos seguintes namespaces: • • • MmfCacheStorage SingletonCacheStorage SqlServerCacheStorage Esta interface permite implementar mecanismos de armazenamento em cache. 7.8.3.1. Métodos da interface ICacheStorage • Add. Armazena um item com a chave e valor especificados void Add ( string key, object keyData); • Flush. Remove todos os items do armazenamento. void Flush(); • GetData. Devolve o item, correspondente à chave especificada. object GetData ( string key); • Remove. Remove o item , correspondente à chave especificada. void Remove ( string key ) 7.8.3.2. Propriedades da interface ICacheStorage • Size. Devolve o número de items armazenados. int Size{ get; } 7.9. Ficheiros de Configuração Este application block usa vários ficheiros de configuração para a aplicação, o serviço de cache, e para os ficheiros Cliente.exe e Server.exe. É necessário adicionar parâmetros de configuração a cada um destes ficheiros antes de utilizar o CAB. App.config O App.config é usado pelos seguintes parâmetros da classe CacheManager: • Protecção de dados André Coutinho 47 Microsoft .NET – Application Blocks • • • Armazenamento e modo de armazenamento Exploração Expiração <?xml version ="1.0"?> <configuration> <startup> <requiredRuntime version="v1.0.3705"/> <supportedRuntime version="v1.0.3705"/> </startup> </configuration> CacheService.config O CacheService.config é utilizado para configurar os seguintes parâmetros da classe CacheService: • Armazenamento • Exploração • Expiração Client.exe.config O ficheiro de configuração Client.exe.config serve para configurar os seguintes parâmetros no cliente (remoto): • Caminho e tipo de um objecto remoto • Canal usado para comunicar com o cliente <configuration> <system.runtime.remoting> <application> <channels> <channel ref="http" useDefaultCredentials="true" port="0"> <clientProviders> <formatter ref="binary" /> </clientProviders> </channel> </channels> <client> <wellknown url="http://localhost:80/HttpBinary/SAService.rem" type="ServiceClass, ServiceClass" /> </client> </application> </system.runtime.remoting> </configuration> Server.exe.config Este ficheiro é utilizado na configuração de vários parâmetros no servidor (remoto): André Coutinho 48 Microsoft .NET – Application Blocks • • Caminho e tipo de um objecto remoto Canal usado para comunicar com o servidor <configuration> <system.runtime.remoting> <application> <service> <activated type="ClientActivatedType, RemoteType"/> </service> <channels> <channel port="8080" ref="http"/> </channels> </application> </system.runtime.remoting> </configuration> 7.10. Protecção de dados Os dados armazenados em cache devem ser protegidos contra a adulteração e spoofing de dados. O CAB possui um interface IDataProtection, (a ver mais à frente) de modo a permitir a implementação de um esquema de protecção. A classe DefaultDataProtection funciona como um fornecedor de serviços de protecção de dados e encapsula o Win32 Data Protection API (DPAPI) para encriptar/desencriptar items em cache, e usa o algoritmo SHA1 para fazer a validação. Quer se use esta classe ou um outro fornecedor de serviços de protecção de dados, é necessário colocar o atributo Encrypted para o elemento <StorageInfo> a true, no ficheiro App.config, para encriptar os dados armazenados em cache. 7.10.1. A interface IDataProtection A interface IDataProtection permite implementar esquema de protecção da cache. public interface IDataProtection 7.10.1.1. Métodos do interface IDataProtection • ComputerHash. Faz o hashing para validação de dados. byte[] ComputeHash( byte[] plainValue ); • Decrypt. Desencripta um array de bytes. byte[] Decrypt( byte[] cipherValue ); • Encrypt. Encripta um array de bytes. byte[] Encrypt ( byte[] plainValue ) André Coutinho 49 Microsoft .NET – Application Blocks • Init. Inicializa o serviço de protecção de dados. void Init( XmlNode config ); 7.11. Expiração Quando um item é adicionado à cache, é necessário determinar a sua política de expiração, isto é, qual é a sua data de expiração. A tabela seguinte lista as classes existentes no namespace Microsoft.ApplicationBlocks.Cache.Expirations, cada uma destas classes usa o interface IcacheItemExpiration. 7.11.1. Classe AbsoluteTime É utilizada para determinar tempos de expiração. Testa se um item da cache já expirou. public class AbsoluteTime: ICacheExpiration, ISerializable 7.11.1.1. Construtores da classe AbsoluteTime • AbsoluteTime. Cria uma instância da classe. public AbsoluteTime() 7.11.1.2. Métodos da classe AbsoluteTime • GetObjectData. Faz a serialização dos membros da classe. [SecurityPermissionAttribute( SecurityAction.Demand, SerializationFormatter = true )] SecurityPermissionAttribute( SecurityAction.LinkDemand, SerializationFormatter = true )] public void GetObjectData ( SerializationInfo info, StreamingContext context ) • Change. Indica o tempo de expiração de um item. public event ItemDependencyChangeEventHandler Change; 7.11.2. Classe ExtendedFormatTime Esta classe é usada para determinar uma data de expiração, baseada numa expressão (por exemplo cada minuto, cada Domingo). public ExtendedFormatTime() André Coutinho 50 Microsoft .NET – Application Blocks 7.11.3. Classe FileDependency Usa-se esta classe para definir uma data de expiração, baseando-se no facto de um ficheiro ter sido alterado ou não. public class ISerializable FileDependency: IcacheItemExpiration, 7.11.3.1. Construtores da classe FileDependency • FileDependency. Cria uma instância da classe. public FileDependency( string fullFileName ) 7.11.3.2. Métodos da classe FileDependency • GetObjectData. Preenche um objecto SerializationInfo, com os dados necessários para serializar o objecto pretendido. [SecurityPermissionAttribute( SecurityAction.Demand, SerializationFormatter = true)] [SecurityPermissionAttribute( SecurityAction.LinkDemand, SerializationFormatter = true)] public void GetObjectData( SerializationInfo info, StreamingContext context ) • HasExpired. Indica se um objecto já expirou ou não. bool ICacheItemExpiration.HasExpired() • Key. Define a chave de dependência externa. void ICacheItemExpiration.Key( string keyVal) • Notify. Notifica que um item foi usado. void ICacheItemExpiration.Notify() 7.11.3.3. Eventos da classe FileDependency • Change. Ocorre quando um item expira. public event ItemDependencyChangeEventHandler Change; 7.11.4. Classe SlidingTime André Coutinho 51 Microsoft .NET – Application Blocks É usada para definir o tempo de vida de um qualquer item, especificando uma data de expiração com base no último acesso a esse item. public class ISerializable SlidingTime: IcacheItemExpiration, 7.11.4.1. Métodos da classe SlidingTime • GetObjectData. Preenche um objecto SerializationInfo, com os dados necessários para serializar o objecto pretendido. public void GetObjectData( SerializationInfo info, StreamingContext context ) • HasExpired. Indica se um objecto já expirou ou não. bool ICacheItemExpiration.HasExpired() • Key. Define a chave de dependência externa. void ICacheItemExpiration.Key( string keyVal) • Notify. Notifica que um item foi usado. void ICacheItemExpiration.Notify() • SlidingTime. Cria uma instância da classe. public SlidingTime ( TimeSpan slidingExpiration ) 7.11.4.2. Eventos da classe SlindingTime • Change. Ocorre quando um item expira.. public event ItemDependencyChangeEventHandler Change; NOTA: se não for definido nenhum tempo de expiração para um qualquer item, este nunca expira. É possível utilizar uma implementação customizada, ao invés de usar umas das classes acima descritas. No processo de escolha de uma política de expiração, devem-se ter em consideração os seguintes aspectos: • Deve ser usada uma expiração com base no tempo, para items voláteis. Estes são os items que sofrem regularmente actualizações, ou que apenas são válidos por um período de tempo bem definido (normalmente curto). • Deve ser usada uma expiração baseada em FileDependency para definir a validade de um determinado item em cache dependente de André Coutinho 52 Microsoft .NET – Application Blocks certas propriedades do ficheiro, que quando alteradas invalidam os dados armazenados. 7.12. Exploração A classe LruScavenging pertence ao CAB, e pode ser utilizada para remover automaticamente items da cache, quando há poucos recursos de armazenamento. Esta classe usa o algoritmo least recently used para retirar da cache os items que não são usados há mais tempo. Se for criada uma classe costumizada para exploração, esta deve ser especificada no elemento <ScavengingInfo> do ficheiro App.config. As classes criadas para exploração devem implementar o interface IScavengingAlgorithm. Quando um item é explorado apartir da cache, é possível especificar um CacheItemRemovedCallback para representar o método que será invocado quando um item é removido da cache. O CacheItemRemoveCallback também especifica a causa para a remoção de um determinado item. 7.12.1. A interface IScavengingAlgorithm Permite a implementação de algoritmos de exploração. public interface IScavengingAlgorithm 7.12.1.1. Métodos do interface IScavengingAlgorithm • Add. Adiciona um item à lista do algoritmo void Add ( string key, CacheItemPriority priority ); • Execute. Executa o algoritmo. void Execute (); • Flush. Remove todos os items da lista do algoritmo. void Flush (); • Init. Inicia o algoritmo de exploração. void Init ( CacheService cacheService, ICacheStorage cacheStorage, ICacheMetadata cacheMetadata, XmlNode config ); • Notify. Notifica que o item, correspondente à chave especificada, foi usado recentemente. void Notify( string key ); André Coutinho 53 Microsoft .NET – Application Blocks • Remove. Remove o item, correspondente à chave especificada. void Remove ( string key); • CacheItemRemovedCallback. Representa o método a invocar quando um item é removido da cache. public delegate void CacheItemRemovedCallback(string key,CacheItemRemoveCache cause); 7.13. Armazenamento Existem três opções disponíveis para o armazenamento em cache, usando o CAB. • • • MMF (memory-mapped file) Objectos singulares Bases de dados SQL Server 2000 7.13.1. Como escolher uma das opções de armazenamento Ao escolher uma das opções de armazenamento deve-se ter em conta: • • • • O alcance da aplicação. Para uma aplicação cliente em Windows Forms, o MMF só tem alcance no computador cliente. Os MMFs são mais apropriados para um cenário de caching baseado no cliente. É possível utilizar MMFs para desenvolver uma cache distribuída pelos vários AppDomain e processos no mesmo computador. O Framework .NET não suporta MMFs, portanto qualquer implementação que use MMFs não beneficia de várias características do .NET, como por exemplo as características de gestão de memória, (como o garbage collection) e de segurança. Os objectos singulares só podem ser usados no alcance do AppDomain, mas proporcionam melhor performance. O armazenamento em SQL Server 2000 é mais aconselhado para aplicações que requerem uma grande durabilidade, ou quando é necessário armazenar grandes quantidades de dados. Uma vez que o serviço de caching necessita de aceder ao SQL Server através de uma rede, e os dados são devolvidos usando queries, tornando portanto, o acesso relativamente lento. 7.13.2. Classe MemoryMappedFileStream Esta classe é usado para criar, fornecer acesso, ler, e remover dados da cache, de um MMF. public class MemoryMappedFileStream: Sream, IDisposable André Coutinho 54 Microsoft .NET – Application Blocks 7.13.2.1. Construtores da classe MemoryMappedFileStream • MemoryMappedFileStream. Cria uma instância da classe. public MemoryMappedFileStream( string name, MemoryProtection protection ) 7.13.2.2. Métodos da classe MemoryMappedFileStream • Close. Fecha o MMF. public override void Close() • CloseMapHandle. Fecha o map handle do memory map. public void CloseMapHandle() • Dispose. Descarta o MMF. public void Dispose() • Flush. Remove todos os MMFs do armazenamento de dados. void ICacheStorage.Flush () • MapViewToProcessMemory. Verifica o comprimento e o offset de um MMF. public void MapViewToProcessMemory( int offSet, int count ) • OpenDictionary. Abre o objecto Dictionary, se disponível. public static IntPtr OpenDictionary ( string name, MemoryProtection protection ) • OpenExisting. Abre um MMF existente. public static MemoryMappedFileStream OpenExisting( string name, MemoryProtection protection ) • Read. Lê de um MMF e escreve no buffer. public override int Read( byte[] buffer, int offSet, int count ) • Seek. Procura uma determinada posição num stream de bytes. André Coutinho 55 Microsoft .NET – Application Blocks public override long Seek( long offset, SeekOrigin origin ) • SetLength. Ajusta o comprimento de um MMF. public override void SetLength( long memoryLength ) • SetMaxLength. Ajusta o comprimento máximo do ficheiro. public void SetMaxLength( long maxLength ) • Write. Escreve dados binários para um MMF. public override void Write( byte[] buffer, int offSet, int count ) 7.13.2.3. Propriedades da classe MemoryMappedFileStream • CanRead. Verifica se o MMF tem acesso de leitura. public override bool CanRead {get;} • CanSeek. Verifica se o MMF tem acesso de pesquisa. public override bool CanSeek {get;} • CanWrite. Verifica se o MMF tem acesso de escrita. public override bool CanWrite {get;} • Length. Verifica o comprimento do MMF public override long Length {get;} • MaxLength. Verifica o comprimento máximo do MMF. public uint MaxLength {get;} • Position. Verifica ou ajusta a posição no MMF. public override long Position {get; set;} André Coutinho 56 Microsoft .NET – Application Blocks 8. Aggregation Application Block 8.1. Introdução O Aggregation Application Block (AAB) é uma extensão ao Framework .NET que permite a fácil manutenção e junção de informação de vários Service providers e outros sistemas, para a apresentação aos utilizadores. Este application block fornece um ambiente RAD (Rapid Application Development) para auxiliar à construção de aplicações escaláveis, usando o Visual Studio .NET. É fornecido como uma biblioteca de classes .NET. As fontes básicas de dados serão quase sempre serviços baseados em .NET, mas também poderão ser obtidos apartir de servidores do tipo BizTalkServer 2000 e SQL Server 2000, por exemplo. 8.2. Funcionamento Num cenário de agregação de informação, uma aplicação cliente faz um pedido a vários Service providers para obter os dados de que necessita. Um Service Agent é usado para comunicar com cada provider, de modo a reunir os dados requisitados e a entregá-los de forma agregada à aplicação cliente. Os Service Agents são de extrema importância no AAB. Toda a lógica dos pedidos de informação, está contida nas classes ServiceAgent criadas. Para adicionar um novo service provider ou criar um novo service request, com o seu próprio formato e protocolo de pedido/resposta, uma aplicação necessita apenas que seja criado um Service Agent. Um ServiceAgent novo pode herdar o comportamento (ou partes deste) duma classe ServiceAgent mais genérica, que tenha sido criada. O RequestProcessor é o componente responsável pela gestão dos pedidos de agregação de informação. Adiciona Service Agents a uma fila, apartir da qual os Service Agents são executados. Os Service Agents retornam os resultados para o Transformation Manager, para serem transformados para XSL. Usando ficheiros XSL e XSD, serão devolvidos ao utilizador os resultados agregados num único ficheiro XML. 8.3. Usando o AAB com outros Application Blocks O AAB incorpora o EMAB (Exception Management Application Block) para o tratamento de excepções. No entanto, não é obrigatório usar o EMAB em conjunto com o AAB. É possível usar o AAB independentemente numa aplicação, mas também com outros application blocks, tais como, o AIAB (Asynchronous Invocation Application Block) e o CAB (Caching Application Block). André Coutinho 57 Microsoft .NET – Application Blocks 8.4. Design do AAB Abaixo podemos ver a arquitectura do AAB. Figura 14: Arquitectura do AAB Na figura podemos ver: • O cliente cria um objecto AggregationRequest que contêm os detalhes do pedido de informação. Este objecto lê o ficheiro de configuração AggregationConfig.xml, para obter o nome da classe, caminho e tempo de vida para todos os Service Agents criados para atender o pedido. • O objecto AggregationRequest chaem o método ProcessRequest da classe RequestProcessor, e passa os detalhes do pedido. O RequestProcessor cria os objectos ServiceAgentRequestDetails e adiciona-os à fila ServiceAgentDetailQueue da classe AggregationThreadPool. • A classe AggregationThreadPool gere uma pool de threads usadas para executar Service Agents. Uma thread é iniciada chamando o método WorkerThread.Start. O ServiceAgentMonitor monitoriza as threads que estão a executar Service Agents, e aborta as threads que continuam a executar após o seu tempo de vida ter expirado. • O Service Agent adquire a informação do service provider e actualiza o RequestProcessor.XmlResultList, que contém um array de documentos XML com os resultados. • O objectos AggregationRequest cria um objecto Transformation Manager e passa-o ao array de documentos XML, de stylesheets, e ao schema file. O Transformation Manager transforma e une os André Coutinho 58 Microsoft .NET – Application Blocks • documentos XML que contém os resultados, num único documento XML. O objecto AggregationRequest devolve, finalmente, a estrutura AggregationRequest para o cliente. 8.5. Classe AggregationRequest Esta classe é utilizada por uma aplicação cliente para fazer um pedido de agregação da informação que precisa. Passa os pedidos à classe RequestProcessor, e cria um objecto Transformation Manager para transformação dos resultados. public class AggregationRequest 8.5.1. Construtor da classe AggregationRequest • AggregationRequest. Inicializa uma nova instância da classe. public AggregationRequest() 8.5.2. Métodos da classe AggregationRequest • GetResults. Devolve resultados agregados do RequestProcessor. public AggregatedResult GetResults( string requestType ) 8.6. A estrutura AggregatedResult A estrutura AggregatedResult é usada para devolver o resultado da agregação dos dados ao cliente. public struct AggregatedResult 8.6.1. Propriedades da estrutura AggregatedResult • AggregatedDataSet. Dataset que contém resultados de pedidos. public DataSet AggregatedDataSet {get;} • AggregatedXml. Documento TransformationManager. XML agregado, do public XmlDocument AggregatedXml {get;} • ExceptionMessages. Obtém as mensagens de erro de Service Agents que falharam. public ArrayList ExceptionMessages {get} André Coutinho 59 Microsoft .NET – Application Blocks NOTA: esta estrutura pode ser retornada sob a forma de um documento XML ou Dataset. 8.7. Ficheiro de configuração O AAB usa um ficheiro de configuração, o App.config, para a configuração de intervalos de tempo, e pool de threads, para o RequestProcessor. App.config • AggregationConfigFile. Especifica o caminho para o ficheiro AggregationConfig.xml • RequestProcessorSleepTime. • MinThreadPoolSize. • MaxThreadPoolSize. • ServiceAgentMonitorInterval. Especifica o tempo (em milisegundos), durante o qual a thread irá adormecer (sleep) antes de verificar o resultado e o tempo final de um pedido de agregação. Especifica o número mínimo de threads estáticas que deverão estar disponíveis na pool. Especifica o número máximo de threads estáticas que deverão estar disponíveis na pool. Especifica o intervalo de tempo para a classe ServiceAgentMonitor. • Especifica o tempo (em milisegundos) que uma thread dinâmica espera em suspensão (idle) antes de fazer exit. • DynamicThreadCreateTime. DynamicThreadWaitTime. Especifica o tempo (em milisegundos) que deverá esperar na fila antes da classe AggregationThreadPool criar e iniciar uma thread dinâmica. um Service Agent <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <!-- FILE PATH SETTING Use AggregationConfigFile key to specify the path where the Aggregationconfig.xml file_u105 ?s available. --> <add key="AggregationConfigFile" value="<AggregationConfigFilePath>\AggregationConfig.xml" /> <!-- PROCESSOR SLEEP TIME SETTING Use RequestProcessorSleepTime key to specify the amount of time the main thread sleeps before checking the result count and André Coutinho 60 Microsoft .NET – Application Blocks finish time of the aggregation request. --> <add key="RequestProcessorSleepTime" value="1000" /> <!-- THREAD POOL SIZE SETTING Use MinThreadPoolSize key to specify the minimum number of threads that should be available in the thread pool. --> <add key="MinThreadPoolSize" value="10" /> <!-- THREAD POOL SIZE SETTING Use MaxThreadPoolSize key to specify the maximum number of threads in the thread pool. --> <add key="MaxThreadPoolSize" value="20" /> <!-- MONITOR INTERVAL SETTING Use ServiceAgentMonitorInterval key to specify the timer interval of the ServiceAgentMonitor_u99 ?lass. --> <add key="ServiceAgentMonitorInterval" value="1000" /> <!-- THREAD WAIT TIME SETTING Use DynamicThreadWaitTime key to specify the amount of time in milliseconds the dynamic thread idly waits, before it exits. --> <add key="DynamicThreadWaitTime" value="3000" /> <!-- THREAD CREATE TIME SETTING Use DynamicThreadCreateTime key to specify the amount of time in milliseconds. If the service agent has waited in the queue for the above mentioned time interval, then a new dynamic thread is created and started. --> <add key="DynamicThreadCreateTime" value="1000" /> </appSettings> </configuration> AggregationConfig.xml Este ficheiro é utilizado para descrever detalhes do Service Agent, nomeadamente, o nome da classe do Service Agent, o local de instalação, o tempo de vida e a localização das style sheets. Sempre que é criada uma nova classe de Service Agents é necessário actualizar este ficheito para reflectir a configuração dos Service Agents. <!DOCTYPE Aggregation[ <!ELEMENT Aggregation (CONFIGITEM+)> <!ELEMENT CONFIGITEM ANY> <!ATTLIST CONFIGITEM NAME ID #REQUIRED> ]> <!-This file contains the details about the service agents like class name, André Coutinho 61 Microsoft .NET – Application Blocks Assembly file location and the style sheet files etc., --> <Aggregation> <Request> <!-RequestType definition --> <RequestType Type="SampleType"> <ServiceAgent> <!-SAID - Service agent Id's for the request type declared above. --> <SAID>A</SAID> <SAID>B</SAID> <SAID>C</SAID> </ServiceAgent> <!-The aggregation request time-to-live value. If the TTL value is not passed as an argument, then this value is considered. --> <TimeToLive>5</TimeToLive> <!-The schema file path --> <TransformationSchema>"<SchemaFilePath>\<SchemaFileName>"</Trans formationSchema> </RequestType> <RequestType Type="Sample"> <ServiceAgent> <SAID>D</SAID> <SAID>E</SAID> </ServiceAgent> <TimeToLive>5</TimeToLive> <TransformationSchema>"<SchemaFilePath>\<SchemaFileName>"</Trans formationSchema> </RequestType> </Request> <ServiceAgentDetails> <!-Details of the service agent, whose SAID is declared above. --> <ServiceAgent SAID="A"> <!-The class name of the service agent --> <ClassName><Namespace>.<ClassName></ClassName> <!-The location of the assembly where this service agent is defined. --> <AssemblyPath>"<ServiceAgentAssemblyPath>\<ServiceAgentAssemblyN ame.dll>"</AssemblyPath> <!-- André Coutinho 62 Microsoft .NET – Application Blocks The style sheet file that is applied over the xml result obtained from this service agent --> <Transformation>"<XSLTFilePath>\<XSLTFileName>"</Transformation> </ServiceAgent> <ServiceAgent SAID="B"> <ClassName><Namespace>.<ClassName></ClassName> <AssemblyPath>"<ServiceAgentAssemblyPath>\<ServiceAgentAssemblyN ame.dll>"</AssemblyPath> <Transformation>"<XSLTFilePath>\<XSLTFileName>"</Transformation> </ServiceAgent> <ServiceAgent SAID="C"> <ClassName><Namespace>.<ClassName></ClassName> <AssemblyPath>"<ServiceAgentAssemblyPath>\<ServiceAgentAssemblyN ame.dll>"</AssemblyPath> <Transformation>"<XSLTFilePath>\<XSLTFileName>"</Transformation> </ServiceAgent> <ServiceAgent SAID="D"> <ClassName><Namespace>.<ClassName></ClassName> <AssemblyPath>"<ServiceAgentAssemblyPath>\<ServiceAgentAssemblyN ame.dll>"</AssemblyPath> <Transformation>"<XSLTFilePath>\<XSLTFileName>"</Transformation> </ServiceAgent> <ServiceAgent SAID="E"> <ClassName><Namespace>.<ClassName></ClassName> <AssemblyPath>"<ServiceAgentAssemblyPath>\<ServiceAgentAssemblyN ame.dll>"</AssemblyPath> <Transformation>"<XSLTFilePath>\<XSLTFileName>"</Transformation> </ServiceAgent> </ServiceAgentDetails> </Aggregation> André Coutinho 63 Microsoft .NET – Application Blocks 9. Asynchronous Invocation Application Block 9.1. Introdução O problema das comunicações síncronas Uma aplicação distribuída poderá necessitar de informação de vários Service providers. Se todo o processo for feito de maneira síncrona, o cliente terá que esperar até que todos os Service providers sejam contactados e também terá que contar com atrasos no estabelecimento da conexão, ou atrasos de serviço. A comunicação síncrona obriga o utilizador a esperar que todos os Service providers processem o pedido. O AIAB resolve este problema de performance, fornecendo um mecanismo de processamento assíncrono dos pedidos, que aumenta a eficiência do servidor e melhora a experiência do utilizador. O AIAB gere as comunicações assíncronas entre um cliente e um ou mais Foreign Service Providers (FSP). Um FSP pode ser um serviço Web baseado em .NET ou Java, por exemplo. Usando este application block numa aplicação Web é possível: • Melhorar a experiência de browsing do utilizador final. • Aumentar a performance do servidor. 9.2. Funcionamento Um cliente faz um pedido para múltiplos Service providers, submetendo esse pedido ao AIAB. O AIAB usa um Service Agent para comunicar com cada service provider. Assim que a informação requisitada fica disponível, é enviada ao cliente. Desta forma, uma página, por exemplo, está pronta a ser visualizada mesmo antes de toda a informação requisitada estar disponível. O utilizador final “sente” uma aplicação mais rápida, e os recursos dos servidores são libertados muito mais rapidamente, do que num cenário de sincronismo. 9.3. Utilização do AIAB com outros application blocks O AIAB incorpora o EMAB (Exception Management Application Block) para tratamento de excepções, embora não seja obrigatório usar o EMAB conjuntamente com o AIAB. O DAAB (Data Access Application Block) também está incluído para minimizar o código de acesso a dados que seja preciso escrever. Este application block pode ser usado sozinho como uma biblioteca de classes, mas também é possível incluir o seu código fonte num qualquer projecto é modificá-lo de acordo com as necessidades. Também foi desenhado para trabalhar em conjunto com o CAB (Caching Application Block) e o AAB (Aggregation Application Block). André Coutinho 64 Microsoft .NET – Application Blocks 9.4. Arquitectura do AIAB A arquitectura deste application block involve vários subsistemas: • O subsistema Request, que contém classes que permitem criar service requests e persisti-los numa base de dados. • O subsistema Dispatcher, que contém classes para recuperar os service requests da base de dados, pô-los numa fila, e atribuir threads para executar os Service Agents. • O subsistema Monitor, que monitoriza a execução dos Service Agents e executa a garbage collection. A Figura abaixo ilustra o modo de funcionamento do AIAB. Figura 15: Arquitectura do AIAB 9.4.1. O subsistema Request • • • Um cliente cria um objecto AsyncRequestBatch que irá conter os dados do pedido, e chama o método AddRequest para adicionar os pedidos individualmente ao AsyncRequestBatch. O cliente chama o AsyncRequestProcessor.SubmitRequest para submeter o pedido. O AsyncRequestProcessor cria uma estrutura Request para armazenar os detalhes dos pedidos e persisti-los na base de dados usando o RequestDBOp. André Coutinho 65 Microsoft .NET – Application Blocks 9.4.1.1. Classe AsyncRequestBacth Esta classe é utilizada para armazenar os detalhes dos pedidos de informação dos clientes. public class AsyncRequestBatch: ISerializable 9.4.1.1.1. Construtor da classe AsyncRequestBatch • AsyncRequestBatch. Inicializa uma nova instância da classe. public AsyncRequestBatch( int timeOutValue, bool isPersistentValue) 9.4.1.1.2. Métodos da classe AsyncRequestBatch • AddRequest. Adiciona o friendly name, a chave de referência, a contagem de retries, o tempo de vida e parâmetros de input para um Service Agent. public void AddRequest( string friendlyName, string referenceKey) • GetObjectData. Faz a serialização de membros da classe. public void GetObjectData ( SerializationInfo info, StreamingContext context) • RemoveAllServiceAgentInputData. Remove todas as estrutura, do objecto AsyncRequestBatch. public void RemoveAllServiceAgentInputData() • RemoveServiceAgentInputData. Remove a estrutura que contém a chave de referência passada como parâmetro. public void RemoveServiceAgentInputData( string referenceKey) 9.4.1.1.3. Propriedades da classe AsyncRequestBatch • Count. Conta o número de pedidos no serviceAgentInputDataList. public int Count {get;} • IsPersistent. Especifica se um Service Agent é persistente ou não. public bool IsPersistent {get;} André Coutinho 66 Microsoft .NET – Application Blocks • TimeOut. Obtém o valor de time out de um Service Agent. public int TimeOut {get;} • AsyncRequestProcessor.SubmitRequest. Persiste o objecto Request na base de dados e retorna uma string requestID. public static string SubmitRequest( AsyncRequestBatch requestBatch) 9.4.2. O subsistema Dispatcher • • • • • • • Quando o AsyncROHService arranca, obtém a instância do AsyncDispatcher, cria uma nova thread para o Processor e chama o método ExecuteServiceAgent para adicionar objectos ServiceAgentReqDetails à pool AsyncThreadPool. O Processor lê os pedidos dos Service Agents da base de dados, segundo um escalonamento FIFO. A seguir cria um objecto ServiceAgentReqDetails para uso do ServiceAgent, para o contacto com os Service providers. O Processor adiciona cada objecto ServiceAgentReqDetails à fila de Service Agents. As threads da pool AsyncThreadPool lêem os Service Agents da fila de Service Agents e executam-nos. O Processor só recupera novos Service Agents se existirem threads suficientes na pool de threads. Depois de um atraso de poll-back, o Processor volta a inquirir a base de dados para descobrir novos Service Agents. As threads da pool AsyncThreadPool lêem os Service Agents da fila respectiva e executam-nos. Os Service Agents comunicam com os Service providers e retornam os resultados. Os resultados são persistidos na base de dados através de um procedure (usando o objecto DispatcherDBOp) e passando o objecto Result. O cliente inquire o ResultManager para ver o estado do pedido e resultado, ou para cancelar o pedido. Enquanto é processado, um pedido poderá encontrar-se num de vários estados: public enum RequestStatus { Init, Executing, Completed, MarkedForDeletion, ToBeCleared, Failed, PartiallyCompleted, Unknown } Estados • André Coutinho Init. O pedido foi iniciado. 67 Microsoft .NET – Application Blocks • • • • • • • Executing. O pedido está a executar. Completed. O pedido foi completo. MarkedForDeletion. O pedido está assinalado para ser removido. ToBeCleared. O pedido está assinalado para ser terminado. Failed. O pedido falhou. PartiallyCompleted. O pedido está parcialmente completo. Unknown. O pedido está num estado não desconhecido. 9.4.2.1. Classe AsyncROHServices Esta classe é um serviço que permite a execução de Service Agents. public class AsyncRohService : ServiceBase 9.4.2.1.1. Construtor da classe AsyncROHService • AsyncRohService. Construtor da classe public AsyncRohService() 9.4.2.1.2. Métodos da classe AsyncROHService • Dispose. Liberta os recursos usados pelo serviço. protected override void Dispose( bool isDisposing) • OnCustomCommand. Executa uma operação específica enquanto o serviço está a correr. protected override void OnCustomCommand( int command) • OnStart. Chamado quando o serviço arranca. protected override void OnStart( string[] args) • OnStop. Chamado quando o serviço pára. protected override void OnStop() 9.4.2.2. Classe AsyncDispatcher A classe AsyncDispatcher é uma classe usada para instanciar o Processor. public class AsyncDispatcher 9.4.2.2.1. Propriedades da classe AsyncDispatcher André Coutinho 68 Microsoft .NET – Application Blocks • AsyncProcessor. Obtém o Processor, do AsyncDispatcher. public Processor AsyncProcessor {get;} • Dispatcher. Obtém a instância única da classe. public static AsyncDispatcher Dispatcher {get;} 9.4.2.3. Classe Processor Classe que serve para criar e adicionar objectos ServiceAgentReqDetails à pool de threads, e arrancar o Service Agent Monitor. public class Processor 9.4.2.3.1. Métodos da classe Processor • ExecuteServiceAgent. ServiceAgentReqDetails. Executa um objecto public void ExecuteServiceAgent() • RefreshCache. Apaga os dados actuais da tabela de hash e actualiza-a com os dados mais recentes da base de dados. public void RefreshCache 9.4.2.4. Classe ServiceAgentReqDetails Esta classe armazena os detalhes dos Service Agent. public class ServiceAgentReqDetails 9.4.2.4.1. Construtor público da classe ServiceAgentReqDetails • ServiceAgentReqDetails. Construtor público da classe. public ServiceAgentReqDetails( string requestID, string serviceAgentID, int timeToLive, int retryCount, string referenceKey, object[] inputParams, string assemblyPath, string className) 9.4.2.4.2. Métodos da classe ServiceAgentReqDetails • UpdateFinishTime. Actualiza o tempo de término para um Service Agent. André Coutinho 69 Microsoft .NET – Application Blocks public void UpdateFinishTime() 9.4.2.4.3. Propriedades da classe ServiceAgentReqDetails • AssemblyPath. Obtém o caminho da assembly para o Service Agent. public string AssemblyPath{get;} • ClassName. Obtém o nome da classe do Service Agent. public string ClassName{get;} • CurrentRetryCount. Obtém ou altera o número de retries do Service Agent. public int CurrentRetryCount{get; set;} • FinishTime. Obtém o tempo de término do Service Agent. public DateTime FinishTime {get;} • InputParams. Obtém o objecto InputParams. public object[] InputParams {get;} • ReferenceKey. Obtém a reference key do Service Agent. public string ReferenceKey {get;} • RequestID. Obtém o RequestID do pedido que está a ser tratado pelo Service Agent. public string RequestID {get;} • ServiceAgentID. Obtém o ServiceAgentID do Service Agent. public string ServiceAgentID {get;} • State. Obtém ou altera o estado do Service Agent. public ServiceAgentStatus State {get; set;} 9.4.2.5. Classe ResultManager A classe ResultManager é utilizada para obter o estado dos pedidos e resultados. public sealed class ResultsManager 9.4.2.5.1. Métodos da classe ResultManager André Coutinho 70 Microsoft .NET – Application Blocks • CancelRequest. Actualiza MarkedForDeletion. o estado de um pedido para public static void CancelRequest( string requestID) • ClearResults. Actualiza o estado de um pedido para ToBeCleared. public static void ClearResults( string requestID) • GetIncrementedResults. Obtém os resultados não lidos para o requestID e reference key passados. public static InvocationResult GetIncrementalResults( string requestID) • GetRequestStatus. Obtém o estado do pedido, para o requestID passado. public static RequestStatus GetRequestStatus( string requestID) • GetResultCompletionStatus. Devolve “Pending” ou “Completed”, conforme o pedido esteja terminado ou não. public static GetResultCompletionStatus( string requestID) • ResultCompletionStatus GetResults. Obtém os resultados lidos/não lidos para o requestID e reference key passados. public static InvocationResult GetResults( string requestID) 9.4.3. O subsistema Monitor O Recovery Monitor corre em intervalos regulares para monitorizar a execução dos Service Agents e determinar se algum terá excedido o seu tempo de vida. Se um Service Agent excedeu o seu número de retries, é marcado como Failed na base de dados. O Monitor também procura Service Agents que estão como MarkedForDeletion e pára as suas threads. Se houver uma falha no servidor, o Processor nessa máquina volta a ler a base de dados quando reinicia. Se um qualquer service request está à espera de ser processado, será então carregado e processado. O Recovery Monitor também verifica os pedidos em estado Executing quando o tempo de execução excedeu o CompletedTimetoLive do pedido, e não foi marcado como Failed. Se existirem pedidos nestas condições o pedido reinicia. O garbage collection apaga os objectos Request da base de dados depois do seu tempo de vida expirar, ou quando o status é MarkedForDeletion. André Coutinho 71 Microsoft .NET – Application Blocks 9.4.3.1. Classe RecoveryMonitor Esta classe é usada para resubmeter Service Agents que estão em execução para além do seu tempo de vida. public class RecoveryMonitor 9.4.3.1.1. Construtor da classe RecoveryMonitor • RecoveryMonitor. Construtor da classe. public RecoveryMonitor 9.4.3.1.2. Métodos da classe RecoveryMonitor • StopTimer. Liberta todos os recursos usados pelo RecoveryMonitor. public void StopTimer() 9.4.3.2. Classe GarbageCollection A classe GarbageCollection é utilizada para apagar detalhes de um pedido, que estejam marcados para remoção, ou para pedidos já expirados. public class GarbageCollection 9.4.3.2.1. Construtor da classe GarbageCollection • GarbageCollector. GarbageCollect. Activa o timer que invoca o método public GarbageCollector 9.4.3.2.2. Métodos da classe GarbageCollection • StopTimer. Liberta todos os recursos usados pelo GarbageCollector. public void StopTimer 9.4.4. Design da base de dados André Coutinho 72 Microsoft .NET – Application Blocks A Figura seguinte ilustra a base de dados SQL Server 2000 usada pelo AIAB. Figura 16: Base de dados usada pelo AIAB 9.4.5. Ficheiros de configuração O AIAB usa três ficheiros App.config para configurar o acesso à base de dados, o intervalo de tempo de serviços como o Garbage Collection e a recuperação de Service Agents, tamanho da pool de threads, entre outros. Antes de se proceder a qualquer configuração. Só após as alterações se pode reiniciar o serviço. A seguir descrevem-se os três ficheiros de configuração usados pelo AIAB. MonitorService App.config • • • • ConnectionString. Especifica a base de dados e permissões de acesso para o SQL Server 2000. AsyncGarbageCollectorTimeInterval. Especifica o intervalo de tempo (em milisegundos) usado para o Garbage Collector. AsyncRecoveryMonitorTimeInterval. Especifica o intervalo de tempo (em milisegundos) do timer do recovery service. MaxNoofRequestsForGC. Especifica o número máximo de pedidos que o Garbage Collector irá apagar quando é chamado. André Coutinho 73 Microsoft .NET – Application Blocks <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <!-- DATABASE SETTING Use ConnectionString key to specify the database used for Asynchronous Framework Application Block and to set database related access permissions --> <add key="ConnectionString" value="Data Source=localhost;Initial Catalog=AsynchronousFramework;Integrated Security=SSPI" /> <!-- GARBAGE COLLECTOR SETTING Use AsyncGarbageCollectorTimeInterval key to specify the time interval(in msecs) of the timer used for garbage collecor service of AsynchronousInvocation Framework Application Block --> <add key="AsyncGarbageCollectorTimeInterval" value="10000" /> <!-- RECOVERY MONITOR SETTING Use AsyncRecoveryMonitorTimeInterval key to specify the time interval(in msecs) of the timer used for recovery service of AsynchronousInvocation Framework Application Block --> <add key="AsyncRecoveryMonitorTimeInterval" value="10000" /> <!-- GARBAGE COLLECTOR SETTING Use MaxNoofRequestsForGC key to specify the number of requests to be garbage collected in a single run by the garbage collector service of Asynchronous Framework Application Block --> <add key="MaxNoofRequestsForGC" value="10" /> </appSettings> </configuration> ROHService App.config • • • • ConnectionString. Especifica a base de dados e permissões de acesso para o SQL Server 2000. AsyncThreadPoolSize. Especifica o número de threads na pool de threads. É possível ajustar este número de acordo com o número de Service Agents a processar. AsyncMinimumNoOfFreeThreads. Especifica o número mínimo de threads disponíveis. AsyncReCheckTimeInterval. Especifica o tempo (em milisegundos) para o Dispatcher. É feito um Sleep antes de verificar o número mínimo de threads disponíveis para a execução de Service Agents. André Coutinho 74 Microsoft .NET – Application Blocks • • • • AsyncServiceAgentQueueSize. Especifica o tamanhoda fila de Service Agents na pool de threads. AsyncSAMonitorTimeInterval. Especifica o intervalo de tempo (em milisegundos) do timer usado pelo Service Agent Monitor. CommandRefreshCache. Especifica o valor (inteiro) para o comando customizado refrescar os dados SQL do Service Agent Master na cache do ROHService. MachineID. Especifica o machine ID no qual o ROHService está a correr. Se o ROHService está instalado em várias máquinas para loadbalancing, este valor tem que ser único. <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <!-- DATABASE SETTING Use ConnectionString key to specify the database used for Asynchronous Framework Application Block and to set database related access permissions --> <add key="ConnectionString" value="Data Source=localhost;Initial Catalog=AsynchronousFramework;Integrated Security=SSPI" /> <!-- THREAD POOL SIZE SETTING Use AsyncThreadPoolSize key to specify number of threads of the thread pool of Asynchronous Framework Application Block --> <add key="AsyncThreadPoolSize" value="10" /> <!-- THRESHOLD SETTING Use AsyncMinimumNoOfFreeThreads key to specify minimum number of threads free in the thread pool of Asynchronous Framework Application Block --> <add key="AsyncMinimumNoOfFreeThreads" value="5" /> <!-- DISPATCHER SLEEP TIME SETTING Use AsyncReCheckTimeInterval key to specify the sleep time(in msecs) for Dispatcher subsystem of AsynchronousInvocation Framework Application Block. This sleep time is used before checking for minimum number of free threads available for service agent execution. --> <add key="AsyncReCheckTimeInterval" value="3000" /> <!-- SERVICE AGENT QUEUE SIZE SETTING Use AsyncServiceAgentQueueSize key to specify the size of the service agent queue of the thread pool of Asynchronous Framework Application Block --> <add key="AsyncServiceAgentQueueSize" value="20" /> André Coutinho 75 Microsoft .NET – Application Blocks <!-- SERVICE AGENT MONITOR SETTING Use AsyncSAMonitorTimeInterval time interval(in msecs) of the used for service agent monitor Framework Application Block --> <add key="AsyncSAMonitorTimeInterval" key to specify the timer of Asynchronous value="10000" /> <!-- CUSTOM COMMAND SETTING Use COMMAND_REFRESH_CACHE key to specify the integer value for the custom command for refreshing the ServiceAgentMaster data in the cache of the remote object host windows service. --> <add key="CommandRefreshCache" value="128" /> <!-- MACHINE SETTING Use MachineId key to specify the machine id in which the dispatcher service is running. This is an integer value. --> <add key="MachineId" value="1" /> </appSettings> </configuration> SolutionItem App.config • ConnectionString. Especifica a base de dados e permissões de acesso para o SQL Server 2000. <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <!-- DATABASE SETTING Use ConnectionString key to specify the database used for Asynchronous Framework Application Block and to set database related access permissions --> <add key="ConnectionString" value="user id=sa;password=sa;Network=DBMSSOCN;DATABASE=AsyncDatabase;S ERVER=ÜsyncServer" /> </appSettings> </configuration> André Coutinho 76 Microsoft .NET – Application Blocks 10. User Interface Process Application Block 10.1. Introdução A maioria das aplicações requer algum tipo de controlo do processo de interacção com o utilizador, quer seja a mudança de uma página Web para outra, ou controlar o fluxo entre os forms de uma aplicação de tipo Wizard. Normalmente o código para controlar o processo de interacção com o utilizador (UIP) é escrito na própria camada de interacção com o utilizador. Esta prática não facilita a reutilização de código, manutenção e extensibilidade. À medida que as aplicações crescem em complexidade é útil introduzir um interface de interacção com o utilizador, que resolva os seguintes pontos: • Gestão do fluxo de controlo. O que os utilizadores vêem e o que são capazes de fazer, depende do estado de uma aplicação. Codificar esta lógica directamente na camada de interacção com o utilizador vai complicar a manutenção e reutilizaçã do código. • Gestão do estado. A interacção do utilizador com uma aplicação pode ser vista como uma conversa, com um estado associado. Será importante gerir o estado (da “conversa”) separadamente do estado operacional da aplicação. • Separação da lógica. A lógica que controla o fluxo e estado deve ser separada da lógica dos pedidos de informação dos utilizadores. • Encapsulamento do fluxo de contrlo e estado para um use case específico. Um use case é uma unidade lógica de trabalho. O encapsulamento de um use case resulta na possível reutilização de mesmo use case. O User Interface Process Application Block (UIPAB) foi criado para resolver os pontos acima descritos e permite escrever facilmente interfaces para o utilizador e processos de workflow, reusáveis e extensíveis. Especificamente o UIPAB ajuda a: • Separar o fluxo de controlo das páginas e forms, com que o utilizador interage. • Abstrair a gestão do estado de um use case, dos forms e controlos que trabalham com esses mesmos dados. • Usar o mesmo modelo de programação para o código que gere o fluxo de controlo e estado para diferentes tipos de aplicação, incluindo aplicações baseadas em Windows e aplicações Web, por exemplo. • Escrever aplicações capazes de gerir as tarefas do utilizador em cenários complexos, por exemplo, reatribuir tarefas a diferentes utilizadores ou permitir que os utilizadores retomem tarefas interrompidas. 10.2. Descrição da solução O UIPAB é baseado no modelo MVC (model-view-controller) que fornece uma forma para organizar sistemas que suportam múltiplas formas de apresentar os mesmos dados. Este modelo separa o processo de interface com o utilizador em três objectos diferentes: André Coutinho 77 Microsoft .NET – Application Blocks • • • Model. Este objecto conhece todos os pormenores dos dados a serem mostrados. Pode ser visto como a parte de processamento de um sistema do tipo input-process-output. View. Os views são usados para gerir a informação a ser mostrada aos utilizadores. É possível usar vários views para mostrar a mesma informação de diferentes formas. Pode ser visto como a parte de output de um sistema do tipo input-process-output. Controller. É usado para permitir que o utilizador interaja com a aplicação. Recebe o input do utilizador e passa instruções ao modelo. Pode ser visto como a parte de input de um sistema do tipo inputprocess-output. Figura 17: Funcionamento do Controller 10.3. Classes Factory A criação dos principais objectos no UIPAB usa o padrão factory, o que resulta no código de criação do objectos entralizado. Problema Todos os tipos de objectos a serem usados numa aplicação UIPAB estão identificados na configuração da aplicação. O código para criar cada uma destas classes encontra-se duplicado por todo o código de criação de objectos. André Coutinho 78 Microsoft .NET – Application Blocks Solução Para reduzir a duplicação de código e melhorar a manutenção deste, o código de criação de objectos é centralizado na classe GenericFactory, e é chamado para cada tipo específico. 10.3.1. Classe GenericFactory Esta classe é uma classe abstracta que fornece métodos para a criação de diferentes tipos de objectos. A classe GenericFactory centraliza todo o código de activação de objectos num único sítio, portanto todas as mudanças feitas a esse código só precisarão de ser feitas uma vez. Porque só é usada dentro do UIPAB está declarada, esta classe, como sealed class. 10.3.1.1. Métodos da classe GenericFactory A classe fornece dois overloads do método Create que carregam a assembly apropriada, baseando-se nos parâmetros passados ao método. Os overloads deste método suportam diferentes parâmetros, portanto é possível decidir como o assembly name e argumentos são passados. 10.3.2. Classe ControllerFactory A classe ControllerFactory é utilizada para criar uma instância da classe ControllerBase e devolvê-la ao código que a chamou. Usa o método GenericFactory.Create que contém o código usado para a sua activação. Foi desenhada para ser unicamente usada pelas outras classes do UIPAB, e como tal, está declarada como internal. 10.3.2.1. Métodos da classe ControllerFactory Esta classe contém um overload do método Create, que cria e devolve uma instância da classe ControllerBase à função que chamou o método. 10.3.3. Classe StatePersistenceFactory Classe usada para criar uma instância de uma implementação da interface A implementação a ser usada é identificada no ficheiro de configuração da aplicação. IStatePersistence. André Coutinho 79 Microsoft .NET – Application Blocks 11. Conclusão O estudo feito sobre os application blocks apresentado neste trabalho teve como objectivo, não só a exposição das suas classes, métodos, interfaces e propriedades mais importantes, documentando esta tecnologia, mas fazer uma avaliação de como tirar partido das capacidades de cada um, para integração em aplicações reais. Os cenários de utilização incluídos neste documento, mostram exemplos de como a integração em aplicações poderá ser feita, ou de como é possível tirar partido das capacidades dos application blocks. No caso do DAAB as suas capacidades de acesso e gestão de dados, tais como, a execução de Stored Procedures e comandos SQL, por exemplo, ou ainda a manipulação de Datasets, podem ser utilizadas em aplicações de comércio electrónico. O encapsulamento dos métodos mais comuns de acesso a dados permitem um desenvolvimento mais rápido das aplicações, uma vez que este tipo de aplicações requer a execução de updates a bases de dados, na gestão de clientes e produtos, por exemplo. Quanto ao LAB, este application block, facilita a implementação dos cenários mais comuns de logging, sendo portanto de grande utilidade a quase todo o tipo de aplicações existentes nos nossos dias. A gestão das excepções de logging deve ser feita de forma eficiente e consistente. Como tal, em conjunto com o LAB deverá ser usado o EMAB, que aumenta a robustez e facilita a depuração do código de uma aplicação, graças à encapsulação que faz dos métodos mais comuns de gestão das excepções de logging. A sua flexibilidade permite configurar as suas funcionalidades usando um simples ficheiro .NET XML. A utilidade do CMAB também é muito diversificada, uma vez que todos os tipos de aplicações, de alguma forma, adquirem, gerem e/ou armazenam informação. Este application block proporciona uma solução simples e flexível para a configuração de dados e aplicações. O UAB segundo o estudo realizado neste projecto poderá ser utilizado em organizações de média e grande dimensão, nomeadamente na actualização automática de todos os desktops ligados à rede da empresa. No que toca ao AAB, este applicaton block poderá ser usado em aplicações Web, nomeadamente em cenários onde a agregação de informação é necessária. Uma vez que a informação requerida pelos clientes pode ter diversas fontes, o AAB permite a fácil manutenção e junção de informação de vários Service Providers. O AAB incorpora o EMAB para o tratamento de excepções e pode ser usado com o AIAB e o CAB. O AIAB gere as comunicações assíncronas entre um cliente e um ou mais Foreign Service Providers. Quanto ao UIPAB, facilita o controlo do processo de interacção com o utilizador, podendo ser usado em aplicações Web e do tipo Wizard, nomeadamente para separar o fluxo de controlo das páginas e forms com que o utilizador interage, e para escrever aplicações capazes de gerir as tarefas do utilizador em cenários complexos, por exemplo. André Coutinho 80 Microsoft .NET – Application Blocks Apesar de existir espaço para melhoramentos, concretamente na implementação de uma aplicação exemplo que demonstrasse na prática cada um dos application blocks em funcionamento, integrados na referida aplicação. Para terminar considerou-se o trabalho concluído satisfatoriamente, tendo sido compreendidos os objectivos desta tecnologia da MicroSoft, e como podem ser usados na escrita de aplicações. André Coutinho 81 Microsoft .NET – Application Blocks 12. Bibliografia http://aspnet.4guysfromrolla.com/articles/062503-1.aspx - Introdução ao Data Access Application Block. http://aspnet.4guysfromrolla.com/articles/070203-1.aspx - Exemplos das vantagens do Data Access Application Block. http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnbda/html/daab-rm.asp - Documentação da Microsoft sobre o Data Access Application Block. http://builder.com.com/5100-6373-5054665.html - Exemplos de alguns métodos do Data Access Application Block. DAAB.chm – Documentação do Data Access Application Block, fornecida com o código do application block. http://scarecrow.fmrp.usp.br/~mavcunha/public/anti-patterns.php - Problemas de programação. http://www.dca.fee.unicamp.br/courses/POOCPP/node37.html - Sobre a encapsulação. http://www.clarionmag.com/col/98-03-reuse.html - Vantagens da programação orientada a objectos e reutilização de código. http://www.clarionmag.com/col/97-07-reusablecode.html - Vantagens da reutilização de código. http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnpag/html/logging-ch01.asp - Documentação da Microsoft sobre o Logging Application Block. Logging Block.chm - DAAB.chm – Documentação do Logging Application Block, fornecida com o código do application block. http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnbda/html/cmab.asp - Documentação da Microsoft sobre o Configuration Management Application Block. CMAB.chm - DAAB.chm – Documentação do Configuration Management Application Block, fornecida com o código do application block. http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnpag/html/CachingBlock.asp - Documentação da Microsoft sobre o Caching Application Block. Cache.chm - DAAB.chm – Documentação do Caching Application Block, fornecida com o código do application block. http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnpag/html/PAIBlock.asp - Documentação da Microsoft sobre o Asynchronous Invocation Application Block. Asyn.chm - DAAB.chm – Documentação do Asynchronous Invocation Application Block, fornecida com o código do application block. http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnpag/html/ServiceAgg.asp - Documentação da Microsoft sobre o Aggregation Application Block. Aggregation.chm - DAAB.chm – Documentação do Aggregation Application Block, fornecida com o código do application block. http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnbda/html/uip.asp - Documentação da Microsoft sobre o User Interface Process Application Block. André Coutinho 82 Microsoft .NET – Application Blocks UIPAB.chm - Documentação do User Interface Process Application Block, fornecida com o código do application block. EMAB.chm - DAAB.chm – Documentação do Exception Management Application Block, fornecida com o código do application block. http://msdn.microsoft.com/library/default.asp?url=/library/enus/dnbda/html/updater.asp - Documentação da Microsoft sobre o User Interface Process Application Block. UAB.chm - Documentação do Updater Application Block, fornecida com o código do application block. André Coutinho 83