Month: March 2011

Working with Expiration Human Task on Oracle Soa Suite 11g

Hello,

I had that to implement an expiration process and go to leave the main steps here:

1- Creating a new Application: File -> New -> Generic Application -> ApplicationExpirationHT
2- Adding a project to application: Project Name: ProjectExpirationHT Project Type: SOA
3- Select: Empty Composite and click at Finish
4- Make a drag and drop of BPEL Process to your composite.xml screen PS: Choose One Way BPEL Process on template combobox
5- Go to file BPELProcess1.bpel in design mode and drag and drop a Human Task Component
6-Double Click in the green human task icon, click at the green plus button
7-Add a Task parameter, for this example i will make the default input parameter (string), that i will use to set the time of expiration (on duration format)
8-Here goes the magic point; Add the custom outcome action called “EXPIRED”
9-This is the final configuration screen. Your screen should like this:
10-Edit your Humantask1.task file and add an user assignment (weblogic is the default user)
11-This is the main configuration for the task expiration (Deadlines): Check “Expire After” and select the task parameter that exists in your payload variable, for this example is inputstring (/task:task/task:payload/task:Parameter1)
12-This is the final step, when your task expires, will be enter on Expired branch and you could make other things when the task expires
Important Tips-

  1. Your payload parameter should be in a DURATION Format, here is an example (P0Y0M0DT0H0M0S). Here is more details about this default type: http://www.w3.org/TR/xmlschema-2/#duration
  2. When you set the Deadlines on your .task file, the expression for duration should be a parameter of your payload, because the rest of task parameters is READ-ONLY
  3. If your bpel is already created and you want to add the expiration validation, don’t worry, you cannot to recreate all flow, it is enough to add a new branch, copy of the others outcomes expressions and change the status task to EXPIRED, it works !
That is it people, if have any question, invite a email for me or better, add commentary for this post and i can help you !
Advertisements

Script Python para Deploy Automatizado usando MD5 e SFTP

Boa Noite pessoal,

Contratei uma hospedagem na Locaweb recentemente para meus sites em Django / Python e eu como sempre querendo automatizar tudo o tempo todo, criei um script em Python que realiza o deploy da minha aplicação lá no servidor remoto.

Objetivo: Copiar uma pasta e todo o seu conteúdo recursivamente para o servidor usando Secure File Transfer Protocol (SFTP), sincronizando as duas pastas (local e remota), isto é, identificar quais arquivos modificaram (usando md5) e copiar apenas eles para o servidor, removendo todos os arquivos e pastas remotas que foram deletados localmente desde a última sincronização. Tudo isto de um jeito super rápido, ocupando o mínimo de banda possível e feito com apenas 1 clique. Semelhante ao deploy do Google App Engine, para quem já utilizou.

Características:

  1. Biblioteca Paramiko – Estou utilizando uma biblioteca poderosa que implementa a parte de comunicação remota (SSH e SFTP e muito mais), chamada Paramiko.
  2. Índice MD5 – Para comparar arquivos locais e remotos, utilizo MD5, isto é, uma espécie de checksum / hashcode, que lê os bytes de um arquivo e me gera uma string única, sobre a qual eu relizo a comparação. Para não ter que fazer download do arquivo remoto apenas para calcular o md5, criei um índice, que nada mais é do que um dicionário em python que contém como chave o caminho completo do arquivo remoto e como valor o seu md5. Atualizo esse índice durante a sincronização.
  3. Arquivo de Configuração (Properties) – Estou utilizando um arquivo properties para isolar os dados de conexão ao servidor, tais como usuário e senha e mantê-los em uma pasta em separado.
  4. Barra de Progresso – Tentei implementar uma espécie de barra de progresso em modo texto, que imprime 100 caracteres “ponto”, e cada caractere representa 1% do upload do arquivo durante a cópia para o servidor.

Não se esqueçam de instalar a biblioteca paramiko, tem informações aqui no site do autor: http://www.lag.net/paramiko/

Aqui estão os pacotes necessários para as pessoas que utilizam o windows:

– Windows 64 bits (Python 2.7)

paramiko-1.7.7.1.win-amd64-py2.7.exe
pycrypto-2.3.win-amd64-py2.7.exe

