António Cruz

Partilha de Experiências com .NET

My Links

Blog Stats

Archives

Login

Friday, March 16, 2007 #

Apresentações da Conferência Architect Insight

Já estão disponíveis para download as apresentações da Conferência Architect Insight:

http://www.microsoft.com/uk/msdn/architecture/architectinsight/2007download.mspx

António Cruz

 

posted @ 1:06 AM | Feedback (0)

Wednesday, March 07, 2007 #

Resumos da Conferência Microsoft Architect Insight 2007, II parte

Aqui ficam os links para os resumos que compilei do segundo (e ultimo) dia da Microsoft Architect Insight 2007:

- Conference Architect Insight 2007, Active or Passive Federation for the Enterprise
- Conference Architect Insight 2007, Identity Scale Federation
- Conference Architect Insight 2007, What Do Architects Do, Anyway?
- Conference Architect Insight 2007, Enterprise Architect Group Final Meeting
- Conference Architect Insight 2007, Solution Supply Chains

Antonio Cruz

posted @ 5:58 AM | Feedback (0)

Tuesday, March 06, 2007 #

Resumos da Conferência Microsoft Architect Insight 2007

Caros,

Publiquei os meus resumos do primeiro dia da conferência em epígrafe. Peço desculpa por estar tudo em inglês mas levava muito tempo a traduzir à medida que escrevo.

- Conference Architect Insight 2007, Introduction
-
Conference Architect Insight 2007, Enterprise Architect Group First Meeting
- Conference Architect Insight 2007, Where to place your SOA bets?
- Conference Architect Insight 2007, Clinics - The Future of Software Industry
- Conference Architect Insight 2007, Roadmap to Strategic SOA
- Conference Architect Insight 2007, SOA for Support and Maintenance
- Conference Architect Insight 2007, Service Capsules - A Language and Patterns Perspective on Service Design and Implementation
-
Conference Architect Insight 2007, SaaS As A Disruptive Technology
- Conference Architect Insight 2007, Enterprise Architect Group Second Meeting

Espero que isto seja interessante para alguém. Ainda falta o segundo dia, por isso amanhã há mais...

António Cruz

posted @ 3:59 AM | Feedback (0)

Saturday, February 10, 2007 #

Como fazer serviços REST com WCF

Muitas vezes usar SOAP não é a melhor escolha de protocolo para acesso a um serviço. Isto é tanto verdade como dizer que usar REST nem sempre faz sentido, é claro. Tal como reza a máxima: "There's no silver bullets".

O REST popularizou-se ligado a serviços de informação não-sensitiva (que não usem dados pessoais, pagamentos, etc.), comunitários e sem grandes preocupações de automatização do consumo e descoberta. O SOAP, por seu lado, tem ganho notoriedade por oferecer a possibilidade de ser usado no contexto de soluções empresariais que recorrem a segurança, transacções, reliable messaging, queing e federação.

Isto quer dizer que provavelmente vamos continuar a desenvolver serviços aos quais acedemos usando HTTP GET e HTTP POST ainda por muito tempo. O REST é uma manifestação do uso de HTTP GET e POST e foi associado ao AJAX e JSON mas ao contrário do que possa parecer à primeira vista, não se tratam de tecnologias antagónicas, mas sim complementares.

As funcionalidades empresariais enunciadas acima correspondem às 4 grandes vertentes do WCF tal como ele se apresenta na actual versão 1.0. Para a versão 2.0 do WCF, está já previsto o suporte a publish-subscribing e a descoberta de serviços. Neste post, vou dar um exemplo de implementação de REST em WCF. O objectivo é duplo:

1) Que quem se inicia no WCF não desespere por achar que é impossível ou por não conseguir fazer os serviços HTTP GET e POST que já fazia com .ASMX (ou outra tecnologia);

2) Que possa ter a percepção do WCF como a API unificada para o desenvolvimento de serviços (tudo aquilo que se encontrava estava disperso no .ASMX, WSE, Remoting, COM/COM+ e Message Queuing).

Segue-se um exemplo de um contrato que possibilita usar REST:

[ServiceContract]
public interface ICalculator
{
    [OperationContract(Action="*", ReplyAction="*")]
    Message Calculate(Message message);
}

Em primeiro lugar, é digno de nota é que tanto os parâmetros de input como o de output são do tipo Message. Traduzindo isto em linguagem menos "WCF-centric", temos: o método Calculate aceita qualquer tipo de input, o que faz deste parâmetro tão genérico como pode ser em WCF. O mesmo para o retorno: podemos devolver qualquer tipo de mensagem.

A razão disto é que o WCF encapsula automaticamente todo e qualquer pedido num objecto do tipo Message, por isso se dizemos que aceitamos Message e devolvemos Message é o mesmo que dizer "aceito qualquer coisa" e "devolvo qualquer coisa".

Em segundo lugar, note-se que tanto a propriedade Action como a ReplyAction do atributo OperationContract têm o valor "*". O WCF por default, usa SOAP. Isto quer dizer que espera receber um determinado valor num HTTP Header ou num elemento de WS-Addressing chamado "SOAPAction" e "Action", respectivamente. Se não queremos usar SOAP mas sim REST, então não queremos indicar qualquer Action/ReplyTo no momento da invocação, pelo que a equipa do WCF decidiu (e bem) que teria que haver uma forma de suportar mensagens não-SOAP. A forma que convencionaram é esta: o "*" faz com que o runtime não verifique o valor destes parâmetros e deixe a mensagem fluir para o interior da operação sem lançar a excepção: "The message with Action '' cannot be processed at the receiver".

