Serviço de Persistência - CORBA VERSUS COM

Autores: Lisiane Volpi Sipert - GPT

    Vidal Martins - GPT

Abstract

The subject of this article is the persistence service provided by distributed objects technology. We know that the players in this technology are OMG CORBA and Microsoft COM. So, this paper describes the persistence service on both of them and make some considerations about the different strategys.

Palavras-Chave

Persistence Service; CORBA; COM; Compound Files; Monikers;

Introdução

Os dois principais concorrentes na área de objetos distribuídos são o padrão CORBA, definido pela OMG, e o COM da Microsoft. Eles possuem diferentes estratégias para realizar a persistência dos objetos. Este artigo mostrará a arquitetura dos dois padrões e, no final, fará algumas considerações sobre os pontos fortes e os pontos fracos de cada um. Veremos primeiro como funciona o serviço de persistência em CORBA e, em seguida, em COM.

CORBA Persistent Object Service (POS)

Os principais componentes da arquitetura CORBA são os seguintes: object request broker (ORB), commom object services, commom facilities e application objects (figura 1).

Figura 1 - Arquitetura de Gerenciamento de Objetos da OMG

O ORB é o middleware que estabelece o relacionamento cliente/servidor entre os objetos. Usando um ORB, um objeto cliente pode invocar de forma transparente um método de um objeto servidor, o qual pode estar na mesma máquina ou em um lugar qualquer da rede.

Os commom object services são coleções de serviços executados a nível de sistema e agrupados em forma de componentes. Eles complementam a funcionalidade do ORB. Você os utiliza para criar um objeto, nomeá-lo e torná-lo disponível no ambiente. Atualmente, a OMG (Object Management Group) possui 16 serviços de uso comum especificados. Um deles é o Persistent Object Service (POS), tema principal desta seção.

As commom facilities são coleções de componentes que oferecem serviços diretamente aos objetos da aplicação. Note que a diferença marcante entre services e facilities está no nível em que cada um se aplica: os services estão intimamente relacionados ao ORB, enquanto as facilities estão intimamente relacionadas aos objetos da aplicação.

Concluindo a arquitetura básica do padrão CORBA, aparecem os application objects, também chamados de business objects, que como o próprio nome sugere, são os componentes específicos da aplicação destinada ao usuário final.

Quando o usuário final solicita um serviço para um objeto de negócio (objeto da aplicação), o atendimento a esse pedido normalmente requer a colaboração de vários objetos, os quais estão presentes nos diversos níveis dessa arquitetura.

A partir de agora, vamos analisar em detalhe como funciona o serviço de persistência CORBA. A figura 2 apresenta os principais componentes do POS e como eles estão relacionados.

Figura 2 - Os componentes do Serviço de Persistência de Objetos da OMG

Todos os objetos da aplicação que precisam manter seu estado armazenado de forma persistente devem herdar as características de PO (Persistent Object). A interface PO possui um atributo cujo tipo é PID (Persistent Identifier). O PID descreve a localização dos dados persistentes de um objeto e gera um identificador do tipo string para esses dados.

A interface PO permite que o cliente controle o relacionamento entre um objeto e seus dados persistentes, através de dois mecanismos: conexão e store/restore. O mecanismo de conexão estabelece um relacionamento direto entre o PO e seu estado persistente, de tal forma que, enquanto durar a conexão, qualquer mudança de estado em PO é imediatamente refletida na persistência. O outro mecanismo permite que o cliente movimente os dados entre o PO e o seu estado persistente, em ambas as direções, no momento em que achar conveniente, através das operações store e restore. Portanto, segundo a norma, as operações disponíveis na interface PO são as seguintes:

  • Connect: inicia uma conexão entre os dados presentes em PO e o local do datastore indicado pelo PID. Uma tradução literal para datastore poderia ser "armazém de dados". A norma utiliza essa expressão genérica justamente para enfatizar o fato de que o estado de PO pode ser armazenado usando qualquer tipo de tecnologia, tal como banco de dados SQL, banco de dados orientado a objetos, arquivo convencional, etc.
  • Disconnect: finaliza uma conexão entre os dados presentes em PO e o local do datastore indicado pelo PID.
  • Store: copia os dados persistentes do objeto (PO) em outra posição da memória e coloca-os no local do datastore indicado pelo PID. Embora esta descrição da operação store tenha sido traduzida a partir da norma publicada pela OMG, em nenhum momento a norma prevê uma invocação de PO para outros componentes do serviço de persistência, passando como parâmetro a cópia do estado de PO. O que ela propõe, ao explicar as outras interfaces envolvidas neste serviço, é que o próprio PDS (Persistent Data Service) solicite diretamente ao PO que ele externalize seu estado, quando necessário, respeitando um protocolo específico que os dois sejam capazes de compreender. Os protocolos especificados pela norma são DDO, ODMG-93 e DA.
  • Restore: copia os dados persistentes do objeto do local do datastore indicado pelo PID e insere-os no objeto. Neste ponto, aplicam-se as mesmas observações registradas para a operação store. A única diferença é que o restore, ao contrário do store, está trazendo os dados da persistência para a transiência, isto é, a transferência de dados está ocorrendo no sentido inverso da operação anterior e, por esse motivo, o PDS solicitará ao PO que internalize os dados que ele está recebendo segundo um protocolo que ambos são capazes de compreender.
  • Delete: exclui os dados persistentes, pertencentes ao objeto, do local do datastore indicado pelo PID.

