edermag, só de boa: 2013
Algoritmos de ordenação String (char) e o uso de Collator no Java 7
Um dos temas que eu mais gosto na área da computação é a Análise de Algoritmos, especificamente a ordenação de estrutura de dados. O Java utiliza diversos algoritmos na classificação de dados em sua API, com o foco em unir a eficiência com a performance. Inclusive, no Java 7 foram implementadas algumas melhorias no uso de algoritmos de ordenação. Nesse post eu descrevo as características sobre ordenação de caracteres e String em Java.
Arrays de char
Pra começar, um exemplo simples demonstrando como ordenar os caracteres que compõe uma String:
String content = "ebdca";
System.out.println(sorted); //abcde
A classe Arrays do Java ordena o array chars na ordem natural, alfabética. Mas o que acontece dentro do método sort? Como o array é pequeno o algoritmo utilizado para ordenação é o Insertion Sort, aonde a comparação ocorre no sentido de elementos da esquerda para a direita, garantindo que todos os elementos à esquerda estejam ordenados.
Array original -> e b d c a
Comparação 1 -> b < e, sim -> Resultado: b e d c a
Comparação 2 -> d < e, sim -> Resultado: b d e c a
Comparação 3 -> d < b, não
Comparação 4 -> c < e, sim -> Resultado: b d c e a
Comparação 5 -> c < d, sim -> Resultado: b c d e a
Comparação 6 -> c < b, não
A análise matemática de Insertion Sort varia de acordo com o conteúdo do array. No melhor caso, quando o array já está ordenado, o número de comparações seria linear O(n). Enquanto no pior caso, se os elementos estiverem em ordem decrescente, o número de comparações seria quadrático O( N^2). Em arrays pequenos Insertion Sort é uma ótima opção.
Em um array maior, com mais de 47 caracteres, o algoritmo utilizado é o QuickSort c/ Dual-Pivot. Como o nome diz, trata-se de uma variação de QuickSort com dois pivôs, abordagem parecida com QuickSort 3-way. Alguns estudos apontam que essa estratégia pode reduzir em até 10% o tempo em relação ao QuickSort tradicional. O algoritmo é recente, reformulado em 2009 por Vladimir Yaroslavskiy. O uso desses algoritmos fazem parte de melhorias do Java 7, verifique a classe DualPivotQuicksort.java.
Um detalhe muito importante sobre a comparação de char, é que o Java utiliza o Unicode para determinar a pontução de cada caracter. Nesse caso 'A' vem bem antes de 'a', e 'ã' vai bem depois de 'a'. Modificando o conteúdo da variável content para "abcdeãA", o valor de sorted seria "Aabcdeã". Volto a falar sobre isso no decorrer do post.
O próximo trecho de código também é bem simples, nele coloco quatro strings em um array e peço para a classes Arrays realizar a ordenação:
String content[] = { "Jose", "Andre", "Claudia", "Bruna" };
for (String s: content) {
Por se tratar de um array pequeno, a API do Java utiliza o Binary Insertion Sort, uma alternativa um pouco mais performática que usa a busca binária (binary search) para determinar a posição correta da troca. No pior caso o número de comparações é linearítmica O(n log n).
Já em arrays maiores, a partir de 32 elementos, o algoritmo de ordenação é o TimSort. TimSort é um algoritmo derivado de Merge Sort e Insertion Sort, no melhor caso o número de comparações é linear O(n), no pior é linearítmico O(n log n). Esse também é um algoritmo recente, descoberto em 2002 por Tim Peters, para a linguagem Python. O uso desses algoritmos também fazem parte de melhorias do Java 7, verifique a classes TimSort.java e ComparableTimSort.java.
Importante a ressalva de quem define a classificação de cada objeto durante o sort é o método compareTo de Comparable.
Coleções de String
No próximo exemplo uso a coleção TreeSet para organizar algumas Strings:
Collection<String> content = new TreeSet<>();
content.add("Jose");
content.add("Andre");
content.add("Claudia");
content.add("Bruna");
O TreeSet mantém os elementos classificados via Comparator ou Comparable, nesse caso através do método compareTo de Comparable. O TreeSet, como em versões anteriores do Java, mantém os elementos em TreeMap, que por sua vez continua utilizando o Red-Black Tree como algoritmo de ordenação. Por manter os elementos em uma ãrvore balanceada, esse algoritmo é extremamente eficiente, suas as operações são logaritmicas O(log n).
O trecho a seguir, um exemplo de ordenação em uma lista de String:
List<String> content = new ArrayList<>();
Collections.sort(content);
Em listas o Java usa o mesmo mecanismo de ordenação aplicado em arrays. O método sort de Collections usa o toArray da lista p/ recuperar os elementos contidos em um array, realiza a ordenação do array (Arrays.sort) e por fim atualiza os elementos na lista de acordo com resultado da ordenação.
O compareTo não funciona como o esperado?
A classificação natural da String é a ordem alfabética (lexicographically), ela ocorre através do método compareTo de Comparable. Esse método funciona perfeitamente quando não há necessidade de usar caracteres especiais, ou diferenciar letras minúsculas e maiúsculas. No começo do post, citei algumas dificuldades com o uso de caracteres com acentuação e maiúsculas com char, o mesmo vale para a String!
String content[] = { "Andrei", "Andréa", "Cláudia", "Claudio" };
O resultado desse sort é: "Andrei", "Andréa", "Claudio" e "Cláudia".
Uma solução simples para esse tipo de problema é usar classe Collator, introduzida a partir do Java 5. Collator é uma classe abstrata, que implementa Comparator e define um conjunto de regras para realizar a comparação entre strings a partir de um determinado locale. Os principais idiomas são suportados por Collator, através de sua classe filha RuleBasedCollator. Uma sobrecarga do método sort, de Arrays, recebe um Comparator como argumento viabilizando o uso de Collator.
Collator colPtBr = Collator.getInstance(new Locale("pt_BR"));
Arrays.sort(content, colPtBr);
Note o valor "andréa", usando somente letras minúsculas. Agora o resultado do sort é: "andréa", "Andrei", "Cláudia" e "Claudio".
O Collator em pt_BR determina que caso duas palavras sejam semelhantes, variando apenas a acentuação, a palavra com acento vem logo após a palavra sem acento. Veja o resultado de comparações via Collator e Comparable com algumas Strings:
System.out.println(colPtBr.compare("Andréa", "andrea"));  // 1: Andréa após andrea
System.out.println("Andréa".compareTo("andrea"));  // -32: Andréa antes andrea
System.out.println(colPtBr.compare("maca", "maçã"));  // -1: maca antes de maçã
System.out.println("maca".compareTo("maçã"));  // -132: maca antes de maçã
Mais um detalhe sobre o Collator pt_BR, é em relação a letras minúsculas e maiúsculas. No caso de duas strings semelhantes, variando apenas letras minúsculas e maiúsculas, o Collator assume que a String com letra minúscula fica antes da String com maiúscula. Veja:
System.out.println(colPtBr.compare("pedro", "PEDRO")); // -1: pedro antes de PEDRO
System.out.println("pedro".compareTo("PEDRO")); // 32: pedro após PEDRO
A classe Collator permite o uso de estragégias "mais fortes" de comparação. No método setStrength é possível, por exemplo, pedir ao Collator que as diferenças entre acentuação, maiúsculas e minúsculas sejam ignoradas. Veja um exemplo:
colPtBr.setStrength(Collator.PRIMARY);
System.out.println(colPtBr.compare("pedro", "PEDRO")); // 0
System.out.println(colPtBr.compare("Andréa", "andrea"));  // 0
System.out.println(colPtBr.compare("maca", "maçã"));  // 0
Em casos mais simples, cuja a ordenação deve apenas desconsiderar diferenças entre letras maísculas e minúsculas, é possível usar um Comparator pronto e disponível na classe String: a constante CASE_INSENSITIVE_ORDER.
String content[] = { "A", "x", "c", "B", "d", "a" };
Arrays.sort(content, String.CASE_INSENSITIVE_ORDER);
Resultado da ordenação é: A a B c d x
Collator vs performance
O uso em de Collator em grande volume de objetos pode degradar a performance. Uma alternativa para reverter esse problema é utilizar a classe CollationKey, que armazena a chave binária em representação a uma string. Veja como funcionaria essa estratégia:
CollationKey[] collKeys = new CollationKey[content.length];
for (int i = 0; i < collKeys.length; i++) {
//guarda a chave da string
collKeys[i] = colPtBr.getCollationKey(content[i]);
Arrays.sort(collKeys); //ordeno as chaves do collator
for (int i=0; i < collKeys.length; i++) {
System.out.print(content[i]+" ");
O truque desse último trecho é colocar as chaves geradas pelo Collator em um array, ordenar esse array e por fim exibir o as strings de acordo com a ordem das chaves.
Coloquei boa parte do código desse post no github.
Postado por Eder Magalhaes às 6:23 PM 0 comentários
Marcadores: Algoritmos, Java, JDK7
Como extrair um diretório de um Zip c/ Java
Exemplo de um programa simples, com Java 7, para extrair um diretório (e filhos se existir) contido em um arquivo zip (tar / war / ear / jar ...).
public class ExtractZipDir {
System.out.println("Informe o arquivo zip e a pasta");
String dirName =  args[1];
extract(zipName, dirName);
static void extract(String zipName, String dirName) {
long before = currentTimeMillis();
try (ZipFile zip = new ZipFile(new File(zipName))) {
ZipContent zContent = new ZipContent(zip);
int files = zContent.extract(dirName);
System.out.printf("Extraiu %s arquivos (e, %sms)", files,
currentTimeMillis() - before);
private static class ZipContent {
private Map<String, List<ZipEntry>> content = new HashMap<>();
private static final String ROOT_DIRECTORY = ".";
private ZipContent (ZipFile zipFile) {
zip = zipFile;
organizeEntries(zip.entries());
private void organizeEntries(Enumeration<? extends ZipEntry> entries) {
putRootDir();
putEntry(entry);
private void putEntry(ZipEntry entry) {
String path = extractDirNameFromEntry(entry);
List<ZipEntry> entries = content.get(path);
content.put(path, new LinkedList<ZipEntry>());
private void putRootDir() {
content.put(ROOT_DIRECTORY, new LinkedList<ZipEntry>());
private List<ZipEntry> listContent(String folder) {
return content.get(folder);
private boolean hasDirectory(String folder) {
return content.keySet().contains(folder);
private Queue<String> getAllDirectoriesFromParent(String dir) {
for (String d: content.keySet()) {
if (d.contains(dir) && !d.equals(dir))
q.add(d);
q.add(dir);
private int extract(String dir) {
if (!dir.equals(ROOT_DIRECTORY)) {
if (!dir.endsWith(SEPARATOR)) {
dir = dir.concat(SEPARATOR);
if (!hasDirectory(dir)) {
throw new RuntimeException("Directory not found!");
Queue<String> dirs = getAllDirectoriesFromParent(dir);
String path = dirs.poll();
List<ZipEntry> entries = listContent(path);
File parent = new File(path);
for (ZipEntry entry: entries) {
extract(parent, entry);
private void extract(File path, ZipEntry e) {
String fileName = extractFilenameFromEntry(e);
File destFile = new File(path, fileName);
try (BufferedInputStream is = new BufferedInputStream(zip.getInputStream(e));
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER)) {
throw new RuntimeException("Não foi possível extrair!\n" +
private static String extractDirNameFromEntry(ZipEntry entry) {
if (name.lastIndexOf(SEPARATOR) != -1) {
return name.substring(0, name.lastIndexOf(SEPARATOR)+1);
return ROOT_DIRECTORY;
private static String extractFilenameFromEntry(ZipEntry entry) {
if (name.lastIndexOf(SEPARATOR) == -1) {
return name.substring(name.lastIndexOf(SEPARATOR)+1);
Alguns formas de como executar esse programa (Linux / Windows):
$ java ExtractZipDir /home/user/meu.zip dir
$ java C:\usuario\Documents\meu.zip dir
Compartilhei esse código no github.
Postado por Eder Magalhaes às 8:21 AM 0 comentários
Marcadores: Java, JDK7
Postado por Eder Magalhaes às 7:31 AM 0 comentários
Como executar testes no JMeter (remoto) a partir do ANT
Dica útil para projetos aonde os builds são realizados pelo ANT. O projeto jmeter-ant-task define uma task customizada do ANT, para executar testes (stress / carga / ...) com o JMeter.
Na task jmeter existem diversos atributos utilizados para indicar qual é o diretório de instalação do JMeter, qual é o diretório com os planos de testes (arquivos jmx), qual diretório deve conter resultados dos testes devem ser armazenados e outros.
O trecho de build.xml a seguir demonstra como utilizar a task para executar o plano de testes MyTest.jmx.
<project name="MyProject" basedir=".">
<!-- define uma var que guarda a url de app qualquer -->
<property name="app.url" value="htpp://localhost:8080/myapp" />
<!-- vars utilizadas pelo jmeter -->
<property name="jmeter-home" value="/opt/jmeter" />
<property name="jmeter-lib-dir" value="${jmeter-home}/lib" />
<property name="jmeter-results" value="/home/app/jmeter-results" />
<property name="jmeter-tests" value="/home/app/jmeter-tests" />
<!-- Carrega a task do JMeter -->
<taskdef name="jmeter" classpathref="apache.ant.ext.lib.classpath"
classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" />
<target name="jmeter.run" depends="" >
<echo>Acionando o JMeter local p/ os testes de stress</echo>
<jmeter jmeterhome="${jmeter-home}" testplan="${jmeter-tests}/MyTest.jmx"
resultlogdir="${jmeter-results}/jtl" jmeterlogfile="${jmeter-results}/log">
<property name="app.url" value="${app.url}"/>
<!-- passa argumentos p/ a vm do jmeter -->
<jvmarg value="-Xincgc"/>
No inicio do build são definidas variáveis utilizadas pelo o script, boa parte delas na task jmeter. Um detalhe sobre o plano de teste é que ele depende de uma variável que indica a url da aplicação, app.url. Note o uso da tag property, nela eu repasso a variável definida no build ANT, a ${app.url} com o endereço da aplicação. Dessa é possível enviar informações para o JMeter, sem manipular o arquivo jmeter.properties. Na tag jvmarg você pode indicar parâmetros para a VM do JMeter.
O target a seguir simila a execução do Jmeter remoto, em outro servidor. Para executar o JMeter remoto, é necessário pelo menos dois nós (instâncias) do JMeter, uma cliente e outra servidor, veja mais na documentação do projeto. O próximo target aciona o JMeter cliente, na mesma máquina que o build está sendo executado, indicando a propriedade remote_hosts. Dessa forma a instância cliente irá acionar o JMeter servidor nesse endereço indicado.
<!-- host/ip para o jmeter remoto -->
<property name="jmeter-host" value="192.168.1.18"/>
<property name="jmeter-port" value="1099"/>
<target name="jmeter.run.remote">
<echo>Acionando o JMeter remoto p/ os testes de stress</echo>
<jmeter jmeterhome="${jmeter-home}" jmeterlogfile="${jmeter-results}/log" resultlogdir="${jmeter-results}/jtl" runremote="true">
<!-- outra forma de definir os planos de testes -->
<testplans dir="test/jmeter/jmx" includes="MyTest.jmx"/>
<!-- indica que essa property deve ser enviada ao jmeter server -->
<property name="app.url" remote="true" value="${app.url}"/>
<!-- indica a url do jmeter server -->
<property name="remote_hosts" value="${jmeter-host}:${jmeter-port}"/>
Note que a propriedade app.url foi marcada como remote="true", dessa forma o JMeter cliente repassa o valor para o JMeter servidor. Mais informações sobre essa task foram disponibilizadas na página do projeto.
Postado por Eder Magalhaes às 4:09 AM 2 comentários
Marcadores: ANT, JMeter
Overview das ferramentas do Spring para persistência de dados
Durante o desenvolvimento ou manutenção (principalmente) de um projeto de software, seja qual for o estilo, é sempre delicado lidar com os componentes de infra-estrutura responsáveis pela camada de persistência.
Vou descrever alternativas que o Spring Framework oferece para simplificar esse trabalho, em cenários variados, como o foco em base de dados relacional. Ou seja, um overview das ferramentas do Spring para trabalhar com os componentes de persistência.
Código legado, bem legado...
Projetos Java antigos, desenvolvidos a + de 8 anos, normalmente não utilizam uma solução ORM (Mapeamento Objeto Relacional). Nesses projetos é muito comum o uso de bibliotecas "caseiras", escritas in house, para otimizar o uso do JDBC.
O Spring JDBC é um modulo do Spring interessante para esse tipo de cenário. Com ele é possível reduzir consideravelmente o volume de código JDBC. O principal componente do Spring JDBC é o JdbcTemplate, ele disponibiliza uma série de métodos para operações CRUD, consultas e comandos em lote. Para tirar proveito do uso contextos, injeção de dependências e inversão de controle, faz todo o sentido trabalhar em conjunto com o Spring Bean. Dessa forma seria possível injetar a referência do JdbcTemplate nos componente DAO (pattern Data Access Object).
O código a seguir demonstra um fragmento do DAO que utiliza o JdbcTemplate para inserir/atualizar uma entidade (Mercadoria).
public class MercadoriaDAO {
private final static String INSERT_MERCADORIA = "INSERT INTO mercadoria (nome,descricao,preco,quantidade) VALUES (?,?,?,?)";
private final static String UPDATE_MERCADORIA = "UPDATE mercadoria SET nome = ?, descricao = ?, preco = ?, quantidade = ? WHERE id = ?";
private final static String GET_MERCADORIA_BY_ID = "SELECT * FROM mercadoria WHERE id = ?";
private final static String GET_MERCADORIAS_BY_NOME = "SELECT * FROM mercadoria WHERE nome like ?";
public void save(Mercadoria m) {
if (m.getId() == null) {
jdbcTemplate.update(INSERT_MERCADORIA,
new Object[]{ m.getNome(), m.getDescricao(), m.getPreco() });
jdbcTemplate.update(UPDATE_MERCADORIA,
new Object[]{ m.getNome(), m.getDescricao(), m.getPreco(), m.getId() });
Além do método update também pode ser utilizado para realizar a remoção da entidade. No trecho de código a seguir coloco dois exemplos de consultas, utilizando JdbcTemplate. Note que na consulta utilizamos o componente RowMapper, o MercadoriaRowMapper. O RowMapper é utilizado pelos métodos query de JdbcTemplate, ele lê os dados do ResultSet e faz a transformação em uma instância de Mercadoria.
O método queryForObject é utilizado para retornar uma instância da entidade (ou null) com filtro por id, por exemplo. Enquanto o método query retorna uma lista de objetos que podem ser encontrados de acordo com o SQL.
... //ainda em MercadoriaDAO
private class MercadoriaRowMapper implements RowMapper<Mercadoria> {
public Mercadoria mapRow(ResultSet rs, int row) throws SQLException {
String descricao = rs.getString("descricao");
double preco = rs.getDouble("preco");
return new Mercadoria(id, nome, descricao, qtde, preco);
public Mercadoria findById(Integer id) {
return jdbcTemplate.queryForObject(GET_MERCADORIA_BY_ID,
new Object[] { id }, new MercadoriaRowMapper());
public List<Mercadoria> getMercadoriasByNome(String nome) {
return jdbcTemplate.query(GET_MERCADORIAS_BY_NOME,
new Object[] { nome + "%" }, new MercadoriaRowMapper());
Outra estratégia, que não me agrada, seria fazer no DAO a Mercadoria uma herança para JdbcDaoSupport, e acessar o JdbcTemplate encapsulado nesse componente. Com Spring JDBC o código DAO pode ficar bem mais compacto. Para ilustrar isso, compare o DAO c/ JDBC puro e o DAO utilizando Spring JDBC. Veja também as configurações do Spring e o o pom.xml com as dependências para esses módulos.
Projetos com Hibernate
O Spring também oferece soluções para reduzir o esforço e agregar funcionalidades durante o desenvolvimento de projetos Java utilizando soluções ORM, como Hibernate ou JPA. O Spring ORM é outro módulo da suíte Spring, ele oferece funcionalidades para facilitar o uso de soluções baseadas em Mapeamento Objeto Relacional.
Em versões antigas do Hibernate, era trabalhoso manter a Session vinculada ao contexto de execução, por exemplo utilizar a mesma instância em diferentes DAOs dentro do mesmo fluxo de request. Por isso o Spring criou o HibernateTemplate, com a proposta de disponibilizar a Session corrente ao contexto de execução.
Mas a partir do Hibernate 3.0.1 com contextual sessions, isso não é mais necessário. Nas versões mais recentes do Spring é possível usar a Session no contexto de execução, além de centralizar as configurações e realizar a injeção da SessionFactory nos DAOs.
A seguir o código do DAO da Mercadoria com Hibernate:
private final Session getCurrentSession(){
this.getCurrentSession().persist(m);
this.getCurrentSession().merge(m);
return (Mercadoria) this.getCurrentSession().get(Mercadoria.class, id);
.createQuery("from model.Mercadoria m where m.nome like ?")
.setParameter(0, nome+"%")
O spring-config.xml a seguir centraliza no Spring as configurações com banco de dados e do Hibernate. Nesse exemplo o banco de dados utilizado é o HSQLDB (local).
<!-- SessionFactory, DataSource, ... -->
<property name="packagesToScan" value="model" />
<property name="url" value="jdbc:hsqldb:file:mercadoria"/>
Projetos com JPA++
O Spring também oferece funcionalidades bem interessantes para projetos que utilizam a Java Persistence API (JPA), através do módulo Spring Data JPA. Através da interface JpaRepository o Spring define os métodos de consulta e CRUD. O desenvolvedor trabalha de forma alto-nível, criando uma interface de persistência, enquanto a implementação fica por conta do próprio Spring. Outro elemento do Spring Data JPA é a anotação @Query, responsável por informar consultas customizadas via JPAQL.
Para esse tipo de componente o Spring adota o pattern Repository, que abstrai uma coleção de objetos com o design mais próximo ao domínio da aplicação do que com o banco de dados. Veja como ficaria a interface MercadoriaRepository:
public interface MercadoriaRepository extends JpaRepository<Mercadoria, Integer> {
@Query("select m from Mercadoria m where m.nome like ?1")
List<Mercadoria> getMercadoriasByNome(String nome);
Uma vez que a interface foi definida, o Spring cria um proxy que implementa os métodos de persistência. Esse proxy será injetado em classe de negócio/controller pelo Spring. Veja o exemplo:
public class MercadoriaService {
private MercadoriaRepository repo;
m.save(m);
Nesse exemplo demonstrei funcionalidades básicas da interface JpaRepository, mas existem outrs funcionalidades como o suporte a paginação da consulta SQL, veja esse exemplo. No github compartilhamos outros dois projetos que utilizam Spring Data JPA, uma aplicação web e outra desktop. Ambas fazem uso do JpaRepository.
Um pouco além: NoSQL
Na verdade esse módulo compõe o Spring Data, uma solução "guarda-chuva" com o objetivo de unificar e simplicar o armazenamento de dados em bancos relacionais e NoSQL. Abaixo desse projeto existe o módulo Spring Data MongoDB, responsável por abstrair o acesso ao MongoDB. Não é o proposito desse post abordar soluções NoSQL, mas para ter uma idéia o trecho de código a seguir demonstra repositório da Mercadoria (como Document) em versão MongoDB:
public interface MercadoriaRepository extends MongoRepository<User, String> {
@Query("{ nome: ?0 }")
List<User> getMercadoriasByNome(String nome);
Abaixo do Spring Data ainda existem sub-projetos para Neo4j, Apache Hadoop, REST e outros. É possível saber um pouco mais sobre essa tecnologia, em artigo introdutório sobre Spring Data no InfoQ Brasil.
Esse é apenas um resumo de algumas soluções oferecidas pelo Spring Framework para resolver questões relacionadas a persistência em projetos Java. Sou da turma que gosta do Spring e de Java EE. Acredito que tirando proveito das melhoras funcionalidades das duas stacks, aumentamos o nosso poder fogo e logo a possibilidade de desenvolver um projeto com sucesso.
Postado por Eder Magalhaes às 1:27 PM 1 comentários
Marcadores: Hibernate, Java, jpa, MongoDB, NoSQL, Spring, spring-data
Postado por Eder Magalhaes às 10:59 AM 2 comentários
Neste post descrevo os passos para copiar o arquivo de log do Tomcat 7, rodando no Openshift (plataforma cloud da Red Hat). O objetivo de copiar o arquivo de log do Openshift para minha máquina, é simplesmente facilitar a consulta e análise dos registros de log.
Primeiro é necessário acessar a aplicação no Openshift via ssh (a forma mais simples é copiar a hash ssh na página da sua aplicação no site do próprio Openshift).
O Openshift mantém uma série de variáveis de ambiente pré-definidas, com o propósito de armazenar o caminho para algumas pastas do sistema de arquivos da plataforma. Todas essas variáveis possuem o prefixo OPENSHIFT_. Com o comando a seguir é possível listar e visualizar o conteúdo de todas essas variáveis:
<openshift>$ env | grep OPENSHIFT_
Para acessar os logs do Tomcat (JBoss EWS), utilizo a variável de ambiente OPENSHIFT_JBOSSEWS_LOG_DIR. A próxima etapa é fazer a cópia do arquivo de log para o diretório de dados (app-root/data) no OpenShift. A variável de ambiente que aponta para esse diretório é OPENSHIFT_DATA_DIR. A seguir a instrução para realizar a cópia do arquivo catalina.out:
<openshit>$ cp $OPENSHIFT_JBOSSEWS_LOG_DIR/catalina.out $OPENSHIFT_DATA_DIR
Lembre-se que por questões de segurança o Openshift só permite a transferência via ssh de arquivos contidos na pasta data.
Agora é só baixar (copiar) o arquivo catalina.out do Openshift para a máquina local:
yaw@local:~/$ scp {hash_}@{app}.rhcloud.com/~/app-root/data/catalina.out /~
Pronto, com o arquivo local fica mais fácil analisar o log da aplicação.
Postado por Eder Magalhaes às 11:57 AM 3 comentários
Marcadores: Openshift, Tomcat
Dica útil para resolver problemas com npm, o gerenciador de pacotes do Nodejs. O erro "npm error Error: tunneling socket could not be established", pode ocorrer devido ao uso de proxy com autenticação.
No Ubuntu, o npm respeita a configuração de proxy (http e https) das variáveis de ambiente http_proxy e https_proxy, respectivamente. Para visualizar as configurações de proxy do npm, utilize o seguinte comando:
$ npm config get proxy
O comando deve exibir o host (ip) do proxy configurado. Pra informar as credenciais do proxy ao npm, é necessário modificar a configuração do gerenciador, isso pode ser feito com o comando (utilize suas informações de crendencial):
$ npm config set proxy http://usuario:senha@host_proxy:porta_proxy
Para https, utilize a property https-proxy nas configurações do npm.
Agora o npm install vai realizar a autenticação no proxy.
Postado por Eder Magalhaes às 11:04 AM 1 comentários
Marcadores: Linux, nodejs
RESTEasy: Erro ao retornar List em XML - Could not find MessageBodyWriter for response object of type: xxx application/xml
Outro dia me deparei com uma exception gerada pelo RESTEasy, o erro era: Could not find MessageBodyWriter for response object of type: java.util.ArrayList of media type: application/xml.
No meu cenário, o problema ocorria ao utilizar o componente Response do RESTEasy como retorno do método. Pra ficar mais claro a seguir um exemplo de código simulando o erro, no caso a entidade e o endpoint do RESTEasy:
private String renavam;
@Path("/carros")
public class CarroEndpoint {
Carro c = service.findById(id);
return Response.ok(c).build();
} catch (NoResultException nrex) {
return Response.status(Status.NOT_FOUND).build(); //404
public List<Carro> listAll() {
return service.listAll();
@Path("/{modelo}")
public Response listByModelo(@PathParam("modelo") String descModelo) {
Modelo m = service.findModeloByDescricao(descModelo);
List<Carro> carros = service.findByModelo(m);
return Response.ok(carros).build(); //o problema ocorre aqui
Os métodos findById e listAll funcionam corretamente, ambos devolvem o XML representando os dados dos carros encontrados de acordo com a consulta. Note o retorno desses dois métodos: a entidade Carro e uma lista de Carros.
A exception ocorre no terceiro método, no listByModelo, aonde faço uso da Response (componente do RESTEasy). Nesse caso o RESTEasy não consegue transformar o ArrayList em XML. A mesma situação acontece com outra coleção, por exemplo HashSet.
Pra resolver o problema, eu utilizei o GenericEntity para "tipar" a lista, de forma que o RESTEasy possa realizar a transformação em XML. A mesma abordagem funciona com HashSet. A seguir a nova versão de listByModelo:
GenericEntity<List<Carro>> entity = new GenericEntity<List<Carro>>(carros);
return Response.ok(entity).build(); //ok
A versão do RESTEasy utilizada foi a 2.3.6.Final.
Postado por Eder Magalhaes às 12:43 PM 1 comentários
Marcadores: Java, RESTEasy
Configurar conexões remotas para o MongoDB no Ubuntu
Uma vez que o mongoDB já foi instalado no Ubuntu, é muito simples habilitar conexões remotas para o serviço. Basta indicar o uso do ip e porta no arquivo de configurações do mongoDB, mongodb.conf. (Acesse c/ super-usuário - sudo)
$ sudo gedit /etc/mongodb.conf
Inclua esses dois parametros:
A propriedade port já vem comentada na instalação padrão.
Importante: nesse post não levo em consideração aspectos de segurança e restrição de acesso ao mongoDB.
Você pode acessar o mongoDB de outra máquina, indicando o ip dá maquina aonde o banco foi instalado. Esse acesso pode ocorrer via terminal ou com um ferramenta GUI para o MongoDB, como o UMongo (app p/ Linux).
Postado por Eder Magalhaes às 11:00 AM 2 comentários
Openshift, como fazer o deploy de um war pronto no Tomcat
No Openshift é possível trabalhar com diversas tecnologias, como: Java, PHP, Ruby, Node.js e até Perl. Além das linguagens, a plataforma cloud computing da Red Hat (PaaS) também oferece suporte a uma série de serviços complementares, como gestor de build (Maven p/ Java), ferramenta para integração contínua (Jenkins), controlador e repositório de fontes (Git).
O Openshift agrega esses serviços/ferramentas a plataforma com objetivo de ampliar a capacidade de desenvolvimento. Dessa forma além de usar o hosting em nuvem, você pode contar com uma melhor gestão de build, baseado na execução de testes, com a possibilidade de commits em repositórios segmentados.
Mas é possível ignorar esses serviços, e fazer o deploy da aplicação "pronta" no Openshift. Pronto no sentindo de que o artefato foi gerado local, sem interferência do Openshift. Isso pode ser interessante em situações pontuais, como demos ou provas de conceito.
Nesse post demonstro como realizar o deploy de uma aplicação Java web (um war), no Tomcat, sem utilizar o Maven, Jenkins e Git*.
*Na verdade não colocamos os fontes no Git, mas o utilizamos para armazenar o war da aplicação.
1 - Crie a aplicação no Openshift, e faça o clone local do repositório Git. Note que dentro do diretório principal, no repositório local, existe a subpasta webapps.
2 - Acesse o código fonte da aplicação para gerar o artefato web, o war. Se preferir utilize o Maven, rodando local, para controlar o build gerar o war.