Segue-se um serviço de exemplo de implementação do contrato analisado:

[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]
public class Calculator : ICalculator
{
    public Message Calculate(Message message)
    {
        HttpRequestMessageProperty messageProperty =
                message.Properties["httpRequest"] as HttpRequestMessageProperty;

            if (messageProperty != null)
            {
                if (messageProperty.Method.Equals("GET", StringComparison.OrdinalIgnoreCase))
                {
                    string[] segments = OperationContext.Current.IncomingMessageHeaders.To.Segments;

                    if (segments.Length > 2)
                    {
                        string operationName = segments[2].Replace("/", string.Empty).ToLower();

(...)

O serviço usa um ServiceBehavior que tem na propriedade AddressFilterMode o valor AddressFilterMode.Any. Isto quer dizer que não será analisado/validado o url usado para aceder ao endpoint do serviço. Por outras palavras, se podemos enviar qualquer payload *e* podemos usar qualquer tipo de sintaxe no url do serviço, temos o caminho aberto para usar REST com WCF.

Já no interior da operação, uso um HttpRequestMessageProperty para aceder ao pedido HTTP raw por via da propriedade httpRequest deste objecto. Assim, é fácil determinar qual o verbo HTTP que foi usado e agir em conformidade. Posso por exemplo processar os GET (select) de uma maneira e os POST (update) de outra, tal como convencionado em REST. E, é claro, processar qualquer outro verbo aceite pelo servidor da mesma forma (INSERT, APPEND, DEBUG, DELETE, etc.).

Finalmente, acedo a IncomingMessageHeaders para determinar o nome da operação. É interessante analisar os valores tanto de HttpRequestMessageProperty como de IncomingMessageHeaders, por isso sugiro que façam debug a esses objectos e naveguem pela informação que disponibilizam.

Este exemplo deixa claro como é fácil suportar REST em WCF. Considero boa prática suportar diversos protocolos de acesso à mesma funcionalidade, sempre que possível: por exemplo, suportar REST, JSON e SOAP, nesta altura, dá boas garantias de interoperabilidade com mashups de AJAX e deixa a porta aberta para o acesso via proxies de SOAP em C#, Java, PHP, Perl, etc.

António Cruz

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/antoniocruz]

 

posted @ 6:44 AM | Feedback (0)

Wednesday, February 07, 2007 #

WSDL Merger

O contrato WSDL gerado automaticamente pelos serviços desenvolvidos com WCF é regra geral, composto por 4 ficheiros: 2 .wsdl e dois .xsd, cada um deles disponível num url diferente. Uma necessidade que tenho sentido é a de disponibilizar o contrato do serviço em apenas um ficheiro, de modo a ser mais fácil distribuir e arquivar.

Depois de fazer o merge manual destes ficheiros algumas vezes, cheguei à conclusão que faz sentido usar um utilitário que faça a mesma tarefa por mim. O código que se segue não será provavelmente bullet-proof para todo e qualquer .wsdl que contenha referências, mas para os serviços que desenvolvi, funciona lindamente:

using System;
using System.Collections.Generic;
using System.Xml;

namespace MyCompany.ApplicationBlocks.Metadata
{
    public class WsdlMerger
    {
        private static XmlNamespaceManager _namespaceManager = null;

        public static string Merge(string mainWsdlUrlAddress)
        {
            _namespaceManager = new XmlNamespaceManager(new NameTable());
            _namespaceManager.AddNamespace("wsdl", "
http://schemas.xmlsoap.org/wsdl/");
            _namespaceManager.AddNamespace("xsd", "
http://www.w3.org/2001/XMLSchema");

            XmlDocument wsdl = Load(mainWsdlUrlAddress);
            ResolveReferences(wsdl, wsdl);

            Clean(wsdl);

            return wsdl.OuterXml;
        }

        private static XmlDocument Load(string urlAddress)
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(urlAddress);

            return xmlDocument;
        }

        private static void ResolveReferences(XmlDocument mainDocument, XmlDocument currentDocument)
        {
            XmlNodeList wsdlImports = currentDocument.SelectNodes("/wsdl:definitions/wsdl:import", _namespaceManager);

            foreach (XmlNode wsdlImport in wsdlImports)
            {
                string wsdlAddress = wsdlImport.SelectSingleNode("@location").InnerText;

                XmlDocument wsdl = Load(wsdlAddress);
                ImportWsdlNode(mainDocument, wsdl, wsdlImport);
                ResolveReferences(mainDocument, wsdl);
            }

            XmlNodeList xsdImports = currentDocument.SelectNodes("/wsdl:definitions/wsdl:types/xsd:schema/xsd:import", _namespaceManager);

            foreach (XmlNode xsdImport in xsdImports)
            {
                string xsdAddress = xsdImport.SelectSingleNode("@schemaLocation").InnerText;

                XmlDocument xsd = Load(xsdAddress);
                ImportXsdNode(mainDocument, xsd, xsdImport);
                ResolveReferences(mainDocument, xsd);
            }
        }

        private static void ImportWsdlNode(XmlDocument mainDocument, XmlDocument currentWsdlDocument, XmlNode wsdlImport)
        {
            XmlNodeList nodesToImport = currentWsdlDocument.SelectNodes("/wsdl:definitions/*[not(self::wsdl:types) and not(self::wsdl:import)]", _namespaceManager);

            foreach (XmlNode nodeToImport in nodesToImport)
            {
                XmlNode referenceNode = mainDocument.SelectSingleNode("/wsdl:definitions/wsdl:types", _namespaceManager);

                if (referenceNode == null)
                {
                    referenceNode = mainDocument.SelectSingleNode("/wsdl:definitions/wsdl:import", _namespaceManager);
                }

                string wsdlTargetNamespace = mainDocument.SelectSingleNode("/wsdl:definitions/wsdl:import/@namespace", _namespaceManager).Value;

                if (wsdlTargetNamespace != null)
                {
                    XmlNode targetNamespaceAttribute = mainDocument.SelectSingleNode("wsdl:definitions/@targetNamespace", _namespaceManager);

                    if (targetNamespaceAttribute != null)
                    {
                        targetNamespaceAttribute.Value = wsdlTargetNamespace;
                    }
                }

                XmlNode tnsPrefixNamespace = mainDocument.SelectSingleNode("/wsdl:definitions/namespace::tns", _namespaceManager);

                if (tnsPrefixNamespace != null)
                {
                    tnsPrefixNamespace.Value = wsdlTargetNamespace;
                }

                XmlNode importedNode = mainDocument.ImportNode(nodeToImport, true);
                mainDocument.SelectSingleNode("/wsdl:definitions", _namespaceManager).InsertAfter(importedNode, referenceNode);
            }
        }

        private static void ImportXsdNode(XmlDocument mainDocument, XmlDocument currentXsdDocument, XmlNode xsdImport)
        {
            List<string> namespacesToExclude = new List<string>();
            namespacesToExclude.Add("
http://schemas.microsoft.com/2003/10/Serialization/");
           
            XmlNode targetNamespace = currentXsdDocument.SelectSingleNode("/xsd:schema/@targetNamespace", _namespaceManager);

            if (targetNamespace != null && !namespacesToExclude.Contains(targetNamespace.Value))
            {
                XmlNodeList nodesToImport = currentXsdDocument.SelectNodes("/xsd:schema", _namespaceManager);

                foreach (XmlNode nodeToImport in nodesToImport)
                {
                    XmlNode importedNode = mainDocument.ImportNode(nodeToImport, true);
                    mainDocument.SelectSingleNode("/wsdl:definitions/wsdl:types", _namespaceManager).PrependChild(importedNode);
                }
            }
        }

        private static void Clean(XmlDocument wsdl)
        {
            XmlNode wsdlImportNode = wsdl.SelectSingleNode("/wsdl:definitions/wsdl:import", _namespaceManager);

            if (wsdlImportNode != null)
            {
                wsdl.SelectSingleNode("/wsdl:definitions", _namespaceManager).RemoveChild(wsdlImportNode);
            }
        }
    }
}

Aqui está um exemplo de consumo:

using System;
using System.IO;

using MyCompany.ApplicationBlocks.Metadata;

namespace WSDLMerger
{
    class Program
    {
        static void Main()
        {
            string fullWsdl = WsdlMerger.Merge("
http://mycompany/myservice.svc?wsdl");
            File.WriteAllText(@"C:\FullWSDL.wsdl", fullWsdl);
        }
    }
}

Podem facilmente ver que o código é susceptível de várias melhorias. A ideia foi disponibilizá-lo a quem possa ser útil desde já.

António Cruz

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/antoniocruz]

posted @ 2:27 AM | Feedback (0)

Friday, February 02, 2007 #

WCF Programmer's Toolbox

Enquanto programador, acabei por reunir um conjunto de utilitários e práticas que me permitem maior eficácia no desempenho das minhas tarefas de desenvolvimento, debugging e monitorização de serviços. Neste post vou identificar o conjunto de programas que tenho usado, bem como descrever aquela que penso ser a sua utilidade, de acordo com os objectivos que referi.

1) Svcutil.exe - não uso o Add Service Reference. Se apenas quero gerar um proxy para o serviço, dispenso a verborreia que obtenho quando uso o output.config gerado e dispenso a criação da pasta respectiva na solução. Na maioria dos casos, na configuração só preciso dos elementos ABC (address, binding e contract) e um ou outro parâmetro de tuning, como por exemplo o "maxReceivedMessageSize". Na verdade, considero uma boa prática *não* usar o Add Service Reference. Dito isto, às vezes dá jeito usar a feature como "truque" para obter automaticamente a lista de todos os parâmetros e respectivos valores por default, que por sua vez nem sempre são fáceis de identificar na documentação.

2) Microsoft Fiddler - Para simular clientes POX/REST usando o basicHttpBinding, esta ferramenta serve lindamente. É fácil manipular os headers e o body da mensagem enviada, descompacta a stream da resposta quando esta vem gzipped e mostra a comunicação em raw, isto é, sem formatação, o que às vezes também dá jeito, principalmente para detectar problemas no encoding da stream da resposta.

 3) tcpTrace - Há muitos sniffers de rede (por exemplo, o Ethereal é muito bom), mas esta aplicação serve bem quando só quero mesmo fazer um pedido com alteração mínima da configuração (só mudo o endpoint para ser aquele em que escuta o tcpTrace). Podia usar o SvcConfigEditor.exe e alterar o .config para obter a mesma informação em ficheiros, mas geralmente sou mais rápido a i) alterar o endpoint, ii) lançar o tcpTrace e iii) monitorizar *live* a comunicação.

 4) SoapUI - Entre outras, esta aplicação tem uma funcionalidade que aprecio bastante: dou-lhe um endpoint ou um ficheiro que contenha um WSDL e obtenho a listagem de operações e o respectivo conjunto de sample SOAP requests que posso imediatamente usar para provocar um pedido ao serviço fazendo "Play".

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/antoniocruz]

 