– Windows 32 bits (Python 2.7)

paramiko-1.7.7.1.win32-py2.7.exe
pycrypto-2.3.win32-py2.7.exe

Para instalar em linux basta fazer o download do .tar.gz, acessar o diretório expandido e digitar o comando easy_install .

Vou postar agora o código fonte, são 2 arquivos, um para a configuração (properties – .ini) e o outro realmente que realiza o deploy (.py).

Arquivo de Configuração: settings.ini

[deploy]
HOSTNAME: meuservidor.com ou 187.200.123.12
PORT: 22
USERNAME: usuario
PASSWORD: senha
DIR_LOCAL_RAIZ: /app/pythonProjects
DIR_REMOTO_RAIZ: /home/storage/abc/julianajabur
WSGI: /home/storage/abc/julianajabur/public_html/julianajabur/index.wsgi
INDICE_MD5: /home/storage/abc/julianajabur/python_conf/julianajabur/indexmd5

Explicando os parâmetros acima:

  1. HOSTNAME = Endereço do servidor, pode ser um ip ou um nome DNS
  2. PORT = Porta de comunicação, 22 é a porta padrão para ftp
  3. USERNAME = usuário
  4. PASSWORD = senha
  5. DIR_LOCAL_RAIZ = caminho raiz da pasta que eu quero copiar, importante: aqui NÃO É a pasta a ser copiada, e sim apenas o caminho inicial dela, utilizo isso para fazer o de/para da estrutura de arquivos local versus a remota.
  6. DIR_REMOTO_RAIZ = idem ao de cima, só que aplicado à pasta remota.
  7. WSGI – aqui é o caminho até o seu arquivo WSGI, pois eu realizo uma cópia dele por último, para ter o mesmo efeito de touch arquivo.wsgi, indicando ao apache que sua aplicação foi mudada e deve ser atualizada.
  8. INDICE_WSGI = Caminho remoto onde vai ser gravado o arquivo de índice md5, observação, deve ser um diretório fora da pasta do seu projeto, para não atrapalhar no processo de sincronização de pastas.

Arquivo SFTPLocaweb.py

#! /usr/bin/python
# -*- coding: iso-8859-1 -*-

import os, paramiko, hashlib, sys, pickle
from stat import S_ISREG, S_ISDIR
from ConfigParser import RawConfigParser