Tivemos a oportunidade de implementar uma máquina de persistência, baseada no padrão especificado pela OMG, como parte do nosso projeto de conclusão do curso de pós-graduação em objetos distribuídos. Nessa ocasião, percebemos que o fato da OMG especificar apenas as interfaces dos serviços, exige que o implementador complemente o modelo para que tudo possa funcionar satisfatoriamente. Pois bem, em virtude daquilo que foi explicado anteriormente, decidimos ampliar a interface PO na nossa máquina de persistência, acrescentando as operações externalize e internalize. Os objetivos dessas operações são, respectivamente: extrair dados de PO segundo um protocolo preestabelecido, repassando-os ao PDS que se responsabiliza pelo armazenamento persistente desses dados; e inserir dados em PO, provenientes do PDS.

Já sabemos quais são os papéis de PID e PO na arquitetura do POS, mas afinal quem cria esses objetos? A resposta é simples: PIDFactory e POFactory, respectivamente.

A interface PIDFactory possui três operações: create_PID_from_key, create_PID_from_string e create_PID_from_string_and_key. Todas geram o mesmo resultado, um PID. A diferença está no parâmetro que elas recebem como entrada para a geração do identificador persistente.

A interface POFactory possui apenas uma operação, create_PO. Essa operação recebe como parâmetro de entrada um PID e uma referência para um POM (Persistent Object Manager). Assim sendo, quando um PO é criado deve-se atualizar imediatamente seu atributo PID e deve-se registrar imediatamente a referência do POM que será invocado nas operações de persistência.

Esta é a primeira vez que mencionamos a participação do persistent object manager (POM) no processo de persistência. Qual é o papel desse componente na arquitetura do POS? O POM provê uma interface uniforme para a implementação das operações de persistência de um objeto, independente do PDS utilizado; ou seja, devido à existência do POM e de um protocolo de comunicação entre PO e PDS, é possível trocar arbitrariamente o PDS que acessará o datastore, de forma "plug-and-play". Isto ocorre porque, ao receber uma requisição de operação de persistência, o POM roteia a requisição para um PDS capaz de suportar a combinação protocolo/datastore necessária para o objeto. Para que isso seja possível, o POM precisa saber quais são os PDSs disponíveis e quais combinações protocolo/datastore cada um deles suporta.

Já falamos inúmeras vezes sobre a participação do persistent data service (PDS) nas operações de persistência, mas ainda não formalizamos o seu papel na arquitetura do POS. O PDS é o componente responsável por interagir com o objeto (PO) para extrair e inserir dados nele, através de um protocolo e, também, por interagir com o datastore para inserir e extrair dados dele.

Figura 3 - Troca de Mensagens entre os Objetos dos POS

Observe novamente a figura 2. Note que existe uma interface chamada Datastore_CLI entre o datastore do tipo SQL Databases e o PDS que utiliza protocolo DDO. A Datastore_CLI é uma interface adequada para bases de dados orientadas a registros e para sistema de arquivos. Ela suporta sessões de usuário, conexões, transações e navegação através de itens de dados usando cursor. A especificação dessa interface, onde for apropriado, está consistente com os padrões X/Open CLI, IDAPI e ODBC.

Cenário para a criação de um objeto persistente

Nos sistemas orientados a objetos, é normal que uma transação seja realizada através da colaboração entre diversos objetos. A rede de comunicação que se forma nesse processo, muitas vezes torna difícil a compreensão do que está acontecendo realmente. A melhor forma de representar a interação dos objetos de um sistema é o diagrama de rastreamento de eventos.