posted @ 5:27 AM | Feedback (2)

Thursday, February 01, 2007 #

WCF Starter's Roadmap

Por eventualmente ser de interesse para quem pretende iniciar o estudo do WCF, resolvi fazer este post. A ideia é identificar quais são os requisitos desejáveis para serem WCF starters e, de seguida, apresentar alguns recursos que considero de qualidade para quem deseja aprofundar conhecimentos na matéria:

Requisitos:

- Programação em .NET, especialmente componentes, usando interfaces, atributos, genéricos, threading, herança e transacções.
- Bons conhecimentos de arquitecturas distribuídas, em especial usando web services e/ou remoting e queuing, WSE e/ou .ASMX, COM e/ou DCOM.
- Conhecimentos dos core standards usados em web services: REST, JSON, XML, XML Namespaces, XML Schemas, WSDL, WS-Addressing.
- Conhecimentos funcionais de standards WS-*, particularmente WS-Security, WS-Reliable Messaging, WS-Transactions e WS-Trust.
- Compreensão de conceitos ligados a SOA: Orientação a Serviços, Messaging, Concorrência, Transacções Distribuídas, P2P, Federação, Single Sign-On.
- Familiaridade com design patterns como proxy, broker, publish-subscriber, data transfer object, adapter, façade e singleton.
- Muita vontade de aprender a usar a software factory template para desenvolvimento de serviços mais avançada que existe.

