API Java do SCS Tecgraf PUC-Rio Setembro de 2013 A API do SCS • A API fica encapsulada em uma classe chamada ComponentBuilder, utilizada para “montar” um componente • Definindo suas facetas, receptáculos e ComponentId • Um componente SCS-Java é representado por uma interface Java ComponentContext • Existem classes auxiliares 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 ComponentId 4. Instanciar a representação local do componente 5. Adicionar facetas (opcional) 6. Adicionar receptáculos (opcional) 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 Implementação de uma Faceta Criação de Facetas • 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 • Por herança ou delegação public class MyFacetServant extends MyFacetPOA { } Criação de Facetas • 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 (classe ComponentContext) • Não é obrigatório, a não ser na construção por XML Criação de Facetas • Método _get_component() – Inicialmente criado para o CCM e CORBA 3.0 – Presente na especificação de CORBA 2.3.x • Suportado em Java pelo JacORB mas não pelo ORB da Sun – Fornece a interface mais básica de um componente, a partir de qualquer outra de suas interfaces. Isso geralmente permite obter uma interface que acesse outras interfaces • No SCS, a interface mais básica é a IComponent, da qual pode-se obter as outras facetas do componente Criação de Facetas • Definição do método _get_component() – Basta retornar o resultado do método auxiliar getIComponent existente na interface ComponentContext //@Override public org.omg.CORBA.Object _get_component() { return context.getIComponent(); } – Opcional, mas uma mensagem de aviso será impressa caso o método não seja implementado – Possibilidade do método ser adicionado automaticamente no futuro • Desistimos da idéia por enquanto pois estamos investigando outras soluções para versões futuras do SCS Criação de Facetas • Exemplo de faceta completa public class MyFacetServant extends MyFacetPOA { private ComponentContext context; public MyFacetServant(ComponentContext context) { this.context = context; } //@Override public org.omg.CORBA.Object _get_component() { return context.getIComponent(); } ... } Representação Local do Componente (ComponentContext) Criação do ComponentId • Definição do ComponentId • Idêntico ao demonstrado na IDL ComponentId cpId = new ComponentId( “MyComponentName”, (byte) 1, (byte) 0, (byte) 0, “windows” ); O Componente Java • 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) O Componente Java • ComponentContext – A classe 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 Classe Java ComponentContext • public ComponentContext(ORB orb, POA poa, ComponentId id) – Construtor que recebe o ORB e POA a serem utilizados nas facetas do componente e o ComponentId – Adiciona automaticamente as facetas básicas (já ativadas no POA fornecido) • public ComponentId getComponentId() – Retorna o ComponentId fornecido no construtor Classe Java ComponentContext • public void addFacet(String name, String interfaceName, Servant servant) – Adiciona uma faceta ao componente – Ativa o objeto CORBA no POA • public void removeFacet(String name) – Remove uma faceta do componente – Desativa o objeto CORBA no POA • public void updateFacet(String name, Servant servant) – Desativa o objeto CORBA da faceta no POA – Substitui o objeto CORBA pelo novo fornecido – Ativa o novo objeto CORBA no POA Classe Java ComponentContext • public void addReceptacle(String name, String interfaceName, boolean isMultiplex) – Adiciona um receptáculo ao componente • public void removeReceptacle(String name) – Remove um receptáculo do componente Classe Java ComponentContext • public Collection<Facet> getFacets() – Retorna uma coleção com todas as facetas do componente – A classe Facet contém o objeto CORBA e os metadados associados • public Facet getFacetByName(String name) – Retorna uma faceta específica • public Collection<Receptacle> getReceptacles() – Retorna uma coleção com todos os receptáculos do componente • public Receptacle getReceptacleByName(String name) – Retorna um receptáculo específico Classe Java ComponentContext • public IComponent getIComponent() – Retorna a faceta IComponent do componente • public String getComponentIdAsString() – Retorna as informações do ComponentId em uma string • public POA getPOA() – Retorna o POA fornecido no construtor e utilizado na ativação dos objetos CORBA do componente • public ORB getORB() – Retorna o ORB fornecido no construtor Exemplos • Como obter uma faceta a partir de outra? context.getFacetByName(“FacetName”).getServant() • Como acessar uma faceta externa conectada a um receptáculo? context.getReceptacleByName(“ReceptacleName”).get Connections() Alteração de Facetas Básicas • 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) Outras Funcionalidades XMLComponentBuilder • É 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 XMLComponentBuilder • 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>Java 1.6</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 • Exemplo de uso da API … ORB orb = ORB.init(args, orbProps); POA poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA")); poa.the_POAManager().activate(); XMLComponentBuilder xmlBuilder = new XMLComponentBuilder(orb, poa); File is = new File("resources/" + args[0]); ComponentContext context; try { context = xmlBuilder.build(is); } catch (SCSException e) { e.getCause().printStackTrace(); return; } … Aplicação Exemplo Exemplo passo-a-passo • Veremos um exemplo, passo-a-passo, de desenvolvimento de uma aplicação SCS usando Java • Para desenvolver a aplicação, usaremos o JacORB como ORB tanto para o cliente quanto para o servidor, e as bibliotecas do SCS Exemplo de Aplicação ExchangePrinter StockSeller StockServer StockLogger Stock Exchange • O componente StockSeller implementa duas facetas: StockServer e StockExchange • O componente StockSeller tem um receptáculo para componentes que implementem a faceta ExchangePrinter • O componente StockLogger implementa a faceta ExchangePrinter 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); };}; 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 StockServerImpl /** * StockServerImpl implementa a interface IDL StockServer */ public class StockServerImpl extends StockServerPOA { /** * Construtor * @param context o contexto do componente dono da faceta. */ public StockServerImpl(ComponentContext context) { } /** * Retorna o valor corrente a determinada ação do mercado, dado o * símbolo que a representa. */ @Override public float getStockValue(String symbol) { } /** * Retorna a sequência com todos os símbolos que definem as ações * de mercado mantidas por esse StockServer. */ @Override public String[] getStockSymbols() { } } /** * Obtém a faceta IComponent do componente. * @return a faceta IComponent */ public org.omg.CORBA.Object _get_component() { } StockExchangeImpl /** * StockExchangeImpl implementa a interface IDL StockExchange */ public class StockExchangeImpl extends StockExchangePOA { /** * Construtor * * @param context o contexto do componente dono da faceta. */ public StockExchangeImpl(ComponentContext context) { } /** * Quando uma ação é negociada, seu valor aumenta em uma taxa de 10%. * Usa os componentes StockLogger que estejam conectados para imprimir * a negociaçao efetuada. */ @Override public boolean buyStock(String symbol) { } } /** * Obtém a faceta IComponent do componente. * * @return a faceta Icomponent */ public org.omg.CORBA.Object _get_component() { } DisplayExchangePrinter /** * DisplayExchangePrinterImpl implementa a interface IDL ExchangePrinter. * Essa implementação usa o display (console) para mostrar a negociação de * cada ação do mercado. */ public class DisplayExchangePrinterImpl extends ExchangePrinterPOA { /** * Construtor * * @param context o contexto do componente dono da faceta. */ public DisplayExchangePrinterImpl(ComponentContext context) { } /** * 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. */ @Override public void print(String text) { } } /** * Obtém a faceta IComponent do componente. * * @return a faceta Icomponent */ public org.omg.CORBA.Object _get_component() { } 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 Inicia o ORB e ativa o POA /* * As propriedades que informam o uso do JacORB como ORB. */ Properties orbProps = new Properties(); orbProps.setProperty("org.omg.CORBA.ORBClass", "org.jacorb.orb.ORB"); orbProps.setProperty("org.omg.CORBA.ORBSingletonClass", "org.jacorb.orb.ORBSingl /* Inicializa o ORB */ ORB orb = ORB.init(args, orbProps); /* Obtém a referência para o POA e inicializa o POA */ POA poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA")); poa.the_POAManager().activate(); ... orb.run(); • A inicialização do ORB e ativação do POA, ao iniciar o servidor, são iguais ao que já usamos nos exemplos anteriores. • O servidor deve, após criar o componente, deixar o ORB aguardando as requisições Criando o identificador do componente ComponentId componentId = new ComponentId( "StockSeller", (byte) 1, (byte) 0, (byte) 0, "Java"); • Todo componente precisa ter um identificador, que é descrito usando a classe ComponentId Criando o componente ComponentContext context = new Component(orb, poa, componentId); • A classe ComponentContext 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 Criando e adicionando as facetas // cria as facetas StockServerImpl stockServer = new StockServerImpl(context); StockExchangeImpl stockExchange = new StockExchangeImpl(context); // adiciona as facetas ao componente context.addFacet("StockServer", StockServerHelper.id(), stockServer); context.addFacet("StockExchange", StockExchangeHelper.id(), stockExchange); Adicionando receptáculos // adiciona o receptáculo context.addReceptacle("ExchangePrinter", ExchangePrinterHelper.id(), true); Salva o IOR em um arquivo Icomponent component = context.getIComponent(); PrintWriter ps = new PrintWriter(new FileOutputStream(new File(args[0]))); ps.println(orb.object_to_string(component)); ps.close(); • 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 Passo 4: Criando o componente StockLogger • O componente StockLogger oferece a faceta ExchangePrinter • O código StockLoggerMain.java é 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 Passo 5: Recuperando as referências dos componentes para conectá-los /* * Lê o IOR do componente StockSeller do arquivo seller.ior */ BufferedReader reader = new BufferedReader(new InputStreamReader( new FileInputStream(args[0]))); String ior_seller = reader.readLine(); reader.close(); /* * Lê o IOR do componente StockLogger do arquivo logger.ior * parâmetro 2. */ reader = new BufferedReader(new InputStreamReader( new FileInputStream(args[1]))); String ior_logger = reader.readLine(); reader.close(); • A conexão pode ser feita por um dos componentes do sistema ou por um cliente externo Obtendo as facetas IReceptacles do StockSeller e ExchangePrinter do StockLogger /* Obtém a referência para o componente StockSeller. */ org.omg.CORBA.Object obj = orb.string_to_object(ior_seller); IComponent stockSeller = IComponentHelper.narrow(obj); /* Obtém a referência para o componente StockLogger. */ obj = orb.string_to_object(ior_logger); IComponent stockLogger = IComponentHelper.narrow(obj); /* Obtém a referência para IReceptacles do StockSeller */ IReceptacles irStockSeller = IReceptaclesHelper.narrow( stockSeller.getFacet(IReceptaclesHelper.id())); /* Obtém a referência para ExchangePrinter do StockLogger */ ExchangePrinter printer = ExchangePrinterHelper.narrow( stockLogger.getFacet(ExchangePrinterHelper.id())); • 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 Conectando a faceta ExchangePrinter do StockLogger no receptáculo do StockSeller /* * Faz a conexão da faceta ExchangePrinter do componente StockLogger no * receptáculo do StockSeller. */ irStockSeller.connect("ExchangePrinter", printer); • 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 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 componte Obtendo o receptáculo correspondente a ExchangePrinter public boolean buyStock(String symbol) { … Receptacle receptacle = context.getReceptacleByName("ExchangePrinter"); if (receptacle == null) { … } List<ConnectionDescription> connections = receptacle.getConnections(); for (ConnectionDescription connection : connections) { try { ExchangePrinter printer = ExchangePrinterHelper.narrow(connection.objref); printer.print("Ação " + symbol + " foi negociada"); } catch(...) {...} } ... } • O contexto do componente possui o método getReceptacleByName que retorna um receptáculo do componente • A classe Receptacle permite então recuperar as 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 do componente conectado Passo 7: Usando o componente StockSeller • O cliente usa o componente StockSeller através de suas facetas • O StockMarketClient.java 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 Obtendo a referência para o componente StockSeller /* * Lê o IOR do componente StockSeller do arquivo seller.ior * parâmetro. */ BufferedReader reader = new BufferedReader(new InputStreamReader( new FileInputStream(args[0]))); String ior_seller = reader.readLine(); reader.close(); /* Obtém a referência para component StockSeller. */ org.omg.CORBA.Object ior = orb.string_to_object(ior_seller); IComponent stockSeller = IComponentHelper.narrow(ior); • O cliente recupera a referência para o componente StockSeller lendo do arquivo recebido por parâmetro Obtendo as facetas StockServer e StockExchange do componente StockSeller try { /* Obtém a faceta StockServer */ org.omg.CORBA.Object obj = stockSeller.getFacetByName("StockServer"); StockServer stockServerFacet = StockServerHelper.narrow(obj); /* Obtém a faceta StockExchange */ obj = stockSeller.getFacetByName("StockExchange"); StockExchange stockExchangeFacet = StockExchangeHelper.narrow(obj); } catch (…) { … } • Tendo o componente, as facetas StockServer e StockExchange são recuperadas, pelo nome ou pela interface Invocando os métodos disponibilizados em cada faceta try { /* Obtém os símbolos de todas as ações. */ String[] stockSymbols = stockServerFacet.getStockSymbols(); /* Mostra as ações com seus respectivos valores */ for (int i = 0; i < stockSymbols.length; i++) { System.out.println(stockSymbols[i] + " " + stockServerFacet.getStockValue(stockSymbols[i])); } if (stockSymbols.length > 0) { String first = stockSymbols[0]; System.out.println("--Compra a ação :" + first); boolean success = stockExchangeFacet.buyStock(first); if (success) { System.out.println("--Ação " + first + " depois da negociação: " + stockServerFacet.getStockValue(first)); } else { System.out.println("--Não foi possível negociar a ação " + first); } } catch (…) {…} • O cliente faz as chamadas aos métodos usando as respectivas facetas