A figura 3 resume toda a troca de mensagens que ocorre entre os objetos pertencentes ao POS quando a aplicação cliente solicita a criação de uma nova instância de objeto persistente. Esperamos, com esse diagrama, integrar os conceitos apresentados e resumir o funcionamento do persistent object service (POS).

Você deve ter estranhado a presença de um objeto Dictionary no modelo. Não foi falado nada sobre isso! Do que se trata? Mais uma vez, estamos diante de uma decisão de implementação ocorrida durante o desenvolvimento da nossa máquina de persistência no projeto de conclusão do curso de pós-graduação em objetos distribuídos. O Dictionary é um objeto que contém metadados sobre as classes do sistema, tais como class ID, nome da classe, nome da tabela que contém os dados persistentes dos objetos da classe, etc. A operação getClassID que aparece no cenário tem por objetivo recuperar o class ID de uma classe a partir do seu nome. A PIDFactory precisa dessa informação para gerar o próximo PID da classe, mas o que ela recebe da POFactory é o nome da classe.

COM Persistent Object Service

Os principais componentes da arquitetura COM são os seguintes: OLE's object bus (COM), compound document management, compound files, uniform data transfer e OLE automation (figura 4).

COM é um Object Request Broker (ORB) para uma máquina isolada. O broker COM lida apenas com objetos OLE (também chamados objetos Windows). Distributed COM (DCOM) é, na teoria, simplesmente uma extensão da implementação atual do COM.

Um compound document intermedeia as interações entre dois tipos de componentes: containers e servers. Os containers fornecem os locais e os servers são as "coisas" que residem nestes locais.

Figura 4 - Tecnologias que constituem OLE

O mecanismo de transferência de dados (uniform data transfer) permite que você exporte dados usando protocolos como clipboard, drag-and-drop, links ou compound documents. Os dados exportados podem ser arrastados e então colados ou soltos no mesmo documento, em um documento diferente, ou até em uma aplicação diferente.

O serviço OLE automation permite que componentes servers sejam controlados por clientes automatizados (também chamados controllers automatizados). Um controller automatizado é tipicamente direcionado por uma linguagem de script ou por uma ferramenta como Visual Basic.

Assim como acontece com o CORBA, o atendimento a um pedido de serviço em COM normalmente requer a colaboração de vários objetos. Uma vez que o foco deste artigo é o serviço de persistência, a partir deste momento vamos nos concentrar na solução Microsoft para o referido problema.

Assim como o Bento do OpenDoc, OLE provê armazenamento estruturado de tal forma que componentes desenvolvidos independen-temente possam salvar seus conteúdos dentro do mesmo arquivo. OLE chama isso de Structured Storage Architecture (Arquitetura de Armazenamento Estruturado). E assim como OpenDoc/CORBA, a arquitetura OLE provê mecanismos para os componentes salvarem seu estado de forma persistente e para os clientes encontrarem esses objetos persistentes e carregá-los para a memória. A encarnação comercial dessa arquitetura é chamada Compound Files ou DocFiles (nome mais antigo). A Microsoft tem anunciado que os compound files são os precursores do seu novo sistema de arquivos orientado a objetos.

Os elementos que compõem os serviços de persistência e de armazenamento estruturado do OLE são os seguintes: Compound Files, Persistent Objects e Monikers. Vamos estudar um pouco mais sobre cada um deles.

Compound Files

A estrutura de um compound file está representada na figura 5. É uma hierarquia que consiste de elementos análogos a diretorias chamados storages, e também de elementos análogos a arquivos chamados streams. Ambos, storages e streams, são objetos OLE. Eles são implementados usando as intefaces IStorage e IStream.

Um objeto storage pode conter outros objetos storage, bem como objetos stream. OLE permite que você crie qualquer número de storages e streams em um único compound file. Note que storages não contêm dados; os dados estão dentro dos streams. Cada compound file tem um storage raiz a partir do qual todos os outros elementos descendem. Um storage raiz representa o arquivo correspondente e implementa duas interfaces: IRootStorage e IStorage. IRootStorage provê uma única função, SwitchToFile, que você usa para associar um objeto storage com um arquivo. Se você trocar o arquivo correspondente ao storage raiz invocando essa função, você também está trocando todos os substorages e streams anexos. Finalmente, OLE implementa um objeto LockBytes que suporta a interface chamada ILockBytes. Ele é usado para representar um dispositivo físico.