Recursos:

- Windows Communication Foundation Hands-On, Craig McMurtry, SAMS, 0672328771. Este livro é bastante acessível para quem começar a ler os primeiros textos sobre WCF. Não sendo exaustivo em nenhum tópico, apresenta o mérito de incluir um pouco de informação sobre cada tema relacionado com esta tecnologia, diversidade esta que nem sempre encontrei em outras fontes: P2P, CardSpace, Federação, REST, Custom Behaviors, Custom Transports, etc.
- Programming WCF Services, Juval Löwy, O'Reilly Media, 0596526997. Ainda só disponível em "Rough Cuts" na O'Reilly, este é provavelmente o melhor livro disponível até à data. Bastante orientado a aspectos práticos e com muitos exemplos de código reutilizável em projecto. A não perder: todos os exemplos de código disponível no site IDesign.net. Não comecem a desenvolver/testar uma nova funcionalidade a partir do zero, i. é, sem procurar se existe já um exemplo feito. Usando um exemplo como template temos acesso a um excelente ramp-up.
- Windows SDK. Disponibilidade obrigatória como Programmer's Reference no dia-a-dia.
-
http://wcf.netfx3.com/. Site da comunidade de WCF. Aqui temos acesso a apresentações, artigos, vídeos, webcasts, podcasts, exemplos de código e até acesso a fazer um Hands-On Labs on-line. Aqui, sugiro começarem por ler os whitepapers referidos e depois ver alguns videos, por exemplo.
-
WCF Forum. Este é o forum a ter em conta sobre WCF. Seguido atentamente por elementos da equipa de desenvolvimento, é fácil colocar uma dúvida que seja respondida rapidamente.
- Finalmente, o recurso mais importante: nós próprios! Não há *nada* que substitua a experimentação sucessiva e metódica das features e o passar por problemas em projecto participando na sua resolução. Isto quer dizer que devem tentar equilibrar o que vão lendo e ouvindo, com o que vão fazendo.

Com isto, espero ter ajudado alguém a iniciar-se no tema. Comentários, dúvidas e/ou sugestões, just say it.

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/antoniocruz]

posted @ 6:46 AM | Feedback (5)

Friday, August 18, 2006 #

Script#

Hmm, isto promete:

http://projects.nikhilk.net/Projects/ScriptSharp.aspx

posted @ 9:39 PM | Feedback (0)

Friday, August 04, 2006 #

XML2XSD

Recentemente necessitei de uma forma de gerar um XML Schema a partir de um ficheiro de XML. A ideia é poupar as pessoas de terem que fazer os XML Schemas desde o início, principalmente quado já têm um XML de exemplo e agora só necessitam de gerar o Schema correspondente. É de facto mais fácil e rápido começar por um XML Schema gerado a partir do XML, servindo como base e dessa forma só teremos que o alterar com o que pretendemos.

Em .NET, há várias ferramentas que fazem essa tarefa, incluindo o próprio Visual Studio, o utilitário XSD.exe que vem no SDK de .NET ou até no site gotdotnet.com também existe disponível para download uma aplicação de consola chamada Microsoft XSD Inference 1.0.

No entanto, o que necessitava era uma forma de disponibilizar esta funcionalidade a qualquer pessoa, em qualquer plataforma (Linux/Perl, Windows/Java, etc), e de preferência, usando uma aplicação browser-based, de modo a não ser necessário instalar qualquer aplicação, runtime, etc.

Usando a nova classe XmlSchemaInference da versão 2.0 do .NET Framework, foi fácil implementar a dita aplicação.

Para quem estiver interessado, aqui fica o source do .aspx e do respectivo code-behind:

<%

