Publicado por paulo.sales e arquivado em BLOB, CLOB, database, Framewoks, Hibernate, java, JPA, PFK, tags: blob, clob, generatorvalue, genericgenerator, hibernate, java, jpa, ldap, onetoone, pfk, primarykeyjoincolumn, shared primary key Algumas vezes precisamos configurar o JPA para respeitar um relacionamento um para um da base de dados que está modelado com um campo PFK na tabela filha do relacionamento. Tive a necessidade de configurar um relacionamento deste tipo utilizando o JPA com Hibernate, após muito googlar e achar somente um post que explica como se faz essa configuração resolvi escrever este post explicando minha experiência. O Problema Vamos considerar que temos um relacionamento um para um na base de dados envolvendo a tabela pai Usuario e a tabela filha LDAP. Este relacionamento foi necessário devido ao fato que a tabela LDAP armazena um campo do tipo CLOB. Este campo poderia estar na tabela Usuario evitando ter mais uma tabela no modelo de dados, mas podemos analisar alguns problemas em ter este campo na mesma tabela usando o JPA. Segue abaixo duas situações que afetam a performance da aplicação na utilização do campo CLOB na tabela Usuario: - Utilização do find(id): Se a aplicação faz muitas chamadas ao método find do EntityManager para recuperar os dados do usuário o JPA sempre traz o campo CLOB para memória. Muitas vezes não é necessário que o JPA traga o campo CLOB pois ele não é utilizado sempre, isso afeta diretamente a performance da aplicação.
- Utilizaçao de JPA-QL ou HQL: Para consultas utilizando JPA-QL que envolve a entidade Usuario e outras entidades o Hibernate pode executar um ou mais join(s) entre as tabelas. Se temos uma tabela com um campo CLOB em uma consulta complexa que envolve muitos joins a consulta é prejudicada em performance.
Por isso uma das soluções para este problema é separar o(s) campo(s) CLOB ou BLOB em outra tabela utilizando um relacionamento um para um entre as tabelas. Vamos à prática Abaixo segue o código da entidade Usuario: public class Usuario {
@Id
@GeneratorValue(strategy=StrategyType.AUTO)
@Column(name="usuario_id")
private Long id;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JoinColumn(name="Usuario_id", insertable = true, updatable = false)
private LDAP ldap;
// os getters e setters
} Abaixo segue o código da entidade LDAP: public class LDAP {
@Id
@GeneratorValue(generator = "myForeignGenerator")
@GenericGenerator(name = "myForeignGenerator", strategy = "foreign",
parameters = {
@Parameter(name = "property", value = "usuario")
})
@Column(name = "usuario_id")
private Long id;
@Lob
@Column(name = "ldap")
private
@OneToOne(optional = false)
@PrimaryKeyJoinColumn
private Usuario usuario;
// os getters e setters
}Explicando o código Para mapear o relacionamento PFK (PrimaryForeignKey) usando anotações você deve anotar com @PrimaryKeyJoinColumn o campo usuario da entidade LDAP, esta anotação informa o JPA que este relacionamento está sendo mapeado através do campo que é chave primária da tabela. O JPA não inclui um método padronizado para tratar a geração de chave primária para o relacionamento PFK, o que significa que você deve ser o responsável por setar o identificador à entidade LDAP antes de savar. O Hibernate tem uma extensão de anotação que permite customizar um gerador de identificador que pode ser usado para a entidade LDAP. A anotação do Hibernate @GenericGenerator que possibilita criar um gerador customizado que é identificado pela propriedade name, a propriedade strategy deve ser setada para foreign e deve ter a anotação @Parameter para identificar que a foreign key está sendo mapeada pelo atributo usuario. Fácil assim você pode configurar um relacionamento OneToOne que representa uma PFK na base de dados. Lembre-se que o Hibernate não sabe setar um valor para o @Id da entidade LDAP, pois anotamos o @Id com um GeneratorValue customizado. Portanto, não se esqueça de setar o @Id pela aplicação. Observações O JPA permite que um atributo de uma entidade seja anotado com @Basic(fetch = FetchType.LAZY), mas este atributo deve ser um campo da tabela e não um relacionamento. Sendo assim podemos supor que anotando dessa forma o campo Usuario.ldap o JPA vai trazer o campo somente quando for utilizado e não em todas as consultas á entidade, mas isso não acontece. Pesquisando a respeito desta anotação eu li no livro Java Persistence with Hibernate que não são todos os banco de dados que implementam esta funcionalidade de LAZY em um campo CLOB ou BLOB o único que implementa é o PostgreSQL. O relacionamento PFK na base de dados não é muito conhecido e pouco usado, mas nunca sabemos quando ele vai aparecer em nossas vidas para atormentar nossa configuração de mapeamento do JPA. Este tipo de relacionamento também é conhecido como Shared Primary Key. Bom fica ai a dica pra vocês que estão precisando desta característica na aplicação. Até a proxima pessoal!  Paulo R. A. Sales – @salespaulo 1 comentário » 
Depois de um bom tempo em desenvolvimento, no dia 25 de setembro foi liberada a tão esperada versão 2.0 do framework Apache-Mina (Multi-purpose Infrastructure for Network Applications). Para quem ainda não teve contato com esse framework, com o próprio nome diz, ele tem como propósito facilitar o desenvolvimento de aplicações em rede que requerem conectividade com alta performance e alta escalabilidade. A grande vantagem de utilizar MINA, é que ele tem uma API abstrata, orientada a eventos e assíncrona que já implementa diversas camadas de transporte como TCP/IP, UDP/ID e comunicação Serial via Java NIO, porém caso você precise de uma diferente, basta desenvolver e plugar. Essa abstração faz com que o desenvolvedor se preocupe efetivamente com a lógica do protocolo que está desenvolvendo. A figura abaixo demonstra como fica uma aplicação baseada no Apache MINA 
Recomendo esse framework para aplicações JAVA que atuam como servidores (e.g. servidor de transações). MINA 2.0 - Requisitos: JDK 1.5 ou superior. SLF4J 1.3 ou superior.
- Compatibilidade: A nova API é parcialmente compatível com a versão 1.x. Partes complexas da versão 1.x foram simplificadas para ficarem mais intuitivas.
- Principais correções disponíveis nessa última versão:
- Eliminação de problemas de NullPointerException ao abrir socket para um localhost.
- Possibilidade de mais de um codec no chain.
- Correção no NioProcessor que levava a CPU a 100% no Linux.
- Demais correções disponíveis no changeLog.
E pensando em melhoria contínua, o desenvolvimento da versão MINA 3.0 já começou. Confira mais no site do APACHE-MINA. []´ssss Rodrigo Russo @zrusso Nenhum comentário » O que me motivou a escrever este post foi a leitura do capítulo 3.2 do livro Arquitetura e Design de Software e do post Avaliação de Desempenho de Sistemas – Parte 1. Meu objetivo é explicar resumidamente o funcionamento do gerenciamento de memória e das características do Garbage Collector na Java HotSpot Virtual Machine, implementação da JVM da Sun. Como é Dividida a Memória Sabemos que o gerenciamento automático de memória é uma tarefa complicada e custosa para o desempenho da aplicação, portanto, existem alguns algorítmos que realizam essa tarefa. O algorítmo padrão usado pela JVM da Sun é chamado de generational copying que realiza esse gerenciamento dividindo a memória 3 partes copiando os objetos entre as partes comforme necessário. 
- Young Generation: É menor espaço de memória do Heap e armazena os objetos de ciclo de vida curto. Todo objeto instânciado é primeiramente armazenado nesta parte. Normalmente o tamanho inicial é de 2.2 MB.
- Old Generation: É a parte maior destinada aos objetos considerados maduros ou aqueles que tem um ciclo de vida maior.
- PermGen: É o espaço de memória fora do Heap destinado a objetos internos da JVM como objetos Method, Class, Pool de Strings, etc.
![]()
A idéia do algorítmo é coletar os objetos inutilizados somente na Young Generation, sempre que esta memória estiver esgotada. Por ser o menor espaço de memória o desempenho é muito maior. O espeço destinado para a Young generation é pequeno e dividido em 3 partes, uma parte chamada de eden e outras duas partes chamadas de survivor, todo objeto instanciado é colocado no eden e a cada passagem do Garbage Collector por este espaço de memória os objetos sobreviventes são copiados para os survivor spaces. Este processo acontece algumas vezes até o objeto se tornar maduro o suficiente para ser copiado para a Old Generation que é conhecida como ternured pela Sun. O espeço de memória Old Generation também é analisado pelo Garbage Collector, mas com menos intensidade. Esta varredura é chamada de Full GC e é considerada muito custosa para o desempenho da aplicação por isso, por padrão, não é frequente a varredura do Garbage Collector na Old Generation. Apesar do espaço de memória PermGen não pertencer ao Heap este espaço também é coletado durante o Full GC. Portanto, assim o algorítmo do Garbage Collector coleta os objetos inutilizados e copia os objetos que ainda estão sendo referênciados para os espaços survivor até serem duradouros o suficiente para serem copiados para a Old Generation. Neste processo de limpeza e cópia acaba criando lacunas no espaço de memória que são eliminadas pela compactação dos objetos realizada pelo Garbage Collector. Essa compactação organiza os os objetos novamente em memória a cada Full GC. Opções da JVM O espaço de memória destinado a armazenar os objetos de uma aplicação se chama Heap e este espaço pode ser controlado pelas opções -Xms e -Xmx da JVM. A opção -Xms especifica o tamanho inicial do Heap e a opção -Xmx o tamanho máximo do Heap, inicialmente o Heap pode ter um tamanho de memória que pode ser expandido até o tamanho máximo caso seja necessário. Se as duas opções tiverem o mesmo valor indica que o Heap não pode ser remanejado. Segue um exemplo de configuração do Heap com tamanho inicial de 64MB e tamanho máximo de 128MB: java -Xms64m -Xmx128m Main
Além das opções de configuração do Heap temos a opções de configurar o tamanho máximo do PermGen usando a opção -XX:MaxPermSize=. Esta opção é útil para contornar os erros de OutOfMemoryError acusando fim do PermGen space. Segue um exeplo de configuração do PermGen com tamanho máximo de 256MB: java -XX:MaxPermSize=256m Main
Existem algumas opções avançadas como -XX:MinHeapFreeRatio, -XX:MaxHeapFreeRatio e -XX:NewRatio que são muito importantes para aumentarmos a performance de uma aplicação seguindo as características da aplicação e do Hardware que ela será instalada. Como a JVM reajusta a memória Heap conforme a necessidade temos um percentual de aumento e diminuição desta memória, o valor dete percentual pode ser especificado pelos parâmetros -XX:MinHeapFreeRatio e -XX:MaxHeapFreeRatio. Segue um exemplo de configuração com um mínimo de 40% do Heap liberado e um máximo de 70% do Heap liberado: java -XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=70 Main
A opção -XX:NewRatio especifica uma proporção entre os espeços de memória no Heap, entre a Young Generation e a Old Generation. Normalmente esta opção é setada com o valor 2 que diz que a proporção da Young Generation em relação a Old Generation é de 1:2. Segue um exemplo de configuração de uma Old Generation 3 vezes maior que a Young Generation: java -XX:NewRatio=3 Main
São inúmeras as opções de configuração de como o gerenciamento de memória e o Garbage Collector podem se comportar, essas e mais opções podem ser analisadas neste link. Conclusão Neste post eu demonstrei como é o gerenciamento padrão e o algorítmo padrão do Garbage Collector do Java HotSpot Virtual Machine da Sun e demonstrei algumas opções de parâmetros que podem ser passados para a JVM visando melhorar o desempenho da aplicação. Não quis me aprofundar muito só gostaria de passar um pouco o quanto é essencial e fital para uma aplicação crítica uma boa análise e configuração de desempenho na JVM que ela será executada. Em outro post pretendo aplicar algumas configurações em um aplicativo mostrando na prática o quanto pode ser vantajoso conhecer a JVM. Espero que tenham gostado e deixem seus comentários! =D 1 comentário » A análise de desempenho consiste em identificar e mensurar as atividades de um sistema durante um determinado intervalo de tempo, a fim de estabelecer um plano de aumento de performance. O resultado desta análise é a compilação de uma lista de ações contendo o ganho, a prioridade e o impacto no sistema. Desta forma, pode ser decidido a ordem em que serão tomadas as ações. Na plataforma Java são muitos os aspectos que podem comprometer o desempenho de um sistema, entre eles: Dimensionamento do pool de conexões – se, por exemplo, um banco de dados tiver um limite de 100 conexões e você possuir 3 aplicações com pools de 100 conexões (300 conexões ao total) você pode ter alguma requisição aguardando uma conexão ser liberada pelo pool, ocasionando lentidão no sistema. PreparedStatements – Normalmente uma conexão tem uma relação de “um pra um” com um PreparedStatement, entretanto, a criação deste objeto pode ser custoso. Em servidores de aplicações modernos é possível “cachear” PreparedStatements. Para cada conexão é criado um cache, tornando as consultas ao banco de dados mais veloz. É possível configurar o tamanho do cache de PreparedStatements o que pode impactar na velocidade do sistema para baixo ou para cima. Gerenciamento de memória – Embora a JVM possua um mecanismo eficiente de coleta de lixo, um bug na aplicação pode fazer com que o coletor de lixo (garbage collector) não identifique os objetos que devem ser excluídos da memória, resultando em um fenômeno conhecido como vazamento de memória (leak memory) e consequentemente em OutOfMemoryError. Outros – Outros fatores envolvendo e/ou não a plataforma Java podem ocasionar lentidão em seu sistema como CPU, Run Queue, I/O, Network Throughput e etc. Por onde iniciar É muito comum iniciar uma análise de desempenho “atacando” os gargalos. Em Java, um dos problemas mais comuns esta justamente no gerenciamento de memória, ocasinado pela sua aplicação ou por um framework de terceiro. Muitas vezes apenas a atualização de um framework pode garantir um ganho enorme de performance. Neste primeiro POST, irei abordar uma das maneiras de analisar a heap de uma aplicação Java, tendo como foco a identificação de vazamento de memória. Entendendo a coleta de lixo De uma maneira bem simples, um objeto é elegível a ser coletado pelo mecanismo de coleta de lixo quando nenhum outro objeto da heap o referencia. Desta forma, se criamos uma aplicação cujo a soma de número de objetos instanciados (em bytes) é maior que o tamanho configurado da heap, então acontece OutOfMemoryError. É muito comum isto acontecer em relatórios cujo o número de registros apresentados ao usuário é muito grande e em funcionalidades stateful. É justamente nesta parte do sistema que devemos suspeitar em um primeiro momento. Identificando “O” vazamento de memória Muitas vezes nós, desenvolvedores, não temos a noção (embora devessemos) de qual funcionalidade é mais utilizada do sistema e para sistemas com muitas funcionalidades fica ainda mais difícil saber por onde começar, desta forma, uma maneira eficiente de identificar vazamento de memória é no sistema em produção. A maneira mais simples de identificar um vazamento de memória é através de JMX, entretanto, para que isto funcione é necessário que JMX esteja ativado no servidor, o que pode ser incomum (ou apenas não permitir conexão remota). A maneira que irei demonstrar a seguir, consiste em utilizar do log do garbage collector para obter um gráfico similar ao obtido pelo jconsole, pois é muito mais simples você convencer ao administrador a ativar o log do que o suporte a JMX (ou conexão remota via JMX). Obtendo o log do GC Para ativar o log da JVM (Sun Hostpot) passe os seguintes parâmetros ao inicia-la: -verbose:gc -Xloggc:PATH_TO_LOG_FILE Exemplo: java -Xms16M -Xmx16M -verbose:gc -Xloggc:/logs/gc.log -jar leak-memory.jar O que significa: Execute o programa leak-memory.jar com tamanho inicial e total de 16M (-Xms16M e Xmx16M), ative o log do GC (-verborse:gc) e grave no arquivo /logs/gc.log (-Xloggc:gc.log) O arquivo /logs/gc.log deve conter algo como: 9.317: [GC 4160K->1491K(15744K), 0.0190010 secs] 27.595: [GC 5651K->5302K(15744K), 0.0467940 secs] 34.715: [GC 9462K->9182K(15744K), 0.0414860 secs] 34.757: [Full GC 9182K->9177K(15744K), 0.0700270 secs] Exemplo prático Observe a imagem abaixo:  Leak Memory Para analisarmos o log do GC, criei uma aplicação Swing que instância um número determinado de objetos User e adiciona a uma collection (List, atributo da classe Main), estes objetos estão sempre sendo referenciados, desta forma, nenhum deles será coletado pelo coletor de lixo. DICA: Configure o programa para instanciar 1000 objetos User e clique no botão GO! a cada 10 segundos, depois de mais ou menos 5 minutos acontecerá o OutOfMemoryError indicado pela seguinte mensagem: Exception in thread “AWT-EventQueue-0″ java.lang.OutOfMemoryError: Java heap space Obtendo o gráfico da heap Para obtermos o gráfico da memória da JVM utilizaremos o gc.log gerado. Você pode parsear o arquivo e transforma-lo em um CSV criando o gráfico através de um programa de planilha como o excel, mas pra agilizar o processo, iremos utilizar o programa gcviewer. Utilizando o gcviewer, abra o arquivo gc.log (obtido pela experiência proposta acima) e será apresentado um gráfico como este:  gcviewer No gcviewer desabilite todas as opções (menu view) deixando apenas Data Panel, Full GC Lines e Used Heap. Em degradê (vermelho pra branco) temos o tamanho total da heap (~16 MB), a linha horizontal azul representa o tamanho em bytes usado na heap pela aplicação e as linhas verticais pretas representam o momento que foi efetuado o Full GC. É possível identificar que nesta aplicação existe um vazamento de memória devido ao used heap space sempre crescer e nunca diminuir (o que seria desejável em uma aplicação saudável). Ou seja, em uma aplicação normal, quando é executado o GC a parte utilizada da heap deveria diminuir, ou seja, a linha deveria descer. Perceba que mesmo ao ser executado o full GC a linha não desce, o que é totalmente compreensível pois estamos referenciando todos os objetos que criamos. Além disso, um pouco antes de ocorrer o OutOfMemoryError muitos full GCs acontecem o que faz com que a aplicação fique muito lenta. Identificando “ONDE ESTÁ” o vazamento de memória Existem muitas maneiras de descobrir onde está o vazamento de memória. Irei descrever duas maneiras de se obter um dump para podermos analisar os objetos da heap. Heap dump on OutOfMemoryError – Através de um parâmetro da JVM um arquivo de dump com a extensão .hprof será criado no diretório em que foi executado o programa, quando ocorrer um OutOfMemoryError. Exemplo: java -XX:+HeapDumpOnOutOfMemoryError -Xms16M -Xmx16M -verbose:gc -Xloggc:/logs/gc.log -jar leak-memory.jar Heap shot – Consiste em tirar um shot da heap com o programa em execução. Para tanto, precisamos saber qual o PID do processo Java que queremos obter o dump, usaremos então o jps para obter o PID e o jmap para obter o dump (ambos já vem com a JVM): henrique@henrique-ubuntu:/logs$ jps 12594 Jps 12577 jar 10857 gcviewer-1.29.jar 10363 Main No caso, Main é a classe principal do leak-memory.jar. Para obtermos o dump deste programa executamos: jmap -F -dump:format=b,file=dump.hprof 10363 Após obtermos o dump (de qualquer uma das maneiras descritas acima) podemos carregar um relatório que nos possibilitará enxergarmos o que está acontecendo dentro da heap. Para tanto, utilizaremos uma outra ferramenta da JDK chamada jhat. OBS: Após obtermos o dump é desejável retirá-lo de produção para um máquina local. henrique@henrique-ubuntu:~/dev/workspace/netbeans/leak-memory/dist$ jhat java_pid12635.hprof Reading from java_pid12635.hprof… Dump file created Sun Apr 18 19:00:44 BRT 2010 Snapshot read, resolving… Resolving 755646 objects… Chasing references, expect 151 dots……………………………………………………………………………………………………………………………………. Eliminating duplicate references……………………………………………………………………………………………………………………………………. Snapshot resolved. Started HTTP server on port 7000 Server is ready. Conforme descrito na saída do programa, um HTTP server é criado na porta 7000, desta forma basta acessarmos http://localhost:7000 e obtemos o relatório desejado, conforme demonstra a figura abaixo:  jhat Conforme é possível observar na imagem acima, todas as classes usadas no programa leak-memory.jar que não fazem parte da JVM são apresentadas e existem várias maneiras de encontrarmos a informação necessária. O que estamos procurando é um número grande de instâncias de uma mesma classe, desta forma ao clicar no link “Heap Histogram” será apresentada uma tela conforme demonstra a imagem a seguir:  histogram Se navegarmos um pouco sobre esta tela descobrimos que: Existem 359770 da classe java.util.LinkedList$Entry (cujo tamanho é 20 bytes) Existem 359756 da classe br.com.submundojava.User (cujo tamanho é 16 bytes) Ou seja, calculando o valor total destes objetos obtemos o seguinte resultado: (359770 * 20) + (359756 * 16) = 12951496 (~12.3 MB) Considerando que o número muito próximo de Entry e User deduzimos que Entry contém User. Além disso, o tamanho da JVM é de ~16 MB, o que significa que estas duas classes possuem instâncias que ocupam cerca de 80% da memória o que nos indica um forte candidato a ser o culpado pelo OutOfMemoryError. OBS: Os valores aqui descritos são aproximados, nas minhas contas o tamanho em bytes das instâncias de User bateram exatamente com o programa eclipse memory analyzer (citado no final), entretanto, o valor em bytes das instâncias de Entry foram apenas parecidos. Próximos passos Existem muitas maneiras (talvez mais fáceis e inteligentes do que esta) para encontrarmos gargalos na memória. Além disso, as ferramentas jmap e jhat possuem vários outros recurso, recomendo um estudo mais profundo das mesmas. No exemplo citado nest POST fica óbvio onde encontrar o problema e posso garantir que no mundo real a “coisa” não é tão simples assim. Além disso, existem ferramentas mais modernas do que o jhat, como é o caso do eclipse memory analyzer que possui recursos maravilhosos que indicam onde pode ser encontrado um possível vazamento de memória. Conclusão O que foi apresentado neste POST é uma maneira criativa de se obter informações sobre a heap de um sistema em produção, esta técnica já foi utilizada muitas vezes e solucionou diversos problemas. Espero ter contribuido com as horas de sono de algum leitor. Arquivos e Referências leak-memory.jar – Usado para efetuar os testes. Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine – O título já diz tudo http://www.arquiteturajava.com.br/ – Capítulo 3.2, Gerenciar memória não é simples http://www.javaperformancetuning.com/ – Alguns artigos bem antigos mas ainda válidos Forte abraço e aguardo seus comentários. 4 comentários » Tenho conversado com alguns amigos sobre os projetos que tenho participado no emprego novo e uma das dúvidas que surge é como utilizar repositórios em entidades, para tornar o domínio um pouco mais rico. Obviamente, este POST não tem o intuito de levantar nenhuma discussão sobre como você deve tratar sua camada de persistência e o seu domínio, mas sim, mostrar como é possível injetar repositórios dentro de entidades de maneira transparente usando Spring. Para saber mais a respeito do conceito por trás desta solução, leia este POST O problema Ao recuperar uma entidade (user, por exemplo) de um repositório, as dependências desta entidade devem ser injetadas manualmente dentro do método find. Exemplo: public User findById(Long id) {
User user = entityManager.find(User.class, id);
user.setUserRepository(this);
user.setRoleRepository(...);
user.setXXXRepository(...);
}Você talvez queira injetar só um repositório, mas talvez não. Imagine, se ao invés de todos estes setters eu apenas usasse a annotation @ResolveDependencies no método e todas as dependências (no caso, repositórios) fossem injetadas automaticamente na entidade. Um outro problema é que a cada nova dependência (não que eu pretenda ter muitas), mais um set você tem que efetuar no método find e isso daria um pouco mais de trabalho. Além disso, muitas vezes seus repositórios já estão sendo gerenciados pelo container de injeção de dependências e você não gostaria de criar uma nova instância a cada find que efetuar. A solução A classe ApplicationContext do Spring possui recursos para resolver as dependências de um objeto não gerenciado pelo Spring. Desta forma, se a minha entidade User possuir um atributo chamado userRepository anotado pela annotation @Autowired (Spring) ou @Inject (JSR-330), através da chamada do método ctx.getAutowireCapableBeanFactory().autowireBean(user), todas as dependências serão injetadas. Onde, ctx é uma instância de ApplicationContext, user é uma instância de User e userRepository é uma instância de UserDAO gerenciada pelo Spring. A seguir, as classes citadas: UserRepository.java public interface UserRepository {
public User findById(Long id);
public void save(User user);
}UserDAO.java @Named("userRepository")
public class UserDAO implements UserRepository {
@Override
@ResolveDependencies
public User findById(Long id) {
return new User(id);
}
@Override
public void save(User user) {
System.out.println("salvando user id=[" + user.getId() + "]");
}
}A classe UserDAO, foi anotada pela annotation @Named que faz parte da JSR-330 (Dependency Injection) suportada pelo Spring, o parâmetro passado (userRepository) é a String que identifica o DAO dentro do container de injeção de dependência (no caso, Spring). Toda classe anotada por esta anotação, será gerenciada pelo Spring. O método save, simplemente imprime uma mensagem com o id do user. O método findById, foi anotado pela a annotation @ResolveDependencies, que utilizaremos para indicar que a entidade retornada por este método deve ter todas as suas dependências resolvidas (injetadas). A mágica Como foi dito anteriormente, o Spring tem condições de resolver dependências de objetos não gerenciados pelo mesmo, desta forma, utilizaremos este recurso para o exemplo acima: SpringUtils.java public abstract class SpringUtils {
public static void autowire(Object obj, ApplicationContext ctx) {
ctx.getAutowireCapableBeanFactory().autowireBean(obj);
}
}Esta classe abstrata possui um método chamado autowire que, simplesmente, injeta as dependências (contidas no container, no caso, ApplicationContext) em um dado objeto (obj). Para juntarmos tudo, utilizaremos AOP para interceptarmos os métodos anotados por @ResolveDependencies e, através da classe SpringUtils, resolvermos as dependências do objeto de retorno, conforme demostra a classe a seguir: DetachedSpringBeanDependencyResolver.java @Aspect
@Named
public class DetachedSpringBeanDependencyResolver {
@Inject
@Named("applicationContext")
private ApplicationContext applicationContext;
@AfterReturning(value="@annotation(br.com.submundojava.ResolveDependencies)", returning="retVal")
public void resolve(Object retVal) throws Throwable {
if(retVal != null)
SpringUtils.autowire(retVal, applicationContext);
}
}Aqui complica um pouco, mas é bem simples: @Aspect, sucintamente informa que esta classe utilizará AOP. @Named, um objeto gerenciado pelo Spring. @Inject + @Named(“applicationContext”), como esta classe também será gerenciada pelo Spring e no próprio Spring existe uma instância de ApplicationContext (obtida pelo nome applicationContext) podemos então injetar o applicationContext dentro do aspecto, pois o SpringUtils precisa do applicationContext para obter as instâncias que serão injetadas no objeto retornado. @AfterReturning, indica que após o retorno do método anotado pela annotation @ResolveDependencies e antes de retornar ao cliente, o aplicativo deverá passar pelo método resolve. Método resolve(retVal), acho que fica óbvio que retVal é o valor retornado pelo método anotado por @ResolveDependencies. No caso o retorno é uma instância de User. SpringUtils.autowire, resolve todas as dependências contidas em applicationContext, do objeto retornado. Para finalizar, a entidade User, a anotação @ResolveDependencies e o application-context.xml User.java public class User {
private Long id;
@Inject
@Named("userRepository")
private UserRepository userRepository;
public User(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void save() {
if(userRepository == null)
throw new IllegalStateException("userRepository == null");
userRepository.save(this);
}
}ResolveDependencies.java @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ResolveDependencies {
}application-context.xml <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Procura por classes anotadas nos sub-pacotes do base-package -->
<context:component-scan base-package="br.com.submundojava" />
<!-- Ativa suporte a aspectos -->
<aop:aspectj-autoproxy />
</beans>Executando public static void main(String argz[]) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:application-context.xml");
UserRepository repository = ctx.getBean("userRepository", UserRepository.class);
User user = repository.findById(1L);
user.save();
}A saída deverá ser algo parecido com: salvando user id=[1] Conclusão O que tentei demonstrar aqui é uma maneira simples de resolver dependências de objetos que não fazem parte do container de injeção de dependências. Esta abordagem está sendo utilizada atualmente nos meus projetos e tem solucionado uma porção de problemas. Para ter uma melhor visualização você pode abaixar o projeto aqui. Certifique-se de ter java 5+ e maven 2 instalados. Dúvidas, críticas ou sugestões. Deixe um comentário. Abraços. 5 comentários » Publicado por paulo.sales e arquivado em Uncategorized Este post é a parte final do resumo do artigo “Exploring Patterns and Principles of a New Generation of SOA Solutions” da 22a. edição da revista “The Architecture Journal”. O artigo original discute sobre alguns desafios das tradicionais arquiteturas orientadas a serviço e explora alguns padrões e princípios para as novas gerações de arquiteturas orientadas a serviço, SOA. Abraçando a WEB: Serviços RESTful Apesar dos Web Services serem desenvolvidos para muitos protocolos o que vemos na grande maioria das implementações é o uso do protocolo HTTP como meio para o transporte de outro protocolo o SOAP. Esse excesso de abstração da camada de transporte impossibilita o uso dos recursos do protocolo HTTP. Por quê não utilizar HTTP somente? Um dos meios para utilizar o protocolo HTTP para distribuir serviços, Web Services, de maneira amigável seria o uso do REST. Seguindo os princípios do REST podemos ter uma arquitetura SOA altamente escalável. Sem dúvida REST se tornou uma alternativa muito atraente para substituir os Web Services SOAP/WS-*, resolvendo os problemas citados na Parte 1, como interoperabilidade e escalabilidade. Segue algumas características dos serviços RESTful: - Endereçamento de recursos/serviços através de uma URI
- Interações baseadas somente em HTTP
- Uso de métodos padrões HTTP (GET, POST, PUT, etc)
- Interações sem estado
- Possibilita diferentes formatos de distribuição
- Armazenamento em Cache
- Iteroperabilidade
- Escalabilidade
A simplicidade e o alto nível de interoperabilidade dos serviços em RESTful são alguns dos fatores que podem melhorar a agilidade da próxima geração de soluções SOA. Interoperabilidade com WS-* Apesar do notável trabalho acadêmico realizado na família de protocolos WS-* percebemos que ainda não supre as expectativas iniciais. Interoperabilidade e complexidade ainda são desafios importantes que encaramos na adoção dos protocolos WS-*. A melhor prática para melhorar a interoperabilidade dos protocolos WS-* é identificar as características dos consumidores dos serviços e criar endpoints distintos para cada particularidade desses consumidores. Por exemplo, vamos considerar um senário que devemos assegurar um Web Service que irá ser consumido por aplicações .NET, Sun Metro, Oracle Weblogic e Ruby on Rails. Neste senário podemos permitir que três endpoints com diferentes configurações de segurança atendam às diferentes características das aplicações que consomem o Web Service, como mostrado na Figura 6:  Padrão de múltiplos endpoints de serviços Federação de ESB: Um caminho menos árduo Vimos na sessão anterior que uma arquitetura com um ESB centralizado é uma das causas fundamentais de falhas na implementação de um sistema SOA. Após inúmeras tentativas de implementação de um ESB centralizado em grandes organizações a indústria está adotando um padrão mais ágil de implementação do ESB. Essencialmente, esse novo padrão visa particionar as funcionalidades em leves ESBs físicos que são agrupados como entidades federativas. Este padrão é comumente conhecido como Federação de ESB e representa uma das emergentes arquiteturas para implementar soluções ESB de alta escalabilidade. Com essa arquitetura podemos ter uma infraestrutura ESB específica para interfaces Business to Business, outra para troca de transações financeiras, como mostrada na Figura 6.
 Padrão de Federação de ESB Governça em SOA: Capitalizando SOA A limitada adoção do UDDI em sistemas SOA de grande porte tem sido um catalisador do surgimento de um modelo mais leve e mais interoperável de governaça em SOA, levando em conta tecnologias como REST e Web 2.0. Essencialmente, esses modelos foram criados para remover algumas complexidades que são apresentadas pelo modelo UDDI centralizado, substituindo algumas tecnologias por outras amplamente adotadas como HTTP, Atom e JSON. Uma das formas mais populares de implementar esse novo modelo de governaça SOA é utilizar um repositório de serviços RESTful. Neste modelo a implementação tradicional SOA como serviços, endpoints, operações e mensagens são representadas como recursos que podem ser acessados através de um conjunto de interfaces RESTful. O maior benefício que traz esse novo modelo é a interoperabilidade ganha utilizando interfaces RESTful, como mostrada na Figura 7, além de ser uma abordagem muito mais leve e flexível.  Repositório RESTful A computação em núvem pode auxiliar em uma arquitetura SOA híbrida, onde alguns componentes de um sistema SOA possam estar em uma outra infraestrutura. Alguns exemplos do uso da computação em núvem em um sistema SOA: - ESB na Núvem: Podemos hospedar um ESB na núvem? Claro que podemos! Este tipo de arquitetura possibilita disponibilizar todos os recursos de um ESB através de uma infraestrutura na Núvem.