A interface IStorage permite que você mova, copie, renomeie, crie, destrua e enumere elementos do storage. Um objeto storage não pode ele próprio armazenar dados definidos pela aplicação. Contudo, ele armazena os nomes dos elementos (storages e streams) que ele contém, ou seja, funciona como um diretório.

Duas funções especialmente úteis da interface IStorage são Commit e Revert. Você usa commit para comprometer todas as transações feitas em um objeto storage desde que ele foi aberto ou comprometido pela última vez. Isto é realmente uma transação aninhada, ou seja, as transações tornam-se visíveis para o storage pai e não serão comprometidas até que a transação pai comprometa. Se a transação pai for cancelada mais tarde, todas as transações subordinadas sofrem rollback. Revert discarta qualquer alteração feita no storage, ou quaisquer alterações tornadas visíveis por commits aninhados, desde a última vez que o object storage foi aberto ou comprometido. Você pode acessar qualquer elemento OLE Storage em modo direto ou em modo transacional. Um único compound file pode conter uma mistura de storages em modo direto e em modo transacional.

Através da interface IStream você sempre vê o stream como um array contínuo de bytes. Cada stream mantém seu próprio ponteiro para os dados que ele contém. As funções membro de IStream permitem que você procure uma posição dentro do stream e então leia, grave ou copie um bloco de bytes. O método Clone permite que você crie um novo objeto stream para trabalhar com o mesmo array de bytes, mas gerencia um ponteiro independente.

Figura 5 - A Estrutura de um Compound File

SetSize pré-aloca espaço para o stream mas não impede gravação fora desse espaço. LockRegion restringe o acesso a um intervalo de bytes do stream. Commit e Revert provêm suporte à transação. Streams dentro de um compound file podem ser muito úteis porque permitem que você abra streams como se fossem arquivos, porém sem requerer um file handle por abertura. Apenas o objeto storage raiz requer um file handle, todo o restante do conteúdo de um compound file é simplesmente uma estrutura em memória.

Vale lembrar que uma arquitetura não é um produto, ou seja, a implementação corrente de uma arquitetura pode não oferecer determinadas funcionalidades previstas no projeto. Neste caso específico, os streams não são nem transacionais nem bloqueáveis (LockRegion, UnlockRegion e Revert não funcionam). O Commit simplesmente faz um flush do estado interno do stream. Além disso, arquitetura permite streams de até 264 bytes (16.777.216 Tb), mas a implementação corrente está limitada a 232 bytes (4 Gb). Finalmente, pequenos streams podem ser ineficientes porque a unidade mínima de alocação é 512 bytes. O lado positivo é que os objetos storage implementam completamente todas as funções presentes em IStorage, exceto SetStateBits. Contudo, algumas das funções são lentas, como por exemplo EnumElements e MoveElementTo.

A figura 6 apresenta um cenário de acesso a um storage. Trata-se da gravação de um stream em um novo compound file. A OLE API StgCreateDocFile cria um novo compound file e o respectivo storage raiz. Se o arquivo já existisse poderia ser chamada a API StgOpenStorage.

Cada componente independente deve gerenciar os conteúdos dos seus próprios objetos storage, sem envolver uma aplicação controladora. Além disso, qualquer componente com autorização apropriada pode navegar pela hierarquia de elementos de um compound file.

Figura 6 - Cenário: Gravando um Stream em um novo Compound File

Persistent Objects

Um objeto OLE persistente é aquele capaz de ler e gravar seu estado em um storage. Usando OLE, os clientes fornecem ponteiros para interfaces IStorage e IStream aos seus servidores. Os servidores usam esses ponteiros para armazenar e recuperar dados persistentes.

Um objeto pode suportar mais de uma interface de persistência para adequar-se a diferentes contextos de armazenamento. As interfaces disponíveis, que podem ser combinadas de qualquer maneira, são as seguintes:

Figura 7 - Interfaces de Persistência do OLE

  • IPersistStorage: o objeto pode ler e gravar seu estado persistente em um objeto IStorage. O cliente fornece ao objeto um ponteiro para um objeto storage através desta interface.
  • IPersistStream: o objeto pode ler e gravar seu estado persistente em um objeto IStream. O cliente fornece ao objeto um ponteiro para um objeto stream através desta interface. Neste caso, o objeto lê e grava suas informações dentro de um único stream.
  • IPersistFile: o objeto pode ler e gravar seu estado persistente em um arquivo, diretamente no sistema de arquivos correspondente. Esta interface normalmente não usa compound files. Ela não sabe nada sobre IStorage ou IStream. O cliente simplesmente fornece ao objeto um nome de arquivo e solicita a gravação ou leitura do seu conteúdo.