@ Page Language="C#" ValidateRequest="false" EnableViewState="false" AutoEventWireup="true" CodeFile="XML2XSD.aspx.cs" Inherits="XML2XSD" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<
html xmlns="http://www.w3.org/1999/xhtml">
<
head runat="server">
<title>XML2XSD</title>
<style type="text/css">
body {background-color: #eee; font: 0.8em Verdana, Arial, Helvetica;}
textarea {border: solid 1px #000;}
</style>
</
head>
<
body>
<form id="form1" runat="server">
<div>
<center>
<asp:Label ID="Message" runat="server" />
<div>Paste your XML here and click <asp:Button ID="Submit" Text="Deduce Schema(s)" runat="server" /> to generate the corresponding XML Schema(s):</div>
<p><asp:TextBox ID="XML" Rows="20" Columns="80" TextMode="MultiLine" runat="server" /></p>
<p><asp:PlaceHolder ID="XSDContent" runat="server" EnableViewState="false" /></p>
</center>
</div>
</form>
</
body>
</
html>

using System;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Xml;
using System.Xml.Schema;

public partial class XML2XSD : Page
{
   
protected void Page_Load(object sender, EventArgs e)
   
{
        
if (XML.Text.Length > 0)
       
{
            
using (MemoryStream inputStream = new MemoryStream(Encoding.UTF8.GetBytes(XML.Text)))
            
{
                
using (XmlReader xmlReader = XmlReader.Create(inputStream))
                {
                    
XmlSchemaInference xsdi = new XmlSchemaInference();
                    
XmlSchemaSet schemaSet = xsdi.InferSchema(xmlReader);

                     XSDContent.Controls.Clear();
                     
HtmlGenericControl textArea = null;

                    
foreach (XmlSchema schema in schemaSet.Schemas())
                    {
                        
using (MemoryStream outputStream = new MemoryStream())
                         {
                             
using (XmlTextWriter xmlWriter = new XmlTextWriter(outputStream, Encoding.UTF8))
                              {
                                   xmlWriter.Formatting =
Formatting.Indented;
                                   schema.Write(xmlWriter);
                                   outputStream.Position = 0;

                                  
using (StreamReader streamReader = new StreamReader(outputStream, true))
                                   {
                                       
textArea = new HtmlGenericControl("textarea");
                                        textArea.Attributes.Add(
"rows", "20");
                                        textArea.Attributes.Add(
"cols", "80");
                                        textArea.InnerText = streamReader.ReadToEnd();
                                        XSDContent.Controls.Add(textArea);

                                       
streamReader.Close();
                                   }

                                   xmlWriter.Close();
                               }

                               outputStream.Close();
                           }
                       }

                       xmlReader.Close();
                   }

                   inputStream.Close();
               }
           }
      }
}

 

posted @ 9:09 AM | Feedback (1)

Tuesday, August 01, 2006 #

JSON Power(ed) II

Há já algum tempo que penso nas implicações do uso das técnicas denominadas de AJAX nas arquitecturas service-oriented. Neste post, vou partilhar alguma da experiência conseguida entretanto.

Cenário: Necessitamos de pesquisar a base de dados de uma aplicação já em produção de forma a apresentarmos a informação com HTML, num browser. A aplicação apenas disponibiliza feeds de RSS, permitindo pesquisar a base de dados e paginar os resultados mediante parâmetros em querystring. Não é viável proceder a alterações na aplicação pelo que se prefere uma solução que não implique fazer alterações ao código existente.

Solução

a) Usar o HTML DOM para navegar o XML do RSS e ir construindo dinamicamente os elementos e atributos de HTML de que necessitamos para apresentar a informação. Apesar de dar bastante trabalho, esta abordagem tem a vantagem de ser cross-browser e não possuir dependências externas;

b) Usar XSLT em client-side (fazendo transformações no browser). Recorrendo a uma livraria como a Sarissa, será fácil transformar em HTML os RSS devolvidos pelo servidor. Também é possível usar o XSL para transformarmos o RSS em JSON (e a partir daí construír a apresentação). Este .xsl faz uma transformação genérica de XML para JSON, embora eu tenha detectado problemas com atributos e aparentemente as secções de CDATA não são suportadas;

c) Usar um broker, isto é, uma aplicação server-side que receba os pedidos que se destinam à aplicação de RSS e faça forward destes para essa aplicação. Porque todos os pedidos (e respectivas respostas) passam pelo broker, é fácil proceder à transformação das respostas em HTML (ou outro formato).

Na minha opinião, a melhor hipótese é a c). Aqui ficam as razões da escolha:

- O uso do padrão de brokering é pelo menos bastante recomendado (pessoalmente, acho obrigatório) em arquitecturas service-oriented. O decoupling decorrente da "intercepção" dos pedidos é só por si, razão suficiente para justificar a sua adopção, mas muitas outras vantagens são obtidas a partir da sua implementação.

- Embora não comparável ao leque de serviços oferecido por um ESB, um broker simples traz a vantagem de que tanto os clientes como os serviços não têm de se preocupar com os formatos dos respectivos pedidos e respostas: o problema das traduções pode sempre ficar a cargo do broker (v. o resto da solução).

Assumindo que optámos pelo broker, enuncio mais algumas hipóteses:

a) Usar um elemento de <script> estático que incluímos na página, cuja propriedade src aponta para o broker. O broker pode fazer a transformação do RSS para JSON usando qualquer linguagem em server-side (não precisa de ser XSLT, pode ser C#, Java, PHP, etc.). Para um exemplo de código que produz JSON a partir de XML usando C#, podem ver aqui. O único problema que até agora encontrei neste código foi o facto de (também) não suportar CDATA. Mas foi simples fazer uma alteração para suportar esse elemento e neste momento tenho uma versão que suporta CDATA, atributos, arrays, etc. (se alguém estiver interessado nesta versão basta fazer um post).

b) Usar o objecto HTTPRequest para fazer o pedido, embora neste caso ficamos sujeitos às restrições de pedidos cross-domain (a propósito, acho que devia chamar-se cross-server, porque não basta estarmos no mesmo domínio para funcionar). Uma solução para isto (mais uma vez) usar um broker no mesmo servidor da aplicação. Esta abordagem tem apenas o problema de termos que ter um broker a fazer de reverse proxy em cada uma das aplicações que pretendemos consumir (caso fossem várias);

c) Usar um elemento <script>, tal como descrito em a), mas criado dinamicamente, usando o HTML DOM. Para um exemplo da criação dinâmica, podem consultar este artigo. De notar que também neste caso é um pressuposto que o JSON seja criado no servidor;

d) Para os casos em que hajam muitos pedidos concorrentes ao servidor, o facto de serem feitas transformações de XML para JSON pode revelar-se problemático, devido ao overhead introduzido pelas transformações. Neste caso, sugiro usarem a abordagem descrita em c) para devolverem apenas o RSS encapsulado numa propriedade de um objecto de JSON e proceder à sua transformação para JSON, mas no cliente. Vamos ganhar com isto que a carga das transformações de XML para JSON são distribuídas por cada cliente, ficando o broker com a carga mínima de criar uma string que apenas contém o RSS pretendido. Para uma implementação de conversões entre XML e JSON usando JavaScript, podem ver aqui.

Face ao exposto, considerem que em alguns casos, não faz grande sentido desenvolvermos XML Web Services que sejam compatíveis com o Basic Profile 1.1 da WS-I, etc. Se a complexidade de o fazer é superior ao benefício alcançado, então a melhor opção pode encontrar-se num mero... RSS (no entanto, a opção de usar JSON em vez de XML também traz várias condicionantes conhecidas como ausência de schema que permita a validação das mensagens, ausência de contrato automatizável como o WSDL, etc.).

Penso que a abordagem descrita em d) condensa a melhor abordagem possível em termos da arquitectura Web 2.0 num cenário SOA, conforme o que ficou descrito. Também penso ter demonstrado o interesse (e actualidade) de pensarmos em que termos a Web 2.0 condiciona/potencia as arquitecturas service-oriented e a arquitectura de software de um modo geral.

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/antoniocruz]

 

posted @ 11:07 AM | Feedback (0)

Monday, July 31, 2006 #

JSON Power(ed)

Tenho feito alguns testes com JSON e brevemente espero usá-lo em produção. O JSON tem vindo a ganhar notoriedade e mesmo o ATLAS usa internamente um JSON Serializer

Há algum tempo subscrevi o grupo de JSON do Yahoo e tenho acompanhado uma discussão interessante sobre a possibilidade (necessidade?) de estender o JSON para suportar a identificação de vários tipos de dados primitivos noutras linguagens, como por exemplo o DateTime e até de alguns tipos de dados complexos, como o DataSet.

Para possibilitar essas features, há até quem proponha usar "tags" no JSON (!) e fazer um Schema que permita validar JSON. E apesar de solicitado, o Douglas Crockford não se pronunciou sobre estas questões. Quer dizer, pelo menos até este fim-de-semana, em que fez um post (ao que me pareceu deliberadamente lacónico), em que simplesmente anuncia ao newsgroup que o RFC do JSON foi publicado.

Da mesma forma que outros, não penso que o JSON deva ser estendido para suportar mais tipos de dados, mas concordo que possam haver outras especificações produzidas tendo como base o JSON, sob pena de tornarmos a utilização do JSON confusa pela falta de consistência entre implementações. Sugiro que agora que existe um RFC, vamos usá-lo para basear o nosso desenvolvimento e especialmente, vamos deixar-nos de suspirar pelas funcionalidades que advêm do uso de XML e XML Schemas, que continuam por seu lado a ser evidentemente insubstituíveis no seu contexto. Ou seja, defendo que usemos a ferramenta certa para cada tipo de trabalho.

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/antoniocruz]

posted @ 10:25 PM | Feedback (1)

Sunday, July 23, 2006 #

Best Practices-First Development

Actualmente existem várias abordagens que podemos usar para desenvolver serviços. Destacam-se duas:

1) Code-First Development: consiste em escrever o código de que vamos necessitar para implementar o serviço e/ou o cliente em primeiro lugar, e só depois de termos o código é que produzimos o WSDL do serviço. Regra geral em .NET e Java, este WSDL pode ser automaticamente produzido pelo framework a partir do código que escrevemos.

2) Contract-First Development: temos necessariamente de identificar em primeiro lugar os interfaces que vamos usar, antes de iniciar a escrita do código. Depois desta fase de identificação das operações (métodos) e respectivos parâmetros, estamos em condições de proceder à geração automática do código, tanto do serviço como do cliente, a partir do WSDL que produzimos (e é por isso que tem o nome de "Contract-First").

Tanto uma técnica de desenvolvimento como a outra possuem vantagens e desvantagens geralmente conhecidas, pelo que aqui fica apenas um resumo:

O Code-First favorece mais o Rapid Application Development do que o Contract-First. O Contract-First é geralmente mais moroso e exigente em relação a conhecimentos diversos, com destaque para XML Namespaces, XML Schemas e WSDL. Por outro lado, usando Contract-First, os serviços que produzimos tendem a ser potencialmente mais interoperáveis do que no Code-First. A razão de ser desta situação prende-se com o facto de que em Contract-First nos focamos mais nos interfaces que estamos a definir, e não nas potencialidades específicas de uma linguagem.