class SFTPLocaweb:

    def __init__(self):
        self.caminhoArquivoConfiguracao = '../../../../python_conf/julianajabur/settings.ini'
        config = self.getConfigurationFile()
        self.hostname = config.get('deploy', 'HOSTNAME')
        self.port = config.getint('deploy', 'PORT')
        self.username = config.get('deploy', 'USERNAME')
        self.password = config.get('deploy', 'PASSWORD')
        self.dirLocalRaiz = config.get('deploy', 'DIR_LOCAL_RAIZ')
        self.dirRemotoRaiz = config.get('deploy', 'DIR_REMOTO_RAIZ')
        self.wsgi = config.get('deploy', 'WSGI')
        self.indice_md5 = config.get('deploy', 'INDICE_MD5')
        self.pastaOrigem = ''
        self.pastaDestino = ''
        self.sftp = None
        self.dicionario_md5 = {}
        self.statusTransferencia = 0    
        self.totais = {}    
        self.transport = None
  
    def getConfigurationFile(self):
        BASE_DIR = os.path.dirname(os.path.abspath(__file__))
        PYTHON_CONF = os.path.abspath(self.pathJoin(BASE_DIR, self.caminhoArquivoConfiguracao))
        config = RawConfigParser()
        config.read(PYTHON_CONF)
        return config        
    
    def getConexaoSSH(self):
        try:
            print 'Estabelecendo conexão com: ', self.hostname, self.port, '...'
            self.transport = paramiko.Transport((self.hostname, self.port))
            self.transport.connect(username=self.username, password=self.password, hostkey=None)
            self.sftp = paramiko.SFTPClient.from_transport(self.transport)
        except Exception, e:
            print '*** Erro ao se conectar com o servidor: %s: %s' % (e.__class__, e)
            sys.exit()
            try:
                self.transport.close()
            except:
                pass
  
    def pathJoin(self, raiz, diretorio):
        return os.path.join(raiz, diretorio).replace('\\', '/')
    
    def criarDiretorioRemoto(self, diretorio):
        try:
            self.sftp.mkdir(diretorio)
            print '    (diretório criado) ', diretorio
            return 1
        except IOError:
            print '    (diretório já existe) ', diretorio
            return 0
  
    def getDiretorioRemotoFromLocal(self, diretorioLocal):
        return diretorioLocal.replace(self.dirLocalRaiz, self.dirRemotoRaiz)

    def getDiretorioLocalFromRemoto(self, diretorioRemoto):
        return diretorioRemoto.replace(self.dirRemotoRaiz, self.dirLocalRaiz)
  
    def existeArquivoIndice(self):
        try:
            if self.sftp.stat(self.indice_md5):
                return True
        except:
            return False
  
    def recuperaMd5(self, entrada):
        try:
            md5 = self.dicionario_md5[entrada]
            return md5
        except KeyError:
            return None
  
    def atualizarIndice(self):
        for key in self.dicionario_md5.keys():
            if not os.path.exists(self.getDiretorioLocalFromRemoto(key)):
                self.dicionario_md5.pop(key)
  
    def carregarDicionarioMd5(self):
        if self.existeArquivoIndice():
            indice_local = self.getDiretorioLocalFromRemoto(self.indice_md5)
            self.sftp.get(self.indice_md5, indice_local)
            indice = open(indice_local, 'rb')
            self.dicionario_md5 = pickle.load(indice)
            indice.close()
  
    def salvarDicionarioMd5(self):
        indice_local = self.getDiretorioLocalFromRemoto(self.indice_md5)
        indice = open(indice_local, 'wb')
        pickle.dump(self.dicionario_md5, indice)
        indice.close()  
        self.copiarArquivoParaServidor(indice_local, self.indice_md5)
    
    def isArquivosIguaisMD5(self, local_file, remote_file):
        m_local = hashlib.md5()
        m_local.update(open(local_file, "rb").read())
        md5Local = m_local.digest()
        md5Remoto = self.recuperaMd5(remote_file)
        if md5Local == md5Remoto:
            return True
        else:
            return md5Local
  
    def calcularMd5(self, local_file):
        m_local = hashlib.md5()
        m_local.update(open(local_file, "rb").read())
        return m_local.digest()        
  
    def acompanharTransferenciaArquivo(self, tamanhoTransferido, tamanhoTotal):
        try:
            porcentagem = int((float(tamanhoTransferido) / float(tamanhoTotal)) * 100)
            pontos = porcentagem - self.statusTransferencia
            if pontos > 0:
                self.statusTransferencia = self.statusTransferencia + pontos
                sys.stdout.write(pontos * '.')
        except Exception, e:
            print '*** Exceção Lançada: %s: %s' % (e.__class__, e)
  
    def copiarArquivoParaServidor(self, local_file, remote_file):
        tentativas = 0
        try:
            self.statusTransferencia = 0
            self.sftp.put(local_file, remote_file, self.acompanharTransferenciaArquivo)
            print ''
        except:
            tentativas += 1     
            print 'ERRO ao enviar o arquivo ', local_file
            self.copiarArquivoParaServidor(self, local_file, remote_file)
        return tentativas
  
    def sincronizarPastas(self, dirLocal, dirRemoto):
        self.totais = {}
        self.pastaOrigem = dirLocal
        self.pastaDestino = dirRemoto
        self.getConexaoSSH()
        self.carregarDicionarioMd5()
        self.executarCopia()
        self.atualizarIndice()
        print 'Copiando o indice md5: ', 
        self.salvarDicionarioMd5()
        print 'Copiando o index.wsgi: ', 
        self.copiarArquivoParaServidor(self.getDiretorioLocalFromRemoto(self.wsgi), self.wsgi)
        self.transport.close()
    
    def deletarRecursosRemotos(self, dirRemoto):
        try:
            print 'PROCESSANDO A PASTA REMOTA - ', dirRemoto
            for entrada in  self.sftp.listdir(dirRemoto):
                remote_entry = self.pathJoin(dirRemoto, entrada)
                remote_entry = remote_entry.replace('\\','/')
                if self.isRemoteDir(remote_entry):
                    self.deletarRecursosRemotos(remote_entry)
                elif self.isRemoteFile(remote_entry):
                    local_file = self.getDiretorioLocalFromRemoto(remote_entry)
                    if not os.path.exists(local_file):
                        print "    (arquivo removido):", remote_entry, " (" + self.formataTamanhoArquivo(self.sftp.stat(remote_entry).st_size) + ") "
                        self.sftp.remove(remote_entry)
                        self.contabilizarTotais('arquivos_removidos', 1)
            if self.sftp.listdir != '' and not os.path.exists(self.getDiretorioLocalFromRemoto(dirRemoto)):
                print '    (diretório removido): ', dirRemoto
                self.sftp.rmdir(dirRemoto)
                self.contabilizarTotais('diretorios_removidos', 1)
        except Exception, e:
            print '*** Exceção Lançada ao deletar Recursos Remotos: %s: %s' % (e.__class__, e)
            sys.exit()
  
    def isRemoteDir (self, remote_path):
        try:
            st = self.sftp.stat( remote_path )
            return S_ISDIR(st.st_mode)
        except Exception:
            return False

    def isRemoteFile (self, remote_path):
        try:
            st = self.sftp.stat( remote_path )
            return S_ISREG(st.st_mode)
        except Exception:
            return False
  
    def formataTamanhoArquivo(self, tamanho):
        tipo = 1
        while(tamanho > 1024):
            tamanho = float(tamanho) / 1024.0
            tipo += 1
        if(tipo == 1):
            tamanho = "%.2f bytes" % (tamanho)
        elif(tipo == 2):
            tamanho = "%.2f Kb" % (tamanho)
        elif(tipo == 3):
            tamanho = "%.2f Mb" % (tamanho)
        elif(tipo == 4):
            tamanho = "%.2f Gb" % (tamanho)
        return tamanho.replace(".00", "")
          
    def contabilizarTotais(self, tipo, valor):
        self.totais[tipo] = self.getResultadoTotal(tipo) + valor
    
    def getResultadoTotal(self, chave):
        try:
            return self.totais[chave]
        except:
            return 0
  
    def executarCopia(self):
        print '=' * 60
        print 'Local = ' + self.pastaOrigem
        print 'Remoto = ' + self.pastaDestino
        print '=' * 60

        try:
            diretorioRemoto = self.pastaDestino
            self.contabilizarTotais('diretorios_criados', self.criarDiretorioRemoto(diretorioRemoto))
            for raiz, diretorios, arquivos in os.walk(self.pastaOrigem):
                raiz = raiz.replace('\\', '/')
                print 'PROCESSANDO A PASTA LOCAL - ', self.pathJoin(self.pastaOrigem, raiz)
                for diretorio in diretorios:
                    self.contabilizarTotais('diretorios', 1)
                    diretorioRemoto = self.getDiretorioRemotoFromLocal(self.pathJoin(raiz, diretorio))
                    self.contabilizarTotais('diretorios_criados', self.criarDiretorioRemoto(diretorioRemoto))
                for arquivo in arquivos:
                    self.contabilizarTotais('arquivos', 1)
                    local_file = self.pathJoin(raiz, arquivo)
                    remote_file = self.getDiretorioRemotoFromLocal(local_file)
                    is_up_to_date = False
                    try:
                        # verifica se o arquivo remoto existe
                        if self.sftp.stat(remote_file):
                            md5 = self.isArquivosIguaisMD5(local_file, remote_file)
                            if md5 == True:
                                print "    (não modificado):", arquivo + " (" + self.formataTamanhoArquivo(os.path.getsize(local_file)) + ")"
                                self.contabilizarTotais('arquivos_naomodificados', 1)
                                is_up_to_date = True
                            else:
                                print "    (modificado):", arquivo + " (" + self.formataTamanhoArquivo(os.path.getsize(local_file)) + ") ",
                                self.dicionario_md5[remote_file] = md5
                                self.contabilizarTotais('arquivos_modificados', 1)
                    except:
                        print "    (novo):", arquivo + " (" + self.formataTamanhoArquivo(os.path.getsize(local_file)) + ") ",
                        self.contabilizarTotais('arquivos_novos', 1)
                        md5 = self.calcularMd5(local_file)
                        self.dicionario_md5[remote_file] = md5
                    if not is_up_to_date:
                        self.contabilizarTotais('tentativas', self.copiarArquivoParaServidor(local_file, remote_file))
                        
            self.deletarRecursosRemotos(self.pastaDestino)
            
        except Exception, e:
            print '*** Exceção Lançada ao copiar arquivo: %s: %s' % (e.__class__, e)
            sys.exit()
        print '=' * 60
        print 'Número de tentativas para erro (retry):', self.getResultadoTotal('tentativas')
        print 'Total de diretórios criados:', self.getResultadoTotal('diretorios_criados')
        print 'Total de arquivos novos:', self.getResultadoTotal('arquivos_novos')
        print 'Total de arquivos modificados:', self.getResultadoTotal('arquivos_modificados')
        print 'Total de arquivos não modificados:', self.getResultadoTotal('arquivos_naomodificados')
        print 'Total de diretórios remotos removidos:', self.getResultadoTotal('diretorios_removidos')
        print 'Total de arquivos remotos removidos:', self.getResultadoTotal('arquivos_removidos')
        print 'Total de diretórios:', self.getResultadoTotal('diretorios')
        print 'Total de arquivos:', self.getResultadoTotal('arquivos')
        print 'Completo!'
        print '=' * 60

