segunda-feira, 26 de julho de 2010

[Oracle ODI] FTP e SFTP


Olá pessoal,

Continuo com meus estudos e implementações em ODI e hoje escrevo sobre uma limitação que encontrei na ferramenta, e também como eu a solucionei/contornei.

Vamos para o que interessa.

Tentei utilizar o componente de FTP padrão do ODI para transferir arquivos para alguns sistemas, e para um caso especifico não consegui fazer funcionar de jeito nenhum. A única diferença que percebi entre os sistemas que funcionavam para o que não funcionava era a seguinte:

Ao logar em um FTP, onde o componente padrão de FTP do ODI funcionou, em uma estação linux, era apresentado a seguinte linha no cabeçalho.

Exemplo: Remote system type is UNIX.

Em um dos sistemas, que eu tive problemas em conectar com o componente do ODI, a seguinte mensagem é retornada:

Exemplo: Remote system type is xxxxxxxxxxxxxxx.
Observe o xxxxxxxxxxxxxxx, acredito que este possa ser o motivo do componente de FTP do ODI não conseguir conectar.
Bom, já que não foi possível utilizar o componente padrão do ODI, resolvi escrever uma procedure em Jython para fazer FTP. Na primeira versão utilizei o módulo snpsftp, e o código que implementei na procedure ficou mais ou menos assim:
1 import snpsftp 2 ftp = snpsftp.SnpsFTP('ftp.myserver.com', 'mylogin', 'mypasswd') 3 ftp.setmode('ASCII') 4 ftp.put('/home/oracle/teste.txt','/u02/arquivos/teste.txt') 5 ftp.close()