- Serviços de Segurança na Núvem: Nos últimos anos temos percebido o aumento da adoção de serviços de segurança, assim como o Windows Live ID ou o Facebook Connect, e usar a Núvem para prover serviços de segurança pode facilitar a implementação de mecanismos altamente interoperáveis, disponibilizando serviços de autenticação, identificação e autorização a diversos clientes distintos.
- Serviços de Armazenamento na Núvem: Indiscutivelmente, serviços de armazenamento como Amazon S3 ou o Azure DB são os serviços mais atraentes encontrados na Núvem. Considerar esse mecanismo pode alavancar a flexibilidade e a interoperabilidade da troca de dados de seu sistema SOA, além de eliminar algumas complexidades existentes em se ter uma infraestrutura própria para armazenamento de dados.
 Serviços na Núvem Conclusão A tradicional arquitetura SOA implica em sérios desafios que tornam impraticável implementações em larga escala. Este artigo post sugeriu uma série de padrões que podem ajudar o desenvolvedor a ter uma implementação mais leve, interoperável e escalável de SOA, possibilitando realmente serviços de negócio com agilidade em senários de larga escala empresarial. Abstração de Protocolos: - Considerar primeiro o protocolo padrão, HTTP. Ele é muito leve e pode conversar com outros frameworks.