Os clientes perguntam aos servidores se eles suportam alguma das interfaces descritas acima usando IUnknown::QueryInterface

A figura 7 mostra a hierarquia de classes das interfaces de persistência OLE. É isso mesmo, herança em OLE! IPersist e IUnknow estão entre os poucos exemplos conhecidos de uso de herança em OLE.

A função InitNew, pertencente à interface IPersistStreamInit, permite que um objeto stream saiba quando ele foi criado pela primeira vez. Os controles OLE precisam desta chamada para diferenciar uma nova inicialização de uma inicialização baseada em dados existentes. IsDirty verifica se o objeto sofreu alteração desde a última vez que foi salvo. GetSizeMax retorna o tamanho máximo do stream que um objeto precisaria para salvar seu estado. Isto permite que o cliente configure o tamanho do stream antes de invocar um Save. SaveCompleted informa ao objeto que o cliente completou um save e que o objeto pode reabrir o file, o stream, ou o storage. HandsOffStorage solicita ao objeto que ele libere todos os storage pointers, inclusive aqueles passados pelo InitNew ou pelo Load. GetCurFile recupera o caminho (path) do arquivo atualmente associado a um objeto e faz o cliente responsável pelo armazenamento.

A figura 8 mostra um cenário de uso de um objeto persistente. O cenário começa com um cliente controlando um storage raiz que possui um substorage chamado "MyStorage". O cliente instanciará um objeto que suporte a interface IPersistStorage e entregará a ele um ponteiro para o substorage. Veja, a seguir, os passos do cenário:

  1. O cliente invoca a função IPersistStorage::Load instruindo o objeto para carregar seus dados persistentes a partir de "MyStorage". O cliente passa o ponteiro para esse objeto.
  2. Antes do objeto usar o ponteiro para IStorage, ele tem que invocar IUnknown::AddRef para incrementar o Reference Count que controla o ciclo de vida do ponteiro.
  3. O objeto pode abrir os streams pertencentes a "MyStorage", carregar seu estado persistente para a memória, gravar nos streams, criar e abrir novos substorages, etc.
  4. O cliente chama a função IPersistStorage::Save instruindo o objeto para salvar seus dados persistentes no storage corrente.
  5. Neste cenário o storage é transacional, portanto o objeto simplesmente invoca IStorage::Commit para tornar persistentes suas operações de escrita. O objeto continua mantendo seus ponteiros para os elementos abertos, embora ele não possa gravar nesses elementos até que receba um SaveCompleted do cliente.
  6. O cliente invoca IPersistStorage:: SaveCompleted para indicar que a operação está completa. Por que esta chamada é necessária se é o próprio objeto quem realiza o save? Porque mais de um objeto pode estar envolvido em um processo de save, no qual o cliente age como coordenador.
  7. O objeto retorna para o estado normal e pode fazer novamente o que ele quiser com o storage.

Figura 8 - Cenário: Trabalhando com Objetos Persistentes

Monikers

Um moniker é um objeto que age como um apelido persistente para outros objetos. Monikers podem prover apelidos para nomes de arquivos distribuídos, consultas a bancos de dados, um parágrafo em um documento, um intervalo de células em uma planilha eletrônica, um computador remoto, etc. O nome torna-se um "objeto moniker" que implementa interfaces relacionadas a nomes.

A interface IMoniker inclui a interface IPersistStream. Isto significa que os monikers podem ser lidos e gravados em streams. Os atributos de um moniker são seu nome e o CLSID da sua implementação. Cada classe de moniker pode armazenar dados arbitrários. OLE implementa 5 classes de moniker: generic composite, file, item, anti e pointer. Essas 5 classes são simplesmente implementações polimórficas da interface IMoniker. Você pode criar seus próprios monikers implementando a interface IMoniker e associando-a a um único CLSID.

Resumidamente, IMoniker apenas define as interfaces que cada objeto moniker tem que suportar. Essas interfaces definem operações genéricas necessárias para localizar algum tipo de objeto e para realizar algum tipo genérico de ação. Cada classe moniker, identificada por um CLSID, define o código que implementa essas funções de uma maneira específica. Cada instância de moniker mantém uma representação persistente do seu próprio nome e de outros parâmetros. Finalmente, os clientes trabalham com monikers usando a interface IMoniker.