Estava tudo funcionando bem, mas logo surgiu a necessidade de fazer SFTP ao invés de FTP. E o módulo snpsftp, claro, não implementa SFTP. :(
A solução foi desenvolver o meu próprio módulo.
Desenvolvendo um novo módulo

O primeiro passo foi criar um módulo (biblioteca) utilizando a linguagem Jython, e neste passo contei com a ajuda de um colega “fera” na linguagem Python. Segue abaixo o código do módulo que desenvolvemos.
1 import com.jcraft.jsch.Channel; 2 import com.jcraft.jsch.ChannelSftp; 3 import com.jcraft.jsch.JSch; 4 import com.jcraft.jsch.JSchException; 5 import com.jcraft.jsch.Session; 6 import com.jcraft.jsch.SftpException; 7 8 import java.util.Hashtable 9 import java.io.File 10 import java.io.FileInputStream 11 12 import os 13 14 __all__ = ["SFTP"] 15 16 class SFTP: 17 18 def __init__(self, paramHost, paramUser, paramPass): 19 self.strHost = paramHost 20 self.strUser = paramUser 21 self.strPass = paramPass 22 23 def putFile(self, paramDestino, paramFile): 24 self.__execute('putFile', paramFile, paramDestino) 25 26 def getFile(self, paramDestino, paramFile): 27 self.__execute('getFile', paramFile, paramDestino) 28 29 def deleteFile(self, paramFile): 30 self.__execute('deleteFile', paramFile, None) 31 32 def __execute(self, paramCommand, paramFile, paramDestino): 33 34 ObjJSch = com.jcraft.jsch.JSch() 35 ObjSession = com.jcraft.jsch.Session 36 37 try: 38 ObjHashTable = java.util.Hashtable() 39 ObjHashTable.put("StrictHostKeyChecking", "no") 40 41 ObjSession = ObjJSch.getSession(self.strUser, self.strHost, 22) 42 ObjSession.setConfig(ObjHashTable) 43 ObjSession.setPassword(self.strPass) 44 45 ObjSession.connect() 46 47 ObjChannel = ObjSession.openChannel("sftp") 48 ObjChannel.connect() 49 50 ObjSftp = com.jcraft.jsch.Channel 51 ObjSftp = ObjChannel 52 53 intMode = com.jcraft.jsch.ChannelSftp.OVERWRITE 54 55 try: 56 if paramCommand == 'putFile': 57 58 try: 59 ObjSftp.cd(paramDestino) 60 ObjFile = java.io.File(paramFile) 61 strNameFile = ObjFile.getName() 62 strNewFileName = "TMP_%s" % strNameFile 63 ObjSftp.put( java.io.FileInputStream(ObjFile), strNewFileName ) 64 ObjSftp.rename(strNewFileName, strNameFile) 65 except Exception, e: 66 raise e 67 68 elif paramCommand == 'getFile': 69 70 ObjSftp.get(paramFile, paramDestino) 71 72 elif paramCommand == 'deleteFile': 73 74 ObjSftp.rm(paramFile) 75 76 else: 77 78 raise Exception("METODO NAO IMPLEMENTADO") 79 80 except com.jcraft.jsch.JSchException, e: 81 raise e 82 83 except com.jcraft.jsch.SftpException, e: 84 raise e 85 86 print "OK"
Após criar o módulo, no nosso caso chamamos de “sftp.py”, este precisa ser salvo na pasta <ODI_HOME>/lib/scripting/Lib/ e é necessário reiniciar o agente para funcionar.
Para fins de teste é possível executar o console da linguagem jython através do executável <ODI_HOME>/bin/jython.sh.
Feito isto, agora podemos criar as procedures dentro do ODI, abaixo segue o código utilizado nas 3 operações que eu precisei implementar.

Operação PUT:
1 import sftp 2 3 ftp = sftp.SFTP(‘host’,’user’,’password’) 4 5 ftp.putFile('diretório destino','diretório origem/nome do arquivo')

Operação GET:
1 import sftp 2 3 ftp = sftp.SFTP(‘host’,’user’,’password’) 4 5 ftp.getFile('diretório destino','diretório origem/nome do arquivo')

Operação DELETE:
1 import sftp 2 3 ftp = sftp.SFTP(‘host’,’user’,’password’) 4 5 ftp.deleteFile(‘diretório/nome do arquivo')

No final, juntei tudo isso e desenvolvi um pacote genérico no ODI, o que me permite utilizar FTP ou SFTP, em qualquer integração sem que eu precise reescrever estes códigos.

Abraço, e até o próximo post.

Referências
Jython Reference

quarta-feira, 19 de maio de 2010

[ODI] Conceitos

Mês passado iniciei os estudos do Oracle Data Integrator (ODI) e de lá para cá desenvolvi algumas integrações, nestas primeiras integrações trabalhei principalmente com arquivos, tabelas (Sybase e Oracle) e até mesmo uma planilha em excel (meu primeiro teste com a ferramenta).

Antes de colocar a mão na massa foi fundamental o entendimento de alguns conceitos. Segue um resumo dos principais conceitos para começar a trabalhar com o Oracle Data Integrator.

Módulos

O ODI se divide em 4 módulos, são eles:

Designer: Este é o principal módulo para os desenvolvedores. É nele que são definidas as regras, as transformações e a integridade dos dados.

Operator: Este módulo gerencia e monitora os logs de execução, foi fundamental para eu conseguir implementar minha primeira interface de teste, pois sem ele seria impossível identificar os erros.

Topology Manager: Responsável por definir as arquiteturas físicas e lógicas da infraestrutura do projeto.

Secutity Manager: Gerencia os profiles de usuários assim como seus privilégios de acesso. (Este módulo foi o único que ainda não tive o prazer de utilizar)

Repositórios

Os repostiórios consistem em um repositório Principal (Master) e um ou mais repositórios de Trabalho (Work). Estes repositórios são mapeados para esquemas de banco de dados.

Há normalmente um único repostório principal, este repositório armazena informações de segurança (profiles de usuários e privilégios), informações de topologia (definições de tecnologias e servidores) e ainda o versionamento dos objetos. Os módulos Topology Manager e Security Manager são responsáveis por manter a informação contida neste repositório.

O repositório de trabalho armazena as informações de projeto, como por exemplo: modelos (tabelas, colunas, constraints, etc), projetos (regras, packages, procedures, pastas, etc) e informações de execução (cenários, informações de agendamento e logs). Os módulos Designer e Operator são responsáveis por manter a informação contida neste repostório.

Knowledge Modules

Além dos conceitos acima, é fundamental entender o conceito dos módulos de conhecimento (Knowledge Modules - KM). Estes módulos são "templates" de código para as tarefas de integração de dados, eles pré-definem os passos necessários para extrair, carregar e integrar dados.

Os KMs são agrupados em: operações de carga (LKMs), operações de integração (IKMs), operações de controle (CKMs), entre outras.

Servidor de dados, Contextos, Esquemas físicos e lógicos

Os 4 conceitos a seguir são mantidos através do módulo "Topoloy Manager".

O Servidor de Dados (Data Server) é o objeto que armazena os dados de conexão, por exemplo: endereço IP, usuário e senha no caso de um banco de dados.

A partir de um servidor de dados são criados os Esquemas Físicos, neste esquema são definidos 1 esquema para leitura dos dados (origem) e 1 esquema de trabalho, onde o ODI irá armazenar objetos temporários durante a execução dos cenários.

Os Esquemas Lógicos são responsáveis pelo mapeamento entre os contextos e os esquemas físicos.

O Contexto define o ambiente em que vamos executar os cenários, por exemplo: ambiente de desenvolvimento, homologação e produção.

Referências

ODI Architecture:
http://www.oracle.com/technology/products/oracle-data-integrator/pdf/oracledi_architecture.pdf

The Benefits of ODI Knowledge Modules:
http://prsync.com/oracle/the-benefits-of-odi-knowledge-modules-a-template-approach-for-better-data-integration-13520/

Context, Logical And Physical Schema – How does it work?
http://odiexperts.com/?p=652

DICA

Antes de terminar o post, segue uma dica importante para quem, assim como eu, está começando.

Sempre que uma interface ou um pacote for alterado é necessário regerar os cenários antes de executá-los, caso contrário as alterações não terão efeito na execução.

* Pare regerar o cenário, clique com o botão direito sobre o cenário e clique em "Regenerar".

Por enquanto é isto, até o próximo post.

quarta-feira, 14 de abril de 2010

Consultas "distinct" com XSL, otimizadas (o modo correto e o não tão correto...)

Esta dica foi enviada pelo meu amigo Luís Fernando Heckler.

As vezes é preciso percorrer os nodos do XML de forma semelhante a cláusula “distinct” do SQL. Por exemplo para gerar uma saída que tenha exatamente uma linha para cada tipo de nodo que estamos buscando no XML original, mas onde esses tipos podem aparecer repetidos.

Essa necessidade surgiu no projeto de um processo BPEL que alimenta um dashboard BAM.

A saída necessária era uma linha para cada departamento, mas no XML de origem os departamentos podiam aparecer mais de uma vez. Para iterar de forma única com XSLT 2.0, podemos utilizar a função xsl:for-each-group

Exemplo: [1]
<xsl:for-each-group select="//tns:departamento" group-by="./tns:dados/tns:nome">

Em [1] temos a expressão XPath que seleciona os nodos que nos interessam e a cláusula que especifica qual deve ser o índice, ou a chave única buscada, no caso, o nome do departamento. Dentro do laço, para identificarmos qual o departamento atual que está sendo processado, podemos usar a função current-grouping-key()

Exemplo: [2]
<xsl:variable name="departamentoAtual" select="current-grouping-key()"/>

Agora entra a dica principal: como fazer subconsultas sobre os nodos filhos de cada departamento, sendo que pode haver mais de uma ocorrência do mesmo departamento?

Primeiro o modo que funciona mas NÃO é otimizado

Digamos que você precise agora buscar todos os nodos “marca” que existem abaixo do departamento atual, que também devem ser buscados de forma única (um registro por marca). O natural é fazer outro for-each-group restringindo o nome do departamento no XPath de seleção das marcas, como abaixo:

Exemplo: [3]
<xsl:for-each-group select="//tns:marca[ancestor::*[local-name(.)='departamento']/tns:dados/tns:nome = $departamentoAtual]" group-by="./tns:dados/tns:nome">

Veja que em [3] estamos filtrando o nome do departamento usando a variável criada em [2], na seleção das marcas. Isso funciona perfeitamente, não está errado. Porém, exige que todo o XML a partir do nível dos departamentos seja parseado novamente, para poder fazer o filtro indicado.

Como melhorar? Detalhes a seguir…

Agora o modo otimizado (1/3 do tempo de processamento nos testes durante o desenvolvimento)

Aproveite as funções e índices em memória que o XSLT lhe proporciona, e não faça parser sobre a mesma parte gigante do arquivo 2x. A função current-group() é um índice em memória que contém o nodeset atual da iteração.

Exemplo: [4]
<xsl:for-each-group select="current-group()//tns:marca" group-by="./tns:dados/tns:nome">

Veja em [4] que estamos solicitando ao processador XSLT que itere as marcas não mais sobre o XML original, mas sobre o nodeset em memória da iteração atual dos departamentos, o que certamente é bem menor pois contém apenas os departamentos do mesmo nome atual e seus filhos.

E para “pegar” a marca atual? Use a função current-grouping-key() que agora está no contexto do laço das marcas, enquanto que o departamento atual nós já salvamos em uma variável lá no exemplo [2].

No desenvolvimento do processo BPEL que alimenta o BAM, uma transformação dessas que levava 3min passou a levar menos de 1min. Parece pouco? Mas são 4 delas por XML e 3 XML processados cada vez que a integração é executada... ;-)

Até a próxima, valeu Luís!

terça-feira, 6 de abril de 2010

[Oracle BPEL] A instância do meu processo sumiu!

Alguma vez você disparou um processo BPEL e não encontrou a instância do processo no BPEL Console?
Isto ocorre quando o processo BPEL lança alguma exceção (não tratada) antes da instância ser persistida no schema de banco da engine (orabpel).

Para resolver este problema podemos forçar a persistência (dehydration) da instância. Para persistir a instância adicionamos um componente "Java Embedding" logo após o inicio do processo BPEL, dentro deste componente utilizamos a função checkpoint(); conforme imagem abaixo:


A utilização desta função pode causar queda no desempenho do processo BPEL durante o tempo de execução devido à sobrecarga adicional para persistir o processo, porém garante que toda a instância iniciada do processo aparecerá no console. 

O BPEL console apresenta o estado da execução somente até o último ponto de persistência, logo alguns erros ainda assim necessitam o acompanhamento dos logs.

Abaixo relato 2 experiências recentes da utilização do checkpoint.

1) Em um processo, que não estava aparecendo no BPEL Console, adicionei a função de checkpoint antes de um "switch" e o processo simplesmente travou, não teve jeito de funcionar, mas foi só passar a função de checkpoint para depois do "switch" que tudo funcionou perfeitamente.