Preste atenção ao parâmetro self.caminhoArquivoConfiguracao, setando para o diretório onde se encontra o seu arquivo de configuração (settings.ini)

Exemplo de utilização (Chamada do Deploy):

#! /usr/bin/python
# -*- coding: iso-8859-1 -*-

from julianajabur.deploy import ajustarFlex, SFTPLocaweb

sftpLocaweb = SFTPLocaweb.SFTPLocaweb()

dir_local_wsgi='/app/pythonProjects/wsgi_apps/julianajabur'
dir_remote_wsgi = "/home/storage/abc/julianajabur/wsgi_apps/julianajabur"
sftpLocaweb.sincronizarPastas(dir_local_wsgi, dir_remote_wsgi)

print '\n\n\n\n'

dir_local_public='/app/pythonProjects/public_html/julianajabur'
dir_remote_public = "/home/storage/abc/julianajabur/public_html/julianajabur"
sftpLocaweb.sincronizarPastas(dir_local_public, dir_remote_public)

E por último segue o resultado da execução do script (SysOut), impresso na linha de comando:

Estabelecendo conexão com:  servidor.com 22 ...
============================================================
Local = /app/pythonProjects/wsgi_apps/julianajabur
Remoto = /home/storage/abc/julianajabur/wsgi_apps/julianajabur
============================================================
    (diretório já existe)  /home/storage/abc/julianajabur/wsgi_apps/julianajabur