- Fazer uso do SOAP e WS-* quando houver necessidade de controle de transações ou a necessidade de muita performance no protocolo TCP/IP.
SOAP e WSDL: - Não fazer uso de SOAP e WSDL até ter certeza que vai precisar de algum recurso que eles oferecem.
- Considerar o uso de REST, JSON e Atom Pub como alternativas mais leves.
- Não cair na armadilha de gerar o WSDL a partir do código, o contrato vem sempre em primeiro.
Governaça - Fazer uso de um repositório de serviços para ajudar a gerenciar os serviços de sua empresa.
- Considerar o uso de um repositório de serviços RESTful.
Enterprise Service Bus - Não confundir um ESB com um sistema que processa eventos.
- Considerar o uso de federação de ESBs.
Serviços baseados na Núvem - Considerar serviços de segurança local, privado ou públic baseados na Núvem, é um dos os serviços mais maduros que existe na Núvem
- Considerar a possibilidade futura de serviços de armazenamento e serviços de ESB na Núvem.
A coisa mais importante que você deve ter em mente quando está contruindo sua aplicação SOA é o mantra “Convenção ao invés de Configuração”. Diminuindo o número de opções ao mínimo e fazendo somente o que são requisitos do negócio, você deve acreditar que é possível contruir uma arquitetura orientada a serviços que seja leve, fácil de manter e evoluir, mesmo em ambientes de larga escala empresarial. Minha Conclusão Acho que essa segunda parte extendeu um pouco, portanto, vou deixar minha conclusão para uma terceira parte.  Mesmo assim nessa segunda parte podemos ver as sugestões de Jesus Rodriguez e Don Demsak para solucionar alguns dos problemas encontrados nas implementações tradicionais SOA. Espero que tenham gostado, mas se não gostaram podem deixar suas queixas nos comentários irei acatá-las com certeza. Até a próxima parte… 1 comentário » Este post é um resumo do artigo “Exploring Patterns and Principles of a New Generation of SOA Solutions” da 22a. edição da revista “The Architecture Journal”. O artigo original discute sobre desafios das tradicionais arquiteturas orientadas à serviço e explora alguns padrões e princípios para as novas gerações de arquiteturas orientadas à serviço, SOA. SOA: Arquitetura sem RestriçõesSOA tem sido o cerne dos sistemas distribuídos nos últimos anos, prometendo agilidade na implementação de serviços de negócio através de interfaces de negócio. É muito comum encontrar um conjunto de características semelhantes entre os tradicionais sistemas em SOA, sendo elas: - A adotação do SOAP e WSDL como padrão para especificar o contrato, ou interface, dos serviços.
- O uso dos protocolos WS-* para pertimir algumas características de missão crítica aos serviços.
- O uso de um ESB central abstraindo diferentes orquestrações de serviço.
- O uso de um servidor de integração para gerenciar complexos processos de negócio, BPM.
- O uso de ferramentas de governança em SOA para gerenciar toda arquitetura de serviços.
 Figura 1: O modelo ideal SOA A arquitetura apresentada na Figura 1 pode ser considerada ideal pra sistemas SOA, mas esta arquitetura não leva em consideração as retrições que os sistemas SOA impõem à arquitetura, como interoperabilidade, performance e escalabilidade. A arquitetura apresentada na Figura 1 leva em consideração abstrair a complexidade de implementação dos sistemas SOA utilizando mais padrões e ferramentas. Ultimamente se ouve muito o mantra Conveção ou invés de Configuração, que remove opções de configuração, ou parâmetros, que aumentam a complexidade do sistema por convenções previamente adotadas. Utilize as opções de configuração somente quando for realmente necessário. SOAP: A ilusão de uma abstração da camada de transporteInicialmente o SOAP foi desenvolvido para abstrair os serviços das diferentes camadas de transporte, ou protocolos. Embora na teoria se mostre uma boa idéia, na prática constatamos que abstrair os serviços da camada de transporte exige um custo significativo de complexidade. Um exemplo da complexidade desta abstração é trafegar SOAP sobre HTTP, que hoje em dia é frequente. Por quê não usar o HTTP somente? WSDL: Em abundânciaO WSDL tem como propósito descrever os características dos serviços e as mensagens trocadas. Conceitualmente, o WSDL representa uma evolução para do antigo IDL. Ao utilizar o WSDL como padrão para especificar os serviços, esse se torna um artefato vital para as aplicações clientes. Essa relação entre o provedor de serviços e o cliente tende a ter um acoplamento alto, sendo que se houver uma alteração no contrato dos serviços o(s) cliente(s) serão afetados. Pensando em uma ambiente pequeno que envolve poucos clientes consumindo alguns serviços não é preocupante, mas em um ambiente empresarial e complexo o alto acoplamento entre o cliente e o provedor de serviços é um problema a se considerar.  Figura 2: Auto acoplamento com WSDL, o grande desafio de sistemas SOA de grande porte ESB: Ter ESB ou não ter ESBA integração heterogênea de sistemas, LOB, tem sido uma grande promessa dos sistemas empresarias SOA. Para pôr em prática essa integração entre diferentes sistemas heterogêneos foi determinado um conjunto de padrões que constituem o ESB. Ainda que não exista um padrão industrial que defina o que é um ESB, podemos adotar algumas características comuns entre os ESBs existentes no mercado. Atualmente o ESB é usado como um barramento central de serviços, como mostrado na Figura 3:  Figura 3: ESB centralizado Pode parecer uma boa estratégia arquitetural utilizar o ESB de forma centralizada, mas na prática esse tipo de arquitetura apresenta sérias limitações nos aspectos de gerenciamento, performance e escalabilidade. Ao inves do ESB se tornar um facilitador ele pode se tornar um gargalo em sistemas SOA. Protocolos WS-*: O lado negroOs protocolos WS-* criados para complementar características de segurança, confiabilidade e transacionabilidade ao SOAP/WSDL não receberam muita adoção em ambientes heterogêneos. Um dos motivos da não adoção dos protocolos WS-* está nas centenas de diferentes versões de protocolos WS-* existentes, isso cria desconfiança e descredibilidade na tecnologia. A interoperabilidade é o maior desafio das soluções baseadas em protocolos WS-*, como diferentes ferramentas de Web Services as vezes implementam diferentes protocolos WS-* teremos então diferentes versões dos mesmos protolos e mesmo diferentes aspectos da mesma especificação.  Figura 4: O desafio da interoperabilidade Governaça em SOA: Ditatura em SOAEm sistemas SOA de médio e grande porte existe a necessidade de versionar, monitorar e gerenciar os serviços de negócio e esse papel é realizado pelas ferramentas de governaça em SOA. O maior desafio dessas ferramentas de governança é permitir de maneira confiável o gerenciamento de complexos serviços de negócio, por este motivo as tecnologias de governança em SOA tem crescido muito nos últimos tempos. As tecnologias de governaça estão adotando uma arquitetura que virtualiza os serviços de negócio em um ambiente centralizado de governaça. Embora, essa arquitetura possa ser aplicada em sistemas SOA de pequeno porte, ela apresenta sérias limitações em termos de iteroperabilidade, performance e escalabilidade em ambientes de sistemas SOA de grande porte, como mostrado na Figura 5:  Figura 5: Modelo centralizado de governança SOA ConclusãoNesta primeira parte do artigo foi explorado as arquiteturas SOA adotadas comumente nos dias de hoje. Na abstração da camada de transporte dos serviços de negócio foi mostrada a adoção do protocolo SOAP utilizando a especificação WSDL para manter um contrato, ou interface, dos serviços de negócio disponíveis que acarreta em uma sobrecarga de protocolos e um aumento muito significativo do acoplamento entre o provedor de serviços de negócio e os clientes cosumidores destes serviços. Também foi mostrada a arquitetura centralizada do ESB que apresenta limitações em termos de gerenciamento, performance e escalabilidade. As diversas implementações dos protocolos WS-* que são utilizadas de forma não padronizada criando desconfiança e descredibilidade da tecnologia. Como a arquitetura centralizada do ESB foi mostrada a arquitetura que virtualiza os serviços de negócio em um ambiente centralizado de governança SOA que apresenta limitações de interoperabilidade, performance e escalabilidade. Na segunda parte deste artigo será discutido arquiteturas e tecnologias que visão melhorar alguns aspectos negativos mostrados nessa primeira parte do artigo. Até lá . Nenhum comentário » Na semana passada, Martin Fowler escreveu um artigo demonstrando as práticas que ele costuma utilizar ao trabalhar com expressões regulares. Neste artigo, o senhor Fowler propõe uma maneira de separar uma expressão regular em partes menores, recompondo tal expressão posteriormente para, entre outra coisas, dar maior legibilidade ao código. Ao acabar de ler o artigo a primeira coisa que veio em minha cabeça foi Fluent Interfaces. Na atualização de seu artigo, Martin Fowler cita este assunto (inclusive expôe sua preferência por não utilizar Fluent Interfaces) e sugere uma alternativa fluente criada por Joshua Flanagan para C#. Este post não tem como objetivo discutir se utilizar Fluent Interfaces, ou não, é a melhor maneira de resolver problemas relacionados a expressões regulares, mas sim, propor um exercício a fim de criar uma possível API fluente e, ao final do artigo, demonstrar uma pequena porção de código Java que pode solucionar alguns problemas relacionados a expressões regulares utilizando esta abordagem. O problema O grande problema de quando trabalhamos com expressões regulares é conhecer todos os recursos disponíveis (metacaracteres, quantificadores gananciosos, possessivos, relutantes, etc) para validarmos determinada String e posteriormente recuperarmos os grupos que nos interessa para aplicarmos a lógica que necessitamos. Além disso, quanto maior for a expressão mais difícil será de interpretá-la, por isso, utilizar a abordagem “Divisão e Conquista” é uma boa prática a ser seguida. A solução Criar uma API para abstrair os recursos (metacaracteres, quantificadores, etc) e disponibilizar ao usuário, métodos mais legíveis e simples de escrever. Para chegar a este fim, me propus a analisar as expressões regulares que mais utilizo no dia-a-dia, “mapeá-las” para o português estruturado e, então, escrever uma porção de código que reflita a interpretação do português estruturado o mais fluente possível. Interpretando Expressões Regulares Analisando uma expressão regular que tem como objetivo validar se Strings representam uma data em um formato dd/mm/aaaa (em java dd/MM/yyyy), temos: Expressão Regular: \\d{2}/\\d{2}/\\d{4} Português estruturado: A String em questão: Inicia com dois dígitos (numéricos), seguido de uma barra, seguido de dois dígitos (numéricos), seguido de uma barra e finaliza com quatro dígitos (númericos). Código Java: RegexComposer composer = RegexComposer.getInstance();