Nas SOA, um objectivo primário é a integração de sistemas garantindo a interoperabilidade dos serviços existentes, pelo que o Contract-First Development se apresenta à partida como a melhor opção. Isto significa que se começamos em primeiro lugar a pensar no XML Schema que vamos usar para definir um serviço, nos data types e na estruturação dos tipos complexos que vamos usar nesse schema, então estaremos no bom caminho para assegurar a interoperabilidade do nosso serviço.

Para ilustrar estas ideias, vou usar dois exemplos:

1) É necessário desenvolver um serviço que contém uma operação que aceita uma data-hora. Como implementar? Bom, se usarmos .NET, a primeira tentação é escrever um método que tem um DateTime como parâmetro, até porque em XML Schemas existe um dateTime. Tudo bem à partida, a operação fica a funcionar e por isso o serviço vai para produção. No entanto, algum tempo depois, somos informados que o nosso serviço vai necessitar de servir um parceiro da nossa organização que, por acaso, usa Java. Não há problema porque o cliente Java vai serializar sem problemas um DateTime e enviá-lo para o nosso serviço em .NET, certo? Ou haverá?

Resposta: podemos vir a ter problemas, porque ao contrário do .NET, em Java podemos atribuir o valor null a um objecto do tipo Date. Isto quer dizer que se o cliente Java enviar um Date com o valor null para o nosso serviço, o .NET não vai conseguir deserializar o pedido porque não tem maneira de o representar. Podem encontrar 3 possíveis workarounds para este problema recomendados pelo Simon Guest, aqui. Pessoalmente, também defendo a representação do dateTime numa string, desde que próxima do formato ISO 8601, e validada por uma expressão regular que conste no XML Schema do WSDL do serviço.

2) Fomos informados de que o nosso serviço necessita de mais uma operação, que terá que devolver uma lista de empregados que se encontra numa tabela da base de dados. Como existe já uma aplicação por camadas, somos levados a pensar que nos basta identificar qual o método do Data Layer que serve listas de empregados ao Business e só teremos que fazer um pequeno wrapper dessa funcionalidade, encapsulando-a na operação que nos foi solicitada. Neste processo, reparamos que o método em causa devolve um DataSet, mas como sabemos que o .NET Framework assegura a serialização automática de DataSets em XML, à partida não vamos ter qualquer problema de interoperabilidade... ou não?

Resposta: o DataSet é um objecto que só existe em .NET e apesar de ser serializável para XML, tem diversos problemas relacionados com a sua descrição em termos de XML Schemas e não é aconselhado o seu uso em parãmetros de serviços, a não ser nos casos em que conseguimos garantir que nunca vamos ter outros clientes que não sejam em .NET. Para uma descrição mais detalhada dos problemas de interoperabilidade do DataSet e uma possível solução, sugiro a leitura deste artigo do Aaron Skonnard.

Na minha opinião, em termos de desenvolvimento de serviços interoperáveis, faz de facto mais sentido desenvolvermos segundo as técnicas de Contract-First Development. No entanto, mesmo quando lidando directamente com XML Schemas em primeiro lugar, é aconselhável sermos bastante comedidos na utilização dos tipos disponíveis neste standard, sob pena de sacrificarmos a interoperabilidade dos nossos serviços, porque vamos caír no mesmo tipo de problemas do Code-First. 

Note-se que temos um potencial problema de interoperabilidade sempre que um toolkit de SOAP tem que comunicar com outra implementação. Isto quer dizer que, mesmo dentro da mesma plataforma (Java, por exemplo), podem haver problemas se os toolkits de SOAP utilizados por clientes e servidores Java-based forem diferentes. E é claro, assegurar a interoperabilidade entre plataformas distintas como Perl, PHP, C/C++ ou Python restringe-nos ainda mais o leque de possibilidades.

Dito isto, e embora pudesse parecer exagerada (mas espero que não, depois do que ficou exposto) aqui fica a minha recomendação para um mínimo denominador comum que garante a interoperabilidade dos serviços:

1) Usar apenas os tipos primitivos de XML Schemas string e boolean;

2) Definir todos os tipos complexos (uma Factura ou um Cliente são exemplos de tipos complexos) usando para a sua composição apenas os tipos simples referidos em 1);

3) Sempre que possível, validar todos os tipos contra a sua XML Schema restriction (expressão regular, minValue/maxValue, etc.). Esta validação é altamente recomendada para alguns dos tipos primitivos não contemplados em 1). Por exemplo: nonPositiveInteger, double, float, dateTime. "Obrigatório", para os tipos complexos como: EMail, Invoice, Payment, etc.

Para uma boa discussão deste tópico (incluindo alguns workarounds com .NET 2.0 que nos permitem estender a lista que referi aos tipos dateTime e int), sugiro a leitura deste post de Anil John.

Qual é a vossa opinião/experiência?

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/antoniocruz]

posted @ 11:27 AM | Feedback (0)

Saturday, July 22, 2006 #

SOA, ESB e BPM

Um pensamento interessante que encontrei num artigo do bpminstitute.org foi o seguinte:

"The absence of an ESB is the most readily identifiable sign of a BPMS that is not really layered on SOA."