PROCESSANDO A PASTA LOCAL -  /app/pythonProjects/wsgi_apps/julianajabur
    (diretório já existe)  /home/storage/abc/julianajabur/wsgi_apps/julianajabur/.settings
    (diretório já existe)  /home/storage/abc/julianajabur/wsgi_apps/julianajabur/julianajabur
    (não modificado): SFTPLocaweb.py (11.93 Kb)
    (não modificado): .project (422 bytes)
    (não modificado): .pydevproject (670 bytes)
PROCESSANDO A PASTA LOCAL -  /app/pythonProjects/wsgi_apps/julianajabur/.settings
    (não modificado): org.eclipse.ltk.core.refactoring.prefs (134 bytes)
    (não modificado): org.eclipse.core.resources.prefs (276 bytes)
PROCESSANDO A PASTA LOCAL -  /app/pythonProjects/wsgi_apps/julianajabur/julianajabur
    (diretório já existe)  /home/storage/abc/julianajabur/wsgi_apps/julianajabur/julianajabur/julianajaburapp
    (diretório já existe)  /home/storage/abc/julianajabur/wsgi_apps/julianajabur/julianajabur/deploy
    (não modificado): urls.py (650 bytes)
    (não modificado): urls.pyc (1004 bytes)
    (não modificado): __init__.pyc (141 bytes)
    (não modificado): __init__.py (0 bytes)
    (não modificado): settings.pyc (3.04 Kb)
    (não modificado): manage.py (654 bytes)
    (não modificado): amfgateway.pyc (1.46 Kb)
    (não modificado): amfgateway.py (855 bytes)
    (não modificado): settings.py (3.57 Kb)