composer
.startsWith(exactly(2, digit()))
.followedBy(exactly(1, literal("/")))
.followedBy(exactly(2, digit()))
.followedBy(exactly(1, literal("/")))
.finishedWith(exactly(4, digit()));Parece bacana, mas não está ao contrário? Porque o quantificador vem antes do dígito no código Java e na expressão regular não? Pois ao interpretarmos a expressão regular em português estruturado, também passamos o quantificador a frente do dígito e é essa a diferença, pois, ao interpretarmos uma expressão como: \\d{2} nós não escrevemos “Esta expressão representa digítos (numéricos) dois“, e sim “Esta expressão representa dois dígitos (numéricos)”. Este foi o meu principal questionamento quando analisei a API do senhor Joshua Flanagan, pois acredito que ao utilizarmos a abordagem fluente, devemos valorizar a maneira como escreveríamos em uma língua (português/inglês) e não como é a sintaxe de uma determinada linguagem (de programação). Delimitadores Uma ocorrência constante (inclusive citado no artigo do Martin Fowler) é a utilização de delimitadores para separarmos os dados em Strings (tokens). São exemplos disto, os arquivos CSV (valores separados por vírgula) e também a data do exemplo anterior (separados por barra “/”). Desta forma, seria conveniente se pudessemos configurar a API para interpretar delimitadores. A seguir, demostro um exemplo de como isto poderia ser feito: Expressão Regular: \\d{2}/\\d{2}/\\d{4} Português estruturado: A String em questão: Inicia com dois dígitos (numéricos), seguido de dois dígitos (numéricos), finaliza com quatro dígitos (númericos) e é delimitada por uma barra. Código Java: RegexComposer composer = RegexComposer.getInstance();
composer
.startsWith(exactly(2, digit()))
.followedBy(exactly(2, digit()))
.finishedWith(exactly(4, digit()))
.delimitedBy(exactly(1, literal("/")));Tá começando a melhorar, mas existe um recurso muito importante que deve ser considerado, os agrupamentos. Agrupamentos Os agrupamentos em Java são efetuados através inserção da expressão em parênteses, isto possibilita recuperar a String de determinado grupo uma vez que foi validado que a String se encontra no padrão requerido. A seguir demonstro uma maneira que, inicialmente, acreditei ser adequada para este fim. Imagine que, após validar uma determinada data, você queira recuperar o dia, mês e ano separadamente para efetuar alguma lógica, então, teríamos algo como: Expressão Regular: (\\d{2})/(\\d{2})/(\\d{4}) Português estruturado: A String em questão: Inicia com dois dígitos (numéricos) agrupados, seguido de dois dígitos (numéricos) agrupados, finaliza com quatro dígitos (númericos) agrupados e é delimitada por uma barra. Código Java: RegexComposer composer = RegexComposer.getInstance();
Pattern p = composer
.startsWith(exactly(2, digit()).grouped())
.followedBy(exactly(2, digit()).grouped())
.finishedWith(exactly(4, digit()).grouped())
.delimitedBy(exactly(1, literal("/")))
.compile();
Matcher matcher = p.matcher("05/01/1979");
if(matcher.matches()) {
System.out.println("Dia: " + matcher.group(1));
System.out.println("Mes: " + matcher.group(2));
System.out.println("Ano: " + matcher.group(3));
} else {
System.out.println("Formato invalido");
}Legal, ao menos à primeira vista, entretanto eu ainda precisaria saber em qual grupo está o dia (matcher.group(1)), o mês (matcher.group(2)) e o ano (matcher.group(3)) e isto não é muito bacana. Creio que o ideal seria criarmos um alias para cada grupo e depois recuperarmos o número do grupo através do alias. Seria algo como .startsWith(exactly(2, digit()).grouped(usingAlias(“dia”))) e depois recuperarmos o grupo usando matcher.group(composer.getAliasGroup(“dia”)). Uma outra questão é que há outra interpretação para expressões agrupadas, por exemplo, “A String em questão: Inicia com um grupo de dois dígitos …”. Isto quer dizer que escreveriamos algo como: .startsWith(groupOf(exactly(2, digit())).usingAlias(“dia”)). Embora eu considere estas opções adequadas, não escrevi nada disso ainda. No exemplo, a seguir, irei demonstrar um pouco mais de detalhes do que já implementei. Exemplo prático Utilizando o exemplo que o Martin Fowler deu em seu artigo, irei validar se a String “score 400 for 2 nights at Minas Tirith Airport” se encontra no padrão correto e ,após isto, recuperar os números 400 e 2. Código Java: import br.com.submundojava.regexcomposer.RegexComposer;
import static br.com.submundojava.regexcomposer.quantifier.Quantifiers.*;
import static br.com.submundojava.regexcomposer.value.MetaCharacters.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
*
* @author Henrique Lima
*/
public class Test {
public static void main(String argz[]) {
RegexComposer composer = RegexComposer.getInstance();
Pattern pattern = composer
.startsWith(exactly(1, literal("score")))
.followedBy(oneOrMore(digit()).grouped())
.followedBy(exactly(1, literal("for")))
.followedBy(oneOrMore(digit()).grouped())
.followedBy(exactly(1, literal("night")))
.followedBy(zeroOrOne(literal("s")))
.followedBy(exactly(1, literal("at")))
.delimitedBy(zeroOrMore(whiteSpace()))
.finishedWith(oneOrMore(any()))
.compile();
Matcher matcher = pattern.matcher("score 400 for 2 nights at Minas Tirith Airport");
if (matcher.matches()) {
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
} else {
System.out.println("Formato inválido");
}
}
}Muito simples! Veja que não utilizamos nenhum metacaracter, nenhum quantificador, agrupamos os dados necessários e ainda utilizamos um delimitador. Uma pessoa nem precisaria conhecer de expressões regulares para validar a String (embora seja desejável). Perceba que novas coisas aconteceram e utilizamos import static do Java 5 para disponibilizarmos os métodos necessários à nossa classe. Além disso, utilizamos novos métodos quantificadores (não regex) que são: zeroOrMore(), para zero ou mais ocorrências (análogo a *), oneOrMore(), para uma ou mais ocorrências (análogo a +), zeroOrOne() para zero ou uma ocorrência (análogo a ?) e por final utilizamos o método compile() para obtermos um objeto do tipo java.util.regex.Pattern para podermos validar a String. Código fonte Abaixo segue o projeto que criei para efetuar este post. Para netbeans: download Para eclipse: download Considerações Finais Procurei demonstrar uma maneira fluente de trabalhar com expressões regulares, abstraindo seus recursos e disponibilizando métodos de alto nível. O exemplo que escrevi ainda está muito “verde”, com nomes esquisitos para algumas classes, métodos e pacotes. Na verdade, em alguns casos, nem sei se o inglês está correto. Além disso, existem muitas limitações, por exemplo, se eu quiser usar um delimitador e ao mesmo tempo validar um dos tokens com um outro RegexComposer, não é possível. Seria desejável poder aninhar RegexComposer’s para este fim. Outro detalhe é que os métodos da classe RegexComposer recebem sempre uma instância de Quantifier e deveriam receber uma instância de uma classe com um nome mais adequado, como Expression ou ExpressionGroup. Detalhes a serem resolvidos. Aguardo seus comentários e quem quiser entrar em contato, meu twitter é hgflima Um abraço. 5 comentários » Publicado por paulo.sales e arquivado em Geral É incrível como algumas empresas tratam o arquiteto de software como se fosse o brinquedo da vez. Lembra quando éramos pequenos e existia aquele brinquedo especial que todos queríamos ter e quem o tinha era visto como um ídolo diante a criançada. Pois bem, as empresas fazem o mesmo com o arquiteto de software! Pense na definição da profissão “Arquiteto de Software”, uma das muitas definições existentes segue abaixo: “A arquitetura de um software é a estrutura ou estruturas do sistema, o que compreende componentes de software, propriedades desses componentes que são visíveis externamente e o relacionamento entre eles”, Paul Clements, SEI. Muitas vezes os profissionais que trabalham nessa profissão não realizam exatamente o que devem, mas a empresa se enaltece por ter um arquiteto de software envolvido em um de seus projeto. O papel de um arquiteto de software é equiparado ao papel de um gerente de projetos, onde um gerência a tecnologia aplicada e o outro gerência pessoas. Portanto, temos que o fracasso de um projeto por causa de uma má gestão pessoal é culpa do gerente de projetos e o fracasso de um projeto por causa de um problema técnico é culpa do arquiteto de software. Ao desenvolvermos um software para um cliente temos que seguir uma lista de requisitos que são eles funcionais e não-funcionais. Os requisitos funcionais estão quase sempre relacionados ao negócio da empresa, como exemplo “Realizar pedido”. Já os requisitos não-funcionais estão relacionados com performance, disponibilidade, interroperabilidade, escalabilidade, segurança, performance, usabilidade, etc. São inúmeros os requisitos não-funcionais e às vezes são desprezados por muitos, principalmente o gerente de projeto. O gerente de projetos quer ver o pedido sendo realizado e o dinheiro entrando na conta da empresa e na dele, mas temos um enorme problema nisso! Ao ignorarmos os requisitos não-funcionais, que nunca são poucos, estamos escondendo o sol com a peneira e esperando para ficar cegos. O papel do arquiteto de software deve ser de não permitir que os requisitos não-funcionais sejam ignorados e nem tão pouco deixados para última hora. Muitos requisitos não-funcionais podem interferir diretamente em um requisito funcional. O Arquiteto deve entender e compreender os requisitos não-funcionais e propor soluções para resolvê-los. Mas isso não é o que acontece nas empresas por ai. Primeiro, para a maioria das empresas é inaceitável o arquiteto de software ter o mesmo nível que um gerente de projeto. Segundo, nunca um arquiteto de software tem voz ativa diante uma equipe de desenvolvedores que estão acostumados a fazer telas em linha de produção. Por isso um arquiteto de software não é um super brinquedo que somente serve para se exibir e que com o tempo perde a utilidade. Um arquiteto de software é o ícone muito influente no ciclo de vida de um projeto e é dever dele garantir que o software funcione desde o início de sua vida em um ambiente de produção. O link de referência citado abaixo tem uma ótima descrição do papel de um arquiteto, todas as empresas deveriam ler e seguir este documento, assim teríamos software funcionando! http://www.wthreex.com/rup/process/workers/wk_archt.htm Links relacionados: http://pt.wikipedia.org/wiki/Arquitetura_de_software http://www.marcomendes.com/ArquivosBlogIntroducaoArquiteturaSoftware.pdf Nenhum comentário » Publicado por paulo.sales e arquivado em Geral Esta é uma das muitas situações que acontece dentro de um ambiente de trabalho, quando o ócio do time não é pura preguiça e sim ocasionado por outro problema. Um dos problemas que podem ocasionar eu vou explicar abaixo já que vivi uma situação destas. Na informática, mais precisamente na área de desenvolvimento, trabalhamos focados em objetivos e entregáveis bem definidos. Claro que sem uma meta bem estabelecida e tarefas bem planejadas não terá trabalho a ser entregue. Para mostrar o que estou tentando dizer vou usar como exemplo em cenário ágil. Como a maioria deve saber o Product Owner com ajuda do time na metodologia Scrum deve lançar atividades que representam os requisitos de um projeto e priorizar cada um desses requisitos. Após o Product Backlog ter sido analisado parte-se para os planejamentos das atividades e assim por diante. Se o Product Backlog não foi bem analisado e bem priorizado o time acaba tendo muita dificuldade em realizar o planejamento das estórias e atividades. Portanto, durante o andamento da Sprint podemos perceber um aumento de atividades que não foram previstas com antecedência gerando uma certa insegurança para o time se realmente aquela estória ou mesmo requisito é necessário ou se deve realmente ser realizada da forma que foi planejado. Isso faz com que o tempo gasto do time até descobrir que a Sprint deve ser cancelada por ter a meta alterada é uma perda para o andamento do projeto. E até o momento de decidir cancelar a Sprint o time detém seu tempo com várias discusões e elocubrações à respeito do real motivo de tudo estar indo para as cucuias. Isso ao olhar do Product Owner e/ou dos Stackholders aparente puro ócio do time. Como o conceito de <em>pronto</em> não foi bem definido eles não visualizam os entregáveis no tempo em que imaginavam e culpam o time por divagar demais em idéias alheias. Mas na verdade o time não tem culpa e não está ocioso, o time está correndo atrás para saber se realmente ocorreu um problema durante a Sprint para cancelar e começar novamente de maneira correta. Já que a metodologia ágil prega o princípio de “Quanto antes encontrarmos o problema mais rápido temos que discuti-lo”. Isso pode ser ocasionado por falta de comunicação e entendimento do Scrum Master, time e Product Owner. Um gráfico que pode ser usado para perceber com rapidez que a sprint esta caminhando em direção a outra meta é o Burn-Up. Por isso valorizem muito a comunicação e o bom entendimento dos requisitos que serão entregue. Acredite, quem não comunica, se estrumbica. Referências: - Tem um artigo bem direto quanto a utilização e alguns benefícios de utilizar o burn-up chart: Burn-up e Burn-Down.
Nenhum comentário » |