Os monikers foram criados para solucionar um problema do OLE 1: hardcoded links. Em OLE 1, os links eram armazenados usando pathnames absolutos e poderiam quebrar toda vez que alguém mudasse o documento fonte de lugar. Os monikers adicionaram mais um nível de indireção que solucionou parcialmente o problema. Contudo, indireções podem ser perigosas porque uma vez introduzidas, podem ser usadas para resolver qualquer tipo de problema.

Por exemplo, quando OLE 2 se deparou com outro tipo de problema, "como lidar com dados aninhados?", a solução foi usar moniker, é claro. A idéia da Microsoft é fazer tudo passar por um nível extra de indireção, de tal forma que ela possa solucionar os problemas à medida que eles são descobertos.

Em contrapartida, CORBA define o serviço de relacionamento para fazer esse tipo de coisa de uma maneira menos ad hoc. No CORBA, cada objeto tem uma identidade única e um estado. O estado pode ser persistido usando um Persistent ID.

Usando COM você obtém um ponteiro para uma interface e não um ponteiro para um objeto que possui estado. Assim sendo, como os clientes COM instanciam um objeto e mais tarde reconectam com esse mesmo objeto? Com monikers, é claro. Os monikers tornam-se remendos para a falta de suporte a identificadores de objetos (OIDs) do OLE. Uma vez introduzido um nível de indireção que permite chamar algum código ad hoc, o céu é o limite para o que se pode fazer com isso. Em resumo, os monikers permitem que você defina qualquer nome, associe-o com algum código de propósito geral e use-o para realizar algum bindind misterioso.

Conclusão

Assim como quaisquer tecnologias concorrentes, CORBA e COM têm semelhanças e diferenças. Comecemos pelas semelhanças. Ambas as tecnologias:

  • separam interface e implementação;
  • fornecem API para descobrir dinamicamente quais interfaces um objeto exporta, como carregá-las e como invocá-las;
  • exigem que todo código compartilhado seja declarado usando interfaces de objeto.

As diferenças:

  • COM provê somente facilidades de RPC local (Lightweitht RPC – LRPC) porque não suporta invocação remota ou objetos distribuídos. Vale lembrar que DCOM suporta;
  • O modelo de objetos COM não suporta herança múltipla. O ambiente COM é inerentemente flat (plano). A herança é obtida através de uma rede de ponteiros que ligam ou agregam diferentes interfaces;
  • Usando COM você obtém reuso de objetos via containment e agregação ao invés de herança. Um objeto COM não é um objeto no sentido OO;
  • As interfaces COM não têm estado e não podem ser instanciadas para criar um único objeto. Uma interface COM é simplesmente um grupo de funções relacionadas. Os clientes COM recebem um ponteiro para acessar as funções de uma interface. Este ponteiro não está associado a informações de estado. Um cliente COM não pode reconectar-se exatamente à mesma instância de objeto com o mesmo estado, em um momento futuro. Ele pode somente reconectar-se a um ponteiro de interface da mesma classe. Em outras palavras, objetos COM não mantêm estado;
  • Uma interface COM é definida como uma API binária de baixo nível baseada em uma tabela de ponteiros. Em contrapartida, o CORBA provê language bindings de alto nível para linguagens como SamllTalk, C++, Cobol, etc. Para acessar uma interface, os clientes COM usam ponteiros para um array de ponteiros de função, conhecido como virtual table (vtable). Cada objeto COM tem uma ou mais vtables que definem o contrato entre a implementação do objeto e seus clientes;
  • COM é gratuito, CORBA não;
  • O serviço provido pelo COM é rudimentar, mas está implementado. CORBA é sofisticado, mas nenhum fornecedor implementou, nem vai implementar, pois a OMG reconhece abertamente a sua pouca aceitação e já está definindo uma nova versão para esse padrão.

Ambas as tecnologias continuam evoluindo no que diz respeito ao serviço de persistência. De um lado a OMG está revendo sua estratégia, e de outro a Microsoft está introduzindo uma nova proposta, OLE-DB. OLE-DB é um conjunto de interfaces que oferece às aplicações acesso uniforme aos dados armazenados em diversas fontes.

"Muita água ainda vai rolar por debaixo dessa ponte!"

Referências Bibliográficas:

ORFALI; Robert; HARKEY; Dan; EDWARDS, Jeri. The essential distributed objects survival guide. New York: J. Wiley, 1996.

GRIMES, Richard. Professional DCOM programming. Wrox Press, 1997.

OBJECT MANAGEMENT GROUP. CORBA services: commom object services specification 2.0. OMG.

MICROSOFT. OLE DB. Disponível na Internet.

http://www.microsoft.com