2) Eu desenvolvi um processo que fazia a chamada de um serviço dentro de um loop, algumas instâncias deste processo chamavam até 5 mil vezes este serviço (sim, era necessário). Naturalmente o processo ficou bem instável e volta e meia travava completamente. A solução que encontrei foi colocar o checkpoint dentro do loop, persistindo o processo a cada chamada do serviço, esta solução onerou o processo, porém a necessidade era garantir a invocação do serviço, independentemente do tempo de execução.


Os exemplos deste post foram executados no Oracle BPEL 10g (10.1.3.3)

quinta-feira, 4 de março de 2010

Ferramentas de apoio

Caros leitores do blog, hoje eu quero dedicar um post para algumas ferramentas de apoio que são muito úteis durante o desenvolvimento de projetos.

E o melhor de tudo, as ferramentas listadas abaixo são todas free!!!

Soap UI
http://www.soapui.org/
Ótima ferramenta para testar webservices.

Hermes JMS
http://www.hermesjms.com/
Ferramenta para acompanhar filas e tópicos JMS.

Find Jar
http://www.findjar.com
Site muito útil para encontrar bibliotecas JAR a partir de classes JAVA.

Oracle SQL Developer
http://www.oracle.com/technology/products/database/sql_developer/index.html
Tenho utilizado para executar SQLs e PL/SQLs, hoje utilizo principalmente para conectar nos bancos de dados Oracle e Sybase. Mas a ferramenta ainda suporta: MySQL, Microsoft SQL Server, Microsoft Access, Teradata and IBM DB2 (pelo menos é o que diz no site :))