PROCESSANDO A PASTA LOCAL -  /app/pythonProjects/wsgi_apps/julianajabur/julianajabur/julianajaburapp
    (não modificado): models.pyc (751 bytes)
    (não modificado): __init__.pyc (157 bytes)
    (não modificado): views.py (538 bytes)
    (não modificado): models.py (212 bytes)
    (não modificado): __init__.py (0 bytes)
    (não modificado): views.pyc (1.11 Kb)
    (não modificado): tests.py (539 bytes)
    (não modificado): admin.pyc (336 bytes)
    (não modificado): admin.py (122 bytes)
PROCESSANDO A PASTA LOCAL -  /app/pythonProjects/wsgi_apps/julianajabur/julianajabur/deploy
    (não modificado): __init__.pyc (160 bytes)
    (não modificado): __init__.py (0 bytes)
    (não modificado): SFTPLocaweb.py (11.07 Kb)
    (não modificado): deployLocaweb.py (600 bytes)
    (não modificado): SFTPLocaweb.pyc (11.45 Kb)
    (não modificado): ajustarFlex.py (1.19 Kb)
    (não modificado): ajustarFlex.pyc (1.90 Kb)
PROCESSANDO A PASTA REMOTA -  /home/storage/abc/julianajabur/wsgi_apps/julianajabur
PROCESSANDO A PASTA REMOTA -  /home/storage/abc/julianajabur/wsgi_apps/julianajabur/.settings
PROCESSANDO A PASTA REMOTA -  /home/storage/abc/julianajabur/wsgi_apps/julianajabur/julianajabur
PROCESSANDO A PASTA REMOTA -  /home/storage/abc/julianajabur/wsgi_apps/julianajabur/julianajabur/julianajaburapp
PROCESSANDO A PASTA REMOTA -  /home/storage/abc/julianajabur/wsgi_apps/julianajabur/julianajabur/deploy
============================================================
Número de tentativas para erro (retry): 0
Total de diretórios criados: 0
Total de arquivos novos: 0
Total de arquivos modificados: 0
Total de arquivos não modificados: 30
Total de diretórios remotos removidos: 0
Total de arquivos remotos removidos: 0
Total de diretórios: 4
Total de arquivos: 30
Completo!
============================================================
Copiando o indice md5: ....................................................................................................
Copiando o index.wsgi: ....................................................................................................

Até a próxima pessoal, espero que este script ajude muitas pessoas, qualquer dúvida, basta deixar um comentário aqui no blog e eu respondo.

Sintam-se à vontade para manipular o script como vocês bem entenderem, e se melhorarem ele, me enviem uma cópia com as melhorias para eu postar aqui no blog, …

Abraços,
Victor Jabur

Testador Completo online para Códigos em Python – MIT Tutor

Olá pessoal,

Segue o link para um testador completo para códigos em python, incluindo debug linha a linha, com possibilidade de avançar, retroceder e visualização completa das variáveis globais e locais, software desenvolvido por Philip J. Guo e hospedado no MIT.

http://people.csail.mit.edu/pgbovine/python/

Como exemplo, já foram desenvolvidos alguns exemplos de código, bastando clicar nos links em baixo, tais como fatorial, fibonacci, dentre outros algoritmos clássicos.

Segue uma imagem do debug, mostrando as variáveis do programa e outros dados:

Aproveitando a oportunidade, gostaria de informar também que a linguagem de programação Python está sendo utilizada como linguagem padrão em várias das disciplinas do MIT, seguem alguns links:

A Gentle Introduction to Programming Using Python

Introduction to Electrical Engineering and Computer Science I

A Gentle Introduction to Programming Using Python

Abraços pessoal,

Espero que tenham gostado desta ferramenta.

Victor Jabur.