versão 1 - julho/2013
Tecgraf PUC-RioNovembro de 2013
API C# do SCS
versão 1 - julho/2013
A API do SCS
• A API da biblioteca SCS permite a construção de componentes– Manualmente– Através de descritores
• A API lida apenas com a representação local do componente– Encarrega-se apenas da construção– Não lida com a composição de vários componentes
(que pode ser distribuída)• A representação local de um componente
SCS-C# é dada pela interface ComponentContext
versão 1 - julho/2013
Criação de um Componente
• São necessários alguns passos:
1.Alterar facetas básicas (opcional)2.Implementar suas facetas (opcional)3.Criar o ComponentId4.Instanciar a representação local do
componente5.Adicionar facetas (opcional)6.Adicionar receptáculos (opcional)
versão 1 - julho/2013
Criação de um Componente
• Outra opção é utilizar descritores XML• Externalizam como deve ser a construção do
componente• Não é necessário recompilar o código para
mudar a configuração do componente• Os passos passam a ser:
• Descrever o componente em um arquivo XML• Implementar suas facetas (opcional)• Utilizar a API para construir o componente a
partir do arquivo XML
versão 1 - julho/2013
Implementação de uma Faceta
versão 1 - julho/2013
• Uma faceta SCS nada mais é do que uma implementação de uma interface CORBA
• O SCS adiciona algumas convenções, mas a implementação da interface se dá da mesma forma
public class MyFacetServant : MarshalByRefObject, MyFacet { }
Criação de Facetas
versão 1 - julho/2013
• Pode ser útil também ter um construtor que receba a representação local do componente ao qual essa faceta pertencerá
• Assim pode-se ter acesso a outras facetas e dados comuns ao componente como um todo
• Essa representação local é chamada de contexto do componente (interface ComponentContext)
• Esse construtor não é obrigatório, a não ser na construção por XML
Criação de Facetas
versão 1 - julho/2013
• Exemplo de faceta completapublic class MyFacetServant : MarshalByRefObject, MyFacet {
private ComponentContext _context;
public MyFacetServant(ComponentContext context) { _context = context; }
...}
Criação de Facetas
versão 1 - julho/2013
Representação Local do Componente (ComponentContext)
versão 1 - julho/2013
• Definição do ComponentId• Idêntico ao demonstrado na IDL
ComponentId cpId = new ComponentId( “MyComponentName”, 1, 0, 0, “.NET 4.0”);
Criação do ComponentId
versão 1 - julho/2013
• ComponentContext– Atua como um envólucro que facilita o uso do
componente como uma entidade única e não apenas um aglomerado de facetas e receptáculos• Representação local do componente• Acesso a todas as facetas (incluindo as básicas) e receptáculos• Guarda os metadados, como descrições de facetas e receptáculos e identificador do componente (ComponentId)•Implementação padrão dada pela classe DefaultComponentContext
O Componente C#
versão 1 - julho/2013
• ComponentContext – Diferença entre representações– Visão distribuída de um componente
– Em um ambiente distribuído (CORBA), existe apenas o que está no contrato da IDL (scs.idl)– O componente é essencialmente o conjunto das facetas IComponent, IReceptacles, IMetaInterface e facetas adicionais– NÃO existe uma “caixa” que agrupe essas facetas em um bloco, representando o componente como um todo– A faceta IComponent é o que mais se aproxima de representar o componente– Receptáculos não existem de fato, e são manuseados apenas através da faceta IReceptacles
– Visão local de um componente– Objeto ComponentContext une as facetas e receptáculos em uma única representação
O Componente C#
versão 1 - julho/2013
• ComponentContext– A classe DefaultComponentContext pode ser
estendida para adicionar outros dados de uso global no componente• Dados acessados por mais de uma faceta• Dados referentes ao componente em si, não a uma faceta específica• Métodos
O Componente C#
versão 1 - julho/2013
• public (Default)ComponentContext(ComponentId id)– Construtor que recebe o ComponentId– Adiciona automaticamente as facetas básicas (e já as
ativa no ORB)
• ComponentId GetComponentId()– Retorna o ComponentId fornecido no construtor
• IComponent GetIComponent()– Retorna o objeto CORBA referente à faceta IComponent do
componente
Interface C# ComponentContext
versão 1 - julho/2013
• void AddFacet(String name, String interfaceName, MarshalByRefObject servant)– Adiciona uma faceta ao componente– Ativa o objeto CORBA no ORB
• void RemoveFacet(String name)– Remove uma faceta do componente– Desativa o objeto CORBA no ORB
• void UpdateFacet(String name, MarshalByRefObject servant)– Desativa o objeto CORBA da faceta no ORB– Substitui o objeto CORBA pelo novo fornecido– Ativa o novo objeto CORBA no ORB
Interface C# ComponentContext
versão 1 - julho/2013
• void AddReceptacle(String name, String interfaceName, Boolean isMultiple)– Adiciona um receptáculo ao componente
• void RemoveReceptacle(String name)– Remove um receptáculo do componente
Interface C# ComponentContext
versão 1 - julho/2013
• IDictionary<String, Facet> GetFacets()– Retorna um dicionário com todas as facetas do componente,
associadas aos seus nomes– A classe Facet contém o objeto CORBA e os metadados
associados
• Facet GetFacetByName(String name)– Retorna uma faceta específica
• IDictionary<String, Receptacle> GetReceptacles()– Retorna um dicionário com todos os receptáculos do
componente, associados aos seus nomes
• Receptacle GetReceptacleByName(String name)– Retorna um receptáculo específico
Interface C# ComponentContext
versão 1 - julho/2013
• Como obter uma faceta a partir de outra?
• Como acessar uma dependência conectada a um receptáculo?
context.GetReceptacleByName(“ReceptacleName”).GetConnections()
context.GetFacetByName(“FacetName”).Reference
Exemplos
versão 1 - julho/2013
• Novas implementações podem ser fornecidas para as facetas básicas
• A implementação é feita normalmente como em qualquer faceta / objeto CORBA, basta seguir a interface desejada
• Após criar o ComponentContext, deve-se substituir o servant da faceta desejada pelo novo (método updateFacet)
Alteração de Facetas Básicas
versão 1 - julho/2013
ComponentId cpId = new ComponentId( “MyComponentName”, (byte) 1, (byte) 0, (byte) 0, “windows”);ComponentContext context = new DefaultComponentContext(cpId);MyIComponentClass myIC = new MyIComponentClass(context);context.UpdateFacet(“IComponent”, myIC);
Alteração de Facetas Básicas
versão 1 - julho/2013
Outras Funcionalidades
versão 1 - julho/2013
• É possível montar um componente a partir de um arquivo descritor em XML ‒ Substitui os passos demonstrados anteriormente
• O descritor deve conter no mínimo: ‒ Identificador do componente
• Pode-se fornecer, opcionalmente: ‒ Descrições de facetas (nome, interface, e classe a ser instanciada)
• A classe da faceta deve conter um construtor que receba apenas um ComponentContext como parâmetro
‒ Descrições de receptáculos (nome, interface, multiplex)‒ Classe a ser usada como ComponentContext
• A biblioteca fornece um arquivo xsd com o formato
XMLComponentBuilder
versão 1 - julho/2013
• Exemplo de arquivo XML
<?xml version="1.0" encoding="iso-8859-1" ?><scs:component xmlns:scs="tecgraf.scs.core"> <id> <name>MyComponent</name> <version>1.0.0</version> <platformSpec>.NET 4.0</platformSpec> </id> <facets> <facet> <name>MyFacet</name> <interfaceName>IDL:module/Interface:1.0</interfaceName> <facetImpl>mypackage.InterfaceImpl</facetImpl> </facet> <facet> <name>AnotherFacet</name> <interfaceName>IDL:module/AnotherInterface:1.0</interfaceName> <facetImpl>mypackage.AnotherInterfaceImpl</facetImpl> </facet> </facets></scs:component>
XMLComponentBuilder
versão 1 - julho/2013
• Exemplo de uso da API
…XmlTextReader reader = new XmlTextReader(args[0]);XmlComponentBuilder xmlBuilder = new XmlComponentBuilder(reader);ComponentContext context = xmlBuilder.build();…
XMLComponentBuilder
versão 1 - julho/2013
Aplicação Exemplo
versão 1 - julho/2013
Exemplo passo-a-passo
• Veremos um exemplo, passo-a-passo, de desenvolvimento de uma aplicação SCS usando C#
• Para desenvolver a aplicação, usaremos o IIOP.NET como ORB tanto para o cliente quanto para o servidor, e as bibliotecas do SCS
versão 1 - julho/2013
Exemplo de Aplicação
StockSeller
StockServer StockExchange
StockLoggerExchangePrinter
• O componente StockSeller implementa duas facetas: StockServer e StockExchange
• O componente StockSeller tem um receptáculo para componentes que implementema faceta ExchangePrinter
• O componente StockLogger implementa a faceta ExchangePrinter
versão 1 - julho/2013
Passo 1: Alterando a IDL
// StockMarket.idl// O módulo StockMarket consiste das definições// úteis para desenvolvimento de aplicações// que lidam com mercado de ações.module StockMarket {
...
// A interface ExchangePrinter é a interface que // representa uma impressora de negociações de ações. interface ExchangePrinter { // Imprime que houve uma negociação da ação indicada. // A saída utilizada para impressão não é especificada. // Exemplos de saídas: tela, arquivo, clients remotos. void print(in StockSymbol symbol); };
// A interface StockExchange é a interface que permite // a compra de ações. interface StockExchange { // Usa os componentes ExchangePrinter que estejam // conectados para imprimir a negociaçao efetuada. boolean buyStock(in StockSymbol symbol); };};
versão 1 - julho/2013
Passo 2: Implementando as facetas
• Facetas são interfaces CORBA e, portanto, sua implementação segue as mesmas regras que vimos nos exemplos de CORBA
• Precisaremos alterar a classe StockServerImpl para que ela se torne uma Faceta SCS
• Além disso, precisaremos implementar as facetas StockExchange e ExchangePrinter
versão 1 - julho/2013
StockServerImpl
namespace StockSeller { public class StockServerImpl : MarshalByRefObject, StockServer {
public StockServerImpl(StockSellerContext context) { }
/// <summary> /// Retorna o valor corrente de uma ação específica. /// </summary> /// <param name="symbol">Símbolo identificador da ação.</param> /// <returns>O valor da ação requisitada.</returns> public float getStockValue(string symbol) { }
/// <summary> /// Retorna os símbolos de todas as ações disponíveis. /// </summary> /// <returns>As ações disponíveis.</returns> public string[] getStockSymbols() { }
public override object InitializeLifetimeService() { } }}
versão 1 - julho/2013
StockExchangeImpl
namespace StockSeller { public class StockExchangeImpl : MarshalByRefObject, StockExchange {
public StockExchangeImpl(StockSellerContext context) { _context = context; }
/// <summary> /// Realiza a compra de uma ação. Quando uma ação é negociada, seu valor /// aumenta em uma taxa de 10%. /// /// Usa os componentes ExchangePrinter que estejam /// conectados para imprimir a negociaçao efetuada. /// </summary> /// <param name="symbol">Símbolo da ação a ser comprada.</param> /// <returns>True se a venda da ação foi bem sucedida, False caso contrário. /// </returns> public bool buyStock(string symbol) { }
public override object InitializeLifetimeService() { } }}
versão 1 - julho/2013
DisplayExchangePrinter
namespace StockLogger {
public class DisplayExchangePrinter : MarshalByRefObject, ExchangePrinter {
public DisplayExchangePrinter(ComponentContext context) { }
/// <summary> /// Imprime que houve uma negociação da ação indicada. /// A saída utilizada para impressão é a tela. /// </summary> /// <param name="text">Texto a ser impresso na tela.</param> public void print(string text) { }
public override object InitializeLifetimeService() { } }}
versão 1 - julho/2013
Passo 3: Criando o componente StockSeller
• O componente StockSeller oferece as facetas StockServer e StockExchange
• O componente StockSeller possui um receptáculo para conectar um ou mais compontes que implementem a faceta ExchangePrinter
versão 1 - julho/2013
Inicia o ORB
• A inicialização do ORB, ao iniciar o servidor, é igual à que já usamos nos exemplos anteriores.
• O servidor deve, após criar o componente, deixar ao menos uma thread ativa para que o ORB possa ficar aguardando as requisições
// inicializa o ORB, indicando que utilize uma porta aleatória para // receber chamadasIiopChannel chan = new IiopChannel(0);ChannelServices.RegisterChannel(chan, false);
...Thread.Sleep(Timeout.Infinite);
versão 1 - julho/2013
Criando o identificador do componente
• Todo componente precisa ter um identificador, que é descrito usando a classe ComponentId
ComponentId componentId = new ComponentId( "StockSeller", 1, 0, 0, “.NET 4.0");
versão 1 - julho/2013
Criando o componente
• A classe DefaultComponentContext deve ser instanciada para servir como representação local do componente (ou uma classe mais específica definida pelo usuário)
• Esse componente já conterá as facetas básicas do SCS
ComponentContext context = new DefaultComponentContext(componentId);
versão 1 - julho/2013
Criando e adicionando as facetas
// cria as facetasStockServerImpl stockServer = new StockServerImpl(context);StockExchangeImpl stockExchange = new StockExchangeImpl(context);
// adiciona as facetas ao componentestring serverType = Repository.GetRepositoryID(typeof (StockServer));string exchangeType = Repository.GetRepositoryID(typeof (StockExchange));context.AddFacet("StockServer", serverType, stockServer);context.AddFacet("StockExchange", exchangeType, stockExchange);
versão 1 - julho/2013
Adicionando receptáculos
// adiciona o receptáculostring printerType = Repository.GetRepositoryID(typeof (ExchangePrinter));context.AddReceptacle("ExchangePrinter", printerType, true);
versão 1 - julho/2013
Salva o IOR em um arquivo
• Nessa solução, ainda usaremos um arquivo para gravar a referência para o objeto CORBA
• Note que estamos usando a referência para um IComponent
OrbServices orb = OrbServices.GetSingleton();string ior = orb.object_to_string(context.GetIComponent());File.WriteAllText(args[0], ior);
versão 1 - julho/2013
Passo 4: Criando o componente StockLogger
• O componente StockLogger oferece a faceta ExchangePrinter
• O código StockLoggerMain.cs é responsável por criar o componente SCS StockLogger
• Em nosso exemplo, o código que o conecta ao componente StockSeller é uma aplicação separada
• A criação do componente StockLogger é similar ao que fizemos para criar o StockSeller, exceto que não há receptáculos
versão 1 - julho/2013
Passo 5: Recuperando as referências dos componentes para conectá-los
• A conexão pode ser feita por um dos componentes do sistema ou por um cliente externo
// Lê os IORs dos arquivos cujos nomes são passados como parâmetrosstring sellerIOR = File.ReadAllText(args[0]);string loggerIOR = File.ReadAllText(args[1]);
versão 1 - julho/2013
Obtendo as facetas IReceptacles do StockSeller e ExchangePrinter do StockLogger
• Precisaremos da faceta ExchangePrinter do componente StockLogger, pois é essa faceta que será conectada ao receptáculo do StockSeller
• A faceta IComponent tem métodos para recuperar suas outras facetas
ORB orb = OrbServices.GetSingleton();// Obtém as referências para os objetos CORBAIComponent sellerIC = orb.string_to_object(sellerIOR) as IComponent;if (sellerIC == null) {...}IComponent loggerIC = orb.string_to_object(loggerIOR) as IComponent;if (loggerIC == null) {...}
string receptaclesType = Repository.GetRepositoryID(typeof (IReceptacles));IReceptacles irStockSeller = sellerIC.getFacet(receptaclesType) as IReceptacles;if (irStockSeller == null) {...}string printerType = Repository.GetRepositoryID(typeof (ExchangePrinter));ExchangePrinter printer = loggerIC.getFacet(printerType) as ExchangePrinter;if (printer == null) {...}
versão 1 - julho/2013
Conectando a faceta ExchangePrinter do StockLogger no receptáculo do StockSeller
• Usamos a faceta IReceptacles de StockSeller para fazer a conexão dos componentes
• O método connect faz a conexão do componente StockLogger usando sua faceta ExchangePrinter
• O exemplo não mostra para simplificar, mas deve-se tratar as exceções CORBA como no exercício anterior em toda chamada remota CORBA, assim como exceções específicas do método
// Faz a conexão da faceta ExchangePrinter do componente StockLogger no// receptáculo do StockSeller
irStockSeller.connect("ExchangePrinter", printer);
versão 1 - julho/2013
Passo 6: Usando os componentes conectados ao receptáculo do StockSeller
• A implementação do método buyStock pela faceta StockExchange do componente StockSeller deve chamar o método print da faceta ExchangePrinter conectada a esse mesmo componente
versão 1 - julho/2013
Obtendo o receptáculo correspondente a ExchangePrinter
• O contexto do componente possui o método GetReceptacleByName que retorna um receptáculo
• A classe Receptacle permite então recuperar as descrições das conexões
• As descrições das conexões são recuperadas com o método GetConnections de Receptacle
• O campo objref de ConnectionDescription possui a referência para a faceta IComponent da dependência conectada
public bool buyStock(string symbol) { ... IList<ConnectionDescription> conns = _context.GetReceptacleByName( "ExchangePrinter").GetConnections(); foreach (ConnectionDescription conn in conns) {
try { ((ExchangePrinter)conn.objref).print( "Negociação da ação " + symbol + ". Novo valor: " + value);
}catch(...) {...}
} ...}
versão 1 - julho/2013
Passo 7: Usando o componente StockSeller
• O cliente usa o componente StockSeller através de suas facetas
• O StockMarketClient.cs possui a etapa de inicialização do ORB que já vimos nos exemplos anteriores
• O que muda é o uso das facetas do componente para invocar os métodos remotos
versão 1 - julho/2013
Obtendo a referência para o componente StockSeller
• O cliente recupera a referência para o componente StockSeller lendo do arquivo recebido por parâmetro
// Lê o IOR do arquivo cujo nome é passado como parâmetrostring ior = File.ReadAllText(args[0]);ORB orb = OrbServices.GetSingleton();// Obtém a referência para o objeto CORBAIComponent sellerIC = orb.string_to_object(ior) as IComponent;if (sellerIC == null) {…}
versão 1 - julho/2013
Obtendo as facetas StockServer e StockExchange do componente StockSeller
• Tendo o componente, as facetas StockServer e StockExchange são recuperadas, pelo nome ou pela interface
try { string serverType = Repository.GetRepositoryID(typeof(StockServer)); string exchangeType = Repository.GetRepositoryID(typeof(StockExchange)); StockServer server = sellerIC.getFacet(serverType) as StockServer; if (server == null) {...} StockExchange exchange = sellerIC.getFacet(exchangeType) as StockExchange; if (exchange == null) {...}}catch (…) {
…}
versão 1 - julho/2013
Invocando os métodos disponibilizados em cada faceta
• O cliente faz as chamadas aos métodos usando as respectivas facetas
try { // Obtém os símbolos de todas as ações string[] symbols = server.getStockSymbols(); // Mostra as ações com seus respectivos valores foreach (string symbol in symbols) { Console.WriteLine(symbol + " " + server.getStockValue(symbol)); } // Compra uma ação if (symbols.Length > 0) { String first = symbols[0]; Console.WriteLine("--Compra a ação :" + first); bool success = exchange.buyStock(first); if (success) { Console.WriteLine("--Ação " + first + " depois da negociação: " + server.getStockValue(first)); } else { Console.WriteLine("--Não foi possivel negociar a ação " + first); } }} catch (…) {…}