Também não posso esquecer as mais básicas, mas não menos importantes:

Putty
http://www.putty.org/
Cliente SSH.

WinSCP
http://winscp.net/
Cliente SFTP, FTP e SCP.

Notepad ++
http://notepad-plus.sourceforge.net/br/site.htm
Um editor de texto mais elaborado que o clássico notepad do windows.

Você utiliza alguma ferramenta de apoio que não está listada aqui? Conhece uma ferramenta melhor do que as que estão listadas aqui? Contribua deixando seu recado na seção de comentários!

Até a próxima!

segunda-feira, 15 de fevereiro de 2010

[Oracle BAM] Exportação e importação de objetos

Olá pessoal, as férias acabaram atrapalhando a atualização do blog, vamos ver se agora consigo manter uma atualização mais frequente (pelo menos 1 vez por mês :P).

Bom, vamos ao que interessa.

Todos os componentes do Oracle BAM, podem ser exportados para um arquivo XML através do utilitário iCommand.

O iCommand é um utilitário que permite importar e exportar os diversos componentes do Oracle BAM e pode ser encontrado no diretório "$SOA_HOME/bam/bin".

Exportando objetos no Oracle BAM
Utilizamos o comando abaixo para exportar uma pasta, com todos os data objects que estão dentro dela.

./icommand -cmd export -name "/public/DataObject/Pasta1" -type folder -file data_objects.xml

onde:
  • "Pasta1" é a pasta do BAM Architect onde os data objects, que você deseja exportar, estão armazenados;
  • "data_objects.xml" é o arquivo xml que será gerado.

Também é possível exportar os data objects um a um, através do comando abaixo:

./icommand -cmd export -name "/Samples/Call Center" -file CallCenter.xml

onde:
  • "Samples" é a pasta onde o data object está armazenado;
  • "Call Center" é o data object que será exportado;

Já os dashboards podem ser privados ou compartilhados, abaixo seguem exemplos de como exportar cada um deles:

Exportando um dashboard privado
./icommand -cmd export -name "/private:colecoes/Report/Pasta1/nomeReport" -type report -file dashboard.xml

onde:
  • "Pasta1" é o nome da pasta onde o dashboard está armazenado;
  • "nomeReport" é o nome do dashboard que será exportado;

Exportando um dashboard compartilhado
./icommand -cmd export -name "/public/Report/Pasta1/nomeReport" -type report -file SharedReport.xml

Até aqui vimos como exportar alguns tipos de objetos do Oracle BAM, agora verenmos como importá-los.

Importando objetos no Oracle BAM
A importação de qualquer tipo de objeto (reports, data objects, etc) do Oracle BAM é feita com o mesmo comando, conforme abaixo.

./icommand -cmd import -file nomeArquivo.xml

Exemplos de importação de objetos

./icommand -cmd import -file data_objects.xml
./icommand -cmd import -file dashboard.xml

Bom, ai estão alguns exemplos bem básicos da utilização do ICommand, mas este utilitário permite muito mais, para maiores informações acesse a página do produto.

Os exemplos deste post foram executados no Oracle BAM 11g (11.1.1.2.0)

quarta-feira, 9 de dezembro de 2009

[Oracle BPM] Adicionando usuários administradores em configurações híbridas de diretório

O Oracle BPM 10g permite a existência de mais de um usuário administrador, estes usuários possuem permissão para logar no Process Administrator e realizar as tarefas administrativas.

Quando utilizamos um serviço híbrido de diretório (banco de dados + LDAP) não é possível conceder estas permissões diretamente pelo Process Administrator pois esta funcionalidade está desabilitada, conforme podemos ver na imagem abaixo.



No Oracle BPM existem 2 perfis de usuários administradores, são eles:
  • Administradores de usuários: os usuários com este perfil somente conseguem realizar operações de mapeamento de papéis para usuários e grupos e cadastrar períodos de ausência.
    • As permissões são atribuídas no nível de Unidade Organizacional;
    • Um administrador de usuários pode administrar somente os usuários associados a Unidades Organizacionais cuja as quais ele tenha permissão de administração.
  • Administrador: os usuários com este perfil tem total acesso ao Process Administrator e podem gerenciar todas as operações disponíveis no diretório.
Mas como atribuir estes perfis para um usuário se as opções estão desabilitadas na interface do Process Administrator? É isso que veremos a partir de agora.

Para exemplificar, vamos utilizar um usuário fictício, cujo user id é "gerentea".

Bom vamos colocar a mão na massa. O primeiro passo é conectar no schema de banco de dados do diretório de usuários. Para isto, você pode utilizar qualquer cliente SQL.

O segundo passo é descobrir o ID interno do usuário para qual você deseja adicionar o perfil, no meu caso o usuário "gerentea". Para isto, você terá que fazer uma consulta na tabela FUEGO_PARTICIPANT, conforme abaixo:

Após executar o SQL, descobrimos que o ID interno do usuário "gerentea" é 30. Guarde este número pois iremos utilizá-lo em seguida.

A partir de agora cada um dos perfis necessitam passos específicos para configuração, vamos começar pelo perfil de Administrador de usuários.

Adicionando perfil de Administrador de usuários (acesso restrito)

Vamos imaginar que temos a seguinte estrutura organizacional.

Usuários
   - TI
   - Comercial
   - Outros

Neste exemplo, vamos permitir que o usuário "gerentea" administre todos os usuários da unidade organizacional "TI", para isto é necessário incluir uma nova linha na tabela FUEGO_PART_ADMINOUS, conforme imagem abaixo:

onde:
  • USUARIOS/TI é o nome da Unidade Organizacional que o usuário "gerentea" terá direito de administrar
  • 30 é o ID interno do usuário "gerentea"
Porém não é só isso, ainda precisamos dizer que o usuário "gerentea" possui o perfil de administrador de usuários, caso contrário ele não conseguirá fazer o login no Process Administrator. Para atribuir a permissão precisamos alterar a tabela FUEGO_PARTICIPANT, conforme imagem abaixo:

onde:
  • 30 é o ID interno do usuário "gerentea"

Agora o usuário "gerentea" pode logar no Process Administrator e também já consegue administrar todos os usuários membros da unidade organizacional "TI", porém continua sem permissão para as demais unidades organizacionais.

Adicionando perfil de Administrador (acesso completo)

Para atribuir o perfil de usuário administrador precisamos apenas alterar a tabela FUEGO_PARTICIPANT, conforme imagem abaixo:


onde:
  • 30 é o ID interno do usuário "gerentea"

Pronto, com isso finalizamos mais esta dica sobre o Oracle BPM.