Concordo que a existência de um ESB é um elemento fundamental para realizar uma arquitectura service-oriented. Por isso, também achei interessante esta opinião de que não podemos realizar um BPMS sem haver um ESB. É como se os ESB estivessem "no meio" do caminho, entre SOA e BPM.

De facto, depois de identificarmos os candidatos a serviços e procedermos à sua implementação de acordo com as boas práticas, ficamos com um conjunto mais ou menos alargado de entry points que temos que gerir individualmente.

E na verdade, enquanto temos meia dúzia de serviços, tudo vai bem sem ESB. O pior é quando começamos a ter dezenas ou até centenas de serviços para gerir: cada um deles vai ter necessidades de autenticação e autorização dos seus clientes, registo de pedidos e respostas efectuadas, protecção de pedidos mal-formados, cache para aumentar a sua disponibilidade e escalabilidade, etc.

É aqui que entram os ESB. Em vez de haverem clientes a invocar serviços directamente, existe uma espécie de "hub" ao qual podemos ligar para enviar e receber mensagens dos serviços. Este mecanismo simples, que na sua essência de broker promove a autonomia dos serviços, permite centralizar todas as funcionalidades descritas que eram da responsabilidade de cada serviço e afigura-se decisivo para obtermos a desejada redução drástica do tempo e complexidade do desenvolvimento, de cada vez que necessitamos de um novo serviço.

Aqui chegados, dir-se-ia que realizámos a arquitectura service-oriented. Mas não é assim.

Ao contrário do autor do artigo, não considero que SOA seja "IT-driven". A arquitectura service-oriented não começa, não acaba e não se traduz num projecto eminentemente técnico. Com um ESB que gere as interacções entre os serviços, temos uma ferramenta poderosa em que podemos basear o negócio de uma organização. Mas de que serve tamanho poder se estiver numa forma que não é directamente utilizável pelas pessoas que detêm a visão estratégica do negócio e que conhecem os seus processos e interacções?

Considero que o factor primordial num projecto SOA deve ser a participação. As arquitecturas service-oriented não são exclusivas do staff de IT. Pelo contrário, são os decisores de negócio quem devem dar a primeira e última palavra em SOA: no início, para nos dizer onde está o valor do negócio e que serviços é prioritário disponibilizar para assegurar esse valor; durante, para acompanharem todo o processo SOA, de modo a sentirem que se identificam com o projecto, que a sua visão é considerada e que a estratégia de desenvolvimento de baseia naquilo que consideram ser importante; e por fim, para procederem à avaliação da visão estratégica que formularam, por forma a aperfeiçoarem o que foi alcançado e se sentirem motivados em iniciar mais um ciclo do processo, com novos objectivos, mais ambiciosos, mais estruturados e com um espírito de equipa e pertença ainda maior, em suma.

A possibilidade de criar e alterar os processos de negócio de uma forma simples é há muito tempo uma ambição dos gestores. Mas só nos últimos anos começaram a proliferar algumas tentativas de dar uma forma estruturada e standardizar estas abordagens. Uma das linguagens mais importantes nesta área, o BPEL (ou BPEL4WS, como se prefira) tem assumido uma visibilidade crescente. Usando uma ferramenta visual geradora de BPEL, podemos facilmente criar novos serviços a partir da composição de outros serviços já existentes.

Cada processo desenvolvido com BPEL é ele próprio um serviço como qualquer outro, possuindo o seu próprio WSDL. Modelando os processos de negócio fundamentais de uma organização, podemos de seguida criar mais processos, de mais alto nível, porque vão usar os processos criados em BPEL como se fossem serviços individuais. E assim sucessivamente, como se fosse uma vista "em fractal" dos serviços, que no limite serão sempre encapsulados por mais serviços.

Em síntese, penso que a modelação dos processos de negócio é um objectivo natural das arquitecturas service-oriented, e não algo que vem à parte, como se fosse opcional. Mas concordo sem reservas que os ESB são uma peça fundamental sem a qual não realizamos nem uma coisa, nem a outra.

[Cross-Posted de http://www.arquitecturadesoftware.org/blogs/antoniocruz]

posted @ 10:41 AM | Feedback (0)

Thursday, September 23, 2004 #

Open Source .NET

Aqui estão dois links que estão carregados de código potencialmente útil para todas as áreas de desenvolvimento com .NET:

http://sharptoolbox.madgeek.com/
http://www.nantz.org/SushiWiki/wiki.aspx

Já agora, o segundo link é para o site do Brian Nantz, que é o autor dum livro recente (Agosto 2004) que se chama:

"Open Source .NET Development - Programming with NAnt, NUnit, NDoc and more".

Tanto os links que referi como o livro podem ser bastante úteis no que respeita a contextualizar e esclarecer a relação do MS .NET com o software Open Source, designadamente com o Mono.

 

posted @ 4:44 PM | Feedback (0)

Thursday, September 16, 2004 #

Execução transparente de processos

Para quem tenha a necessidade de executar um determinado processo programaticamente (por exemplo, correr um .vbs para registar um evento numa mailbox de exchange server), sem abrir qualquer janela de command line, e também não queira que o respectivo output seja visualizado, mas sim guardado numa variável, para posterior manipulação, aqui fica a dica:

Process process =

new Process();
process.StartInfo.FileName = fileName;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute =
false;
process.StartInfo.RedirectStandardOutput =
true;
process.StartInfo.CreateNoWindow =
true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

bool
runOk = process.Start();

if
(runOk)
{
      output = process.StandardOutput.ReadToEnd();
}

posted @ 12:17 PM | Feedback (0)