Hoje vamos falar de algo mais descontraído, vamos refletir um pouco sobre os
novos rumos da linguagem Java sem pretensão de ser dono da razão ou de ser imparcial,
mas expor honestamente a percepção das ocorrências mais concretas definidoras
dos rumos atuais desta linguagem "amada demais" e
"cornetada" pelos efeitos colaterais de sua grandiosidade.
A finalidade é perceber o cenário atual, acertos, erros e colaborações
necessárias para a continuidade desta história.
Vamos nessa.
Como a maioria dos programadores da linguagem Java já sabe, ela foi feita
para atender a máxima "faça uma vez, execute em qualquer lugar".
Desde sua concepção, implantação, popularização e amadurecimento, o Java fez
jus a sua máxima no que diz respeito a servidores, estações de trabalhos e
computadores aplicados dotados de Linux. O Java se desenvolveu nos campos do
JSE (Java Standard Edition) e JEE (Java Enterprise Edition).
Também deixou muito a desejar na sua proposta JME (Java Micro Edition)
segregada conforme as limitações do hardware em CLDC e MIDP/CDC, o que
exigia técnicas e precauções diferentes para complicar ainda mais o trabalho
com JME até sua segunda versão. Hoje, o JME propõe maior integração e
simplificação em sua terceira geração, mas convenhamos, o Android já chegou e
será preciso um esforço extra para o JME 3.0 emplacar.
Prefiro focar no quadro geral e afirmar: apesar da Sun não ter tido sucesso
em simplificar o JME e até mesmo não ter perfil para tê-lo difundido nos atuais
smartphones, precisamos reconhecer o grande sucesso do Java nas
plataformas empresariais o que não é pouco. Muitos criticam por achar que
plataforma empresarial se limita a empresas, mas sem a "ortodoxia",
plataforma empresarial é basicamente a ciência tecnológica a serviço das
demandas de uma sociedade econômica de extrema exigência. Significa que o Java
esteve até agora presente em muitos ciclos econômicos auxiliando uma Era da
Informação por sua capacidade de integração transparente sobre modernos
sistemas distribuídos.
O Java se consolidou e talvez parte de seus defensores e militantes estejam
pensando no fim de um episódio de mais uma linguagem de programação. Todavia, o
Java da Sun Microsystems não será mais o mesmo desde ter ficado órfão depois da
saída de seu pai, James Gosling, da Sun. Isso está longe de ser uma intenção de
provocar nostalgia nos defensores do Java, porque o Java se expandiu para uma
JVM múlti linguagem em sua versão 7 e ainda expandiu para o Android quando a
Google se inclinou a sua praticidade. Agora o Java não é mais encabrestado pela
Sun e uma concorrência saudável já foi iniciada.
Esta alteração nas fronteiras Java não é obra do acaso, talvez seja obra de
competição entre empresas, mas está marcada por um novo estágio da Era da Informação
compreendido pelo domínio público do conhecimento promovido pelas redes sociais
digitais e ainda pelo paradigma da Computação nas Nuvens.
As empresas Google e Oracle deixam seus interesses e posições bastante claros
neste novo cenário e vão produzir um Java cada vez melhor.
Fontes:
http://www.infoq.com/br/articles/invokedynamic
https://blogs.oracle.com/jrose/resource/pres/201107-JDK7.pdf
http://www.slideshare.net/amsterdatech/linguagens-dinamicas-na-jvm
http://www.amsterdaintelligence.blogspot.com.br/2010/03/mundo-das-linguagens-o-futuro-da.html
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html
http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html
Translate
quinta-feira, 8 de novembro de 2012
domingo, 4 de novembro de 2012
Java - Evitar NullPointerException
O NullPointerException é um erro comum num programa escrito em Java
quando se opera uma variável do tipo objeto sem antes verificar se está
nula.
Algumas estratégias sugerem usar um padrão de produção do objeto que ao invéz de retornar um valor nulo é retornado um objeto que simboliza não ter encontrado o objeto esperado. Exemplo: new ContatoNaoEncontrado("Not found").
Essa sugestão é ruim porque requer a criação de uma classe adicional para simbolizar o caso de não encontrado e ainda torna frustrante a codificação porque o fluxo esperado do programador pode ser enganado caso ele se descuide e deixe de verificar se o objeto retornado representa "not found" (não encontrado).
Acredito que não exista uma fórmula mágica para evitar todas as verificações de nulidade, mas nem por isso devemos deixar de acreditar numa solução que simplifica bastante esta tarefa em casos corriqueiros onde se trabalha com valores mais simples como é o caso das operações de números ou textos em termos de atributo ou variável simples para apoiar a execução de um método.
A função sugerida aqui é para simplificar operações de atributos e até mesmo realizar ETL de forma ágil e limpa, pois isso não é errado e reduz o volume de códigos na situação explicada. Aplicar um padrão de projeto nesses casos não convém e seria impraticável algum padrão de projeto.
Como a maior parte dos programas trata valores numericos e alfanuméricos em grande volume, elaborei esta técnica na tentativa de contribuir com a boa prática de programação. Ela consiste na criação de um método chamado ANPE (Avoid NullPoiterException ou Evitar NullPointerException) que recebe dois valores. Se o primeiro valor for nulo, então retorna o segundo valor.
Vamos à prática.
AvoidNullPointerException
A classe AvoidNullPointerException conterá o método ANPE do qual falei. Ele é estático e utiliza Java Generic para tornar a função flexível ao tratar todos os objetos. Coloque a classe dentro de uma package br.com.wt.util para facilitar o exercício.
package br.com.wt.util;
public class AvoidNullPointerException {
public static <V> V ANPE(V object, V object2) {
if (object == null) {
return object2;
}
return object;
}
}
Testando
O teste é simples, pois se value for nulo, então o tornaremos 0L (zero). A importação da função estática ANPE diminui o trabalho para invocá-la:
import static br.com.wt.util.AvoidNullPointerException.ANPE;
public class Test {
public static void main(String[] args) {
Long value = null;
if (ANPE(value, 0L) == 1L) {
value = 100L;
}
}
}
Este teste pode ser realizado com String, Integer, Short, Double, Float e outros tipos de objetos desde que se esteja testando um valor.
Mais um teste para fixar
Agora com uma String.
import static br.com.wt.util.AvoidNullPointerException.ANPE;
public class Test {
public static void main(String[] args) {
String country = null;
if (ANPE(country, "").equals("Brazil")) {
System.out.println("World Cup 2014");
} else {
System.out.println("But will 2016 Olympics in Brazil");
}
}
}
Boa programação.
Algumas estratégias sugerem usar um padrão de produção do objeto que ao invéz de retornar um valor nulo é retornado um objeto que simboliza não ter encontrado o objeto esperado. Exemplo: new ContatoNaoEncontrado("Not found").
Essa sugestão é ruim porque requer a criação de uma classe adicional para simbolizar o caso de não encontrado e ainda torna frustrante a codificação porque o fluxo esperado do programador pode ser enganado caso ele se descuide e deixe de verificar se o objeto retornado representa "not found" (não encontrado).
Acredito que não exista uma fórmula mágica para evitar todas as verificações de nulidade, mas nem por isso devemos deixar de acreditar numa solução que simplifica bastante esta tarefa em casos corriqueiros onde se trabalha com valores mais simples como é o caso das operações de números ou textos em termos de atributo ou variável simples para apoiar a execução de um método.
A função sugerida aqui é para simplificar operações de atributos e até mesmo realizar ETL de forma ágil e limpa, pois isso não é errado e reduz o volume de códigos na situação explicada. Aplicar um padrão de projeto nesses casos não convém e seria impraticável algum padrão de projeto.
Como a maior parte dos programas trata valores numericos e alfanuméricos em grande volume, elaborei esta técnica na tentativa de contribuir com a boa prática de programação. Ela consiste na criação de um método chamado ANPE (Avoid NullPoiterException ou Evitar NullPointerException) que recebe dois valores. Se o primeiro valor for nulo, então retorna o segundo valor.
Vamos à prática.
AvoidNullPointerException
A classe AvoidNullPointerException conterá o método ANPE do qual falei. Ele é estático e utiliza Java Generic para tornar a função flexível ao tratar todos os objetos. Coloque a classe dentro de uma package br.com.wt.util para facilitar o exercício.
package br.com.wt.util;
public class AvoidNullPointerException {
public static <V> V ANPE(V object, V object2) {
if (object == null) {
return object2;
}
return object;
}
}
Testando
O teste é simples, pois se value for nulo, então o tornaremos 0L (zero). A importação da função estática ANPE diminui o trabalho para invocá-la:
import static br.com.wt.util.AvoidNullPointerException.ANPE;
public class Test {
public static void main(String[] args) {
Long value = null;
if (ANPE(value, 0L) == 1L) {
value = 100L;
}
}
}
Este teste pode ser realizado com String, Integer, Short, Double, Float e outros tipos de objetos desde que se esteja testando um valor.
Mais um teste para fixar
Agora com uma String.
import static br.com.wt.util.AvoidNullPointerException.ANPE;
public class Test {
public static void main(String[] args) {
String country = null;
if (ANPE(country, "").equals("Brazil")) {
System.out.println("World Cup 2014");
} else {
System.out.println("But will 2016 Olympics in Brazil");
}
}
}
Boa programação.
quinta-feira, 1 de novembro de 2012
Java - Tutorial para manipulação binária
Em 2009, elaborei um tutorial para ajudar alguns colegas de trabalho a
entederem sobre manipulação binária de uma maneira mais rápida a partir
da liguagem Java, pois este assunto é muito complicado dominar sem
ajuda. Como obtive ajuda de diversos colegas para entender este assunto
ao longo dos anos, busquei compilar boa parte do assunto neste tutorial
naquela ocasião.
Neste tutorial são abordados os assuntos como operações binárias, operadores AND (e), OR (ou), NAND (não é), NOR (não ou), XOR (disjunção de valores quando dois valores comparados são diferentes), operadores bit-a-bit (bitwise) e ordem binária (Big-Endian e Little-Endian).
Publiquei o tutorial para manipulação binária em Java em: http://goo.gl/X6hka
Qualquer dúvida adicional sobre este tutorial, envie um comentário.
Neste tutorial são abordados os assuntos como operações binárias, operadores AND (e), OR (ou), NAND (não é), NOR (não ou), XOR (disjunção de valores quando dois valores comparados são diferentes), operadores bit-a-bit (bitwise) e ordem binária (Big-Endian e Little-Endian).
Publiquei o tutorial para manipulação binária em Java em: http://goo.gl/X6hka
Qualquer dúvida adicional sobre este tutorial, envie um comentário.
quarta-feira, 31 de outubro de 2012
Java 7 - Notação binária
O Java 7 incorpora uma notação bastante interessante e importante para expressar bits. Esta notação é chamada de Binary Literals e tem como prefixo 0b. Desde antes, o Java já suportava a notação hexadecimal (0x) e octal (0).
Como a maioria já sabe, todos os dados computacionais são bits em sua forma bruta. O tipo de uma variável é uma formatação dos bits para torná-los inteligíveis à nós.
Gostei desta notação porque me pouparia de acessar a calculadora científica (agora do programador) para converter uma máscara de bits em decimal ou hexadecimal. Espero ter a oportunidade de usufruir deste recurso no futuro.
Vamos explorar agora algumas possibilidades de uso do Binary Literals ou Notação Binária em Java 7:
Obs.: Aproveitei para usar "_" (underscore ou underline) para melhorar a leitura dos bits grandes, mas não interfere no resultado.
char A = 0b0000000001000001;
char B = 0b0000000001000010;
char C = 0b0000000001000011;
char D = 0b0000000001000100;
char E = 0b0000000001000101;
short numberShort_16bits = 0b0000000000000011;
int numberInt_32bits = 0b01000000_10000000_10000000_10000000;
long numberLong_64bits = 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L;
float numberFloat_32bits = 0b001000000_10000000_10000000_10000000;
double numberDouble_64bits = 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L;
char arrayOfChar [] = {0b0000000001000001, 0b0000000001000010, 0b000000000100001};
short arrayOfnumberShort_16bits [] = {0b0000000000000011, 0b0000000100000111};
int arrayOfnumberInt_32bits [] = {0b01000000_10000000_10000000_10000000, 0b01000000_10000000_10000000_10000000};
long arrayOfnumberLong_64bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L,
0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};
float arrayOfnumberFloat_32bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L,
0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};
double arrayOfnumberDouble_64bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L, 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};
Até a próxima.
Como a maioria já sabe, todos os dados computacionais são bits em sua forma bruta. O tipo de uma variável é uma formatação dos bits para torná-los inteligíveis à nós.
Gostei desta notação porque me pouparia de acessar a calculadora científica (agora do programador) para converter uma máscara de bits em decimal ou hexadecimal. Espero ter a oportunidade de usufruir deste recurso no futuro.
Vamos explorar agora algumas possibilidades de uso do Binary Literals ou Notação Binária em Java 7:
Obs.: Aproveitei para usar "_" (underscore ou underline) para melhorar a leitura dos bits grandes, mas não interfere no resultado.
char A = 0b0000000001000001;
char B = 0b0000000001000010;
char C = 0b0000000001000011;
char D = 0b0000000001000100;
char E = 0b0000000001000101;
short numberShort_16bits = 0b0000000000000011;
int numberInt_32bits = 0b01000000_10000000_10000000_10000000;
long numberLong_64bits = 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L;
float numberFloat_32bits = 0b001000000_10000000_10000000_10000000;
double numberDouble_64bits = 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L;
char arrayOfChar [] = {0b0000000001000001, 0b0000000001000010, 0b000000000100001};
short arrayOfnumberShort_16bits [] = {0b0000000000000011, 0b0000000100000111};
int arrayOfnumberInt_32bits [] = {0b01000000_10000000_10000000_10000000, 0b01000000_10000000_10000000_10000000};
long arrayOfnumberLong_64bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L,
0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};
float arrayOfnumberFloat_32bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L,
0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};
double arrayOfnumberDouble_64bits [] = {0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L, 0b001000000_10000000_10000000_10000000_01000000_10000000_10000000_10000000L};
Até a próxima.
quinta-feira, 27 de setembro de 2012
Java - Bufferizar dados
Após algumas vezes programar rotinas para capturar bytes vindos de conexão socket ou porta serial, armazenar estes bytes e em seguida processá-los para extrair a informação original, percebi logo a complexidade disso.
Qualquer um que já tenha programado um client ou server socket em Java, C++ e C ANSI sabe o que digo, pois utilizar um byte array para armazenar (bufferizar) dados recebidos antes de enquadrar uma informação significativa é uma maneira arcaica e dispensiosa.
Certo dia, resolvi simplificar esta situação com a criação de um mecanismo chamado WTByteBuffer bastante simples para prover apenas dois métodos, um método de escrita e outro de leitura a partir do ByteBuffer do Java NIO.
O método de escrita armazena os bytes lidos e o método de leitura consome os bytes. Desta forma, o programador Java será poupado de todo o trabalho discutido anteriormente para bufferizar e consumir dados recebidos.
WTByteBuffer - Classe para bufferizar
import java.nio.ByteBuffer;
public class WTByteBuffer {
public static final int DEFAULT_MAX_SIZE = 1024 * 65;
private volatile ByteBuffer buffer;
private int size;
private int bufferMaxSize;
public int getSize() {
return this.size;
}
public ByteBuffer getBuffer() {
return this.buffer;
}
public WTByteBuffer() {
this(WTByteBuffer.DEFAULT_MAX_SIZE);
}
public WTByteBuffer(int bufferMaxSize) {
this.bufferMaxSize = bufferMaxSize;
this.buffer = ByteBuffer.allocate(bufferMaxSize);
buffer.position(0);
this.size = 0;
}
public synchronized void write(byte[] bytes) {
if (this.buffer == null)
System.out.println("Buffer nulo : W");
this.buffer.put(bytes);
this.size = this.buffer.position();
}
public synchronized int read(byte[] bytes) {
int target, position, newPosition, capacity;
byte[] completeBuffer = null;
byte[] remainingBuffer = null;
if (bytes == null || bytes.length == 0) {
return 0;
}
if (this.buffer == null)
System.out.println("Buffer nulo : R");
target = bytes.length;
position = this.buffer.position();
capacity = this.buffer.capacity();
if (target > position) {
target = position;
}
newPosition = position - target;
completeBuffer = new byte[position];
remainingBuffer = new byte[newPosition];
this.buffer.flip();
this.buffer.get(completeBuffer, 0, position);
System.arraycopy(completeBuffer, 0, bytes, 0, target);
this.buffer = null;
this.buffer = ByteBuffer.allocate(capacity);
if (newPosition > 0) {
System.arraycopy(completeBuffer, target, remainingBuffer, 0, newPosition);
this.buffer.put(remainingBuffer);
}
this.size = this.buffer.position();
return target;
}
public synchronized void clear() {
if (this.buffer != null) {
this.buffer.clear();
this.buffer = null;
}
this.buffer = ByteBuffer.allocate(bufferMaxSize);
buffer.position(0);
this.size = 0;
}
}
Testando um buffer
Este exemplo cria uma instância de WTByteBuffer com 100 bytes de capacidade, escreve a série de bytes de um texto qualquer e recupera de um em um byte. Depois repete o teste recuperando de dois em dois bytes do texto bufferizado.
WTByteBuffer buffer = new WTByteBuffer(100);
String text = "ABXYZ-0123456-abc";
byte[] read = new byte[2];
byte[] serialized = text.getBytes();
int size = 0;
System.out.println("-----------------------------------------");
buffer.write(serialized);
System.out.format("Texto guardado em buffer: %s\n", text);
System.out.println("Recupera texto de 1 em 1 byte...");
while (buffer.getSize() > 0) {
read = new byte[1];
size = buffer.read(read);
System.out.format("1 byte lido: %s\n", new String(read));
}
System.out.format("Bytes guardados: %d\n", buffer.getSize());
System.out.println("-----------------------------------------");
buffer.write(serialized);
System.out.format("Texto guardado em buffer: %s\n", text);
System.out.println("Recupera texto de 2 em 2 byte...");
while (buffer.getSize() > 0) {
read = new byte[2];
size = buffer.read(read);
System.out.format("%d byte(s) lido(s): %s\n", size, new String(read));
}
buffer = null;
Numa situação real a capacidade deste buffer deverá ser maior do que 100. Caso desejar aumentar o tamanho, faça isso ao construir o WTByteBuffer ou então utilize o construtor padrão para iniciar com o tamanho padrão de reserva máxima.
A classe abordada ainda provê métodos adicionais para controle e acesso ao buffer central.
Boa sorte.
Qualquer um que já tenha programado um client ou server socket em Java, C++ e C ANSI sabe o que digo, pois utilizar um byte array para armazenar (bufferizar) dados recebidos antes de enquadrar uma informação significativa é uma maneira arcaica e dispensiosa.
Certo dia, resolvi simplificar esta situação com a criação de um mecanismo chamado WTByteBuffer bastante simples para prover apenas dois métodos, um método de escrita e outro de leitura a partir do ByteBuffer do Java NIO.
O método de escrita armazena os bytes lidos e o método de leitura consome os bytes. Desta forma, o programador Java será poupado de todo o trabalho discutido anteriormente para bufferizar e consumir dados recebidos.
WTByteBuffer - Classe para bufferizar
import java.nio.ByteBuffer;
public class WTByteBuffer {
public static final int DEFAULT_MAX_SIZE = 1024 * 65;
private volatile ByteBuffer buffer;
private int size;
private int bufferMaxSize;
public int getSize() {
return this.size;
}
public ByteBuffer getBuffer() {
return this.buffer;
}
public WTByteBuffer() {
this(WTByteBuffer.DEFAULT_MAX_SIZE);
}
public WTByteBuffer(int bufferMaxSize) {
this.bufferMaxSize = bufferMaxSize;
this.buffer = ByteBuffer.allocate(bufferMaxSize);
buffer.position(0);
this.size = 0;
}
public synchronized void write(byte[] bytes) {
if (this.buffer == null)
System.out.println("Buffer nulo : W");
this.buffer.put(bytes);
this.size = this.buffer.position();
}
public synchronized int read(byte[] bytes) {
int target, position, newPosition, capacity;
byte[] completeBuffer = null;
byte[] remainingBuffer = null;
if (bytes == null || bytes.length == 0) {
return 0;
}
if (this.buffer == null)
System.out.println("Buffer nulo : R");
target = bytes.length;
position = this.buffer.position();
capacity = this.buffer.capacity();
if (target > position) {
target = position;
}
newPosition = position - target;
completeBuffer = new byte[position];
remainingBuffer = new byte[newPosition];
this.buffer.flip();
this.buffer.get(completeBuffer, 0, position);
System.arraycopy(completeBuffer, 0, bytes, 0, target);
this.buffer = null;
this.buffer = ByteBuffer.allocate(capacity);
if (newPosition > 0) {
System.arraycopy(completeBuffer, target, remainingBuffer, 0, newPosition);
this.buffer.put(remainingBuffer);
}
this.size = this.buffer.position();
return target;
}
public synchronized void clear() {
if (this.buffer != null) {
this.buffer.clear();
this.buffer = null;
}
this.buffer = ByteBuffer.allocate(bufferMaxSize);
buffer.position(0);
this.size = 0;
}
}
Testando um buffer
Este exemplo cria uma instância de WTByteBuffer com 100 bytes de capacidade, escreve a série de bytes de um texto qualquer e recupera de um em um byte. Depois repete o teste recuperando de dois em dois bytes do texto bufferizado.
WTByteBuffer buffer = new WTByteBuffer(100);
String text = "ABXYZ-0123456-abc";
byte[] read = new byte[2];
byte[] serialized = text.getBytes();
int size = 0;
System.out.println("-----------------------------------------");
buffer.write(serialized);
System.out.format("Texto guardado em buffer: %s\n", text);
System.out.println("Recupera texto de 1 em 1 byte...");
while (buffer.getSize() > 0) {
read = new byte[1];
size = buffer.read(read);
System.out.format("1 byte lido: %s\n", new String(read));
}
System.out.format("Bytes guardados: %d\n", buffer.getSize());
System.out.println("-----------------------------------------");
buffer.write(serialized);
System.out.format("Texto guardado em buffer: %s\n", text);
System.out.println("Recupera texto de 2 em 2 byte...");
while (buffer.getSize() > 0) {
read = new byte[2];
size = buffer.read(read);
System.out.format("%d byte(s) lido(s): %s\n", size, new String(read));
}
buffer = null;
Numa situação real a capacidade deste buffer deverá ser maior do que 100. Caso desejar aumentar o tamanho, faça isso ao construir o WTByteBuffer ou então utilize o construtor padrão para iniciar com o tamanho padrão de reserva máxima.
A classe abordada ainda provê métodos adicionais para controle e acesso ao buffer central.
Boa sorte.
quarta-feira, 29 de agosto de 2012
Alerta de ameaça - Plugin Java para navegadores
Foi detectada recentemente uma falha no plugin do Java para navegadores e está sendo explorada por um malware difundido pela rede.
O malware é capaz de conceder acesso completo ao sistema de seu computador que poderá ser controlado por servidores já preparados para isso.
A recomendação é desativar o plugin do Java de seus navegadores enquanto a falha na segurança é corrigida.
Caso você não saiba como desabilitar o plugin em seu navegador, consulte um especialista para te ajudar.
Fontes:
http://g1.globo.com/tecnologia/noticia/2012/08/java-tem-falha-critica-e-codigo-de-exploracao-ja-circula-na-web.html
http://tecnoblog.net/111640/java-falha-zero-day/
O malware é capaz de conceder acesso completo ao sistema de seu computador que poderá ser controlado por servidores já preparados para isso.
A recomendação é desativar o plugin do Java de seus navegadores enquanto a falha na segurança é corrigida.
Caso você não saiba como desabilitar o plugin em seu navegador, consulte um especialista para te ajudar.
Fontes:
http://g1.globo.com/
http://tecnoblog.net/111640/

sexta-feira, 3 de agosto de 2012
Eclipse - Failed to create Java Virtual Machine
Recentemente meu Eclipse Indigo se atualizou como de costume e após reiniciá-lo recebi a mensagem "Failed to create the Java Virtual Machine".
A razão do problema é que o Eclipse teve seu arquivo de inicialização modificado de maneira errada por alguma atualização.
Foi necessário editar o arquivo Eclipse.INI e reorganizar as informações e o Eclipse voltou a funcionar:
Atenção: Faça um backup do seu Eclipse.INI antes de alterá-lo, porque os pacotes JAR do seu INI original devem ser preservados.
-startup
plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.100.v20110502
-product
org.eclipse.epp.package.rcp.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
-vm
C:\Program Files\Java\jdk1.6.0_27\bin\javaw.exe
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms40m
-Xmx1024m
Fonte: http://wiki.eclipse.org/Eclipse.ini
A razão do problema é que o Eclipse teve seu arquivo de inicialização modificado de maneira errada por alguma atualização.
Foi necessário editar o arquivo Eclipse.INI e reorganizar as informações e o Eclipse voltou a funcionar:
Atenção: Faça um backup do seu Eclipse.INI antes de alterá-lo, porque os pacotes JAR do seu INI original devem ser preservados.
-startup
plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.100.v20110502
-product
org.eclipse.epp.package.rcp.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
-vm
C:\Program Files\Java\jdk1.6.0_27\bin\javaw.exe
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms40m
-Xmx1024m
Fonte: http://wiki.eclipse.org/Eclipse.ini
quarta-feira, 1 de agosto de 2012
Java - Leitura de XML com a SAX
Ao ler um arquivo XML você pode utilizar duas maneiras, SAX e a DOM.
A SAX é prove uma forma direta de acessar elementos de um XML por seu identificador e não requer leitura de uma hierarquia para isso. Diferente da SAX, a DOM é uma maneira de ler o XML também, mas por meio de uma hierarquia.
No entanto, este artigo apresenta um exemplo de leitura de XML via SAX na linguagem Java.
Documento XML
Crie o arquivo cesta.xml. No exemplo, este arquivo foi criado no diretório c:\temp. Preencha com o conteúdo a seguir:
<cesta loja="Papelex">
<caneta>Caneta BIC</caneta>
<caneta>Caneta Kilometrica</caneta>
<caneta>Caneta Faber</caneta>
<caneta>Caneta Helios</caneta>
<caneta>Caneta Ferrari</caneta>
<caneta>Caneta Pentec</caneta>
<lapis>Caneta BIC</lapis>
<lapis>Caneta Kilometrica</lapis>
<lapis>Caneta Faber</lapis>
<lapis>Caneta Helios</lapis>
<lapis>Caneta Ferrari</lapis>
<lapis>Caneta Pentec</lapis>
</cesta>
Classe de leitura do XML
Crie a classe de leitura do XML chamada TestaXML.java.
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
public class TestaXML {
public static void main(String[] args) throws JDOMException, IOException {
File f = new File("c:/temp/cesta.xml");
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(f);
Element cesta = doc.getRootElement();
System.out.println("CESTA:");
System.out.println("Loja: " + cesta.getAttributeValue("loja"));
System.out.println("-------------------------------------------------");
List<Element> canetas = cesta.getChildren("caneta");
for (int i = 0; i < canetas.size(); i++) {
Element caneta = canetas.get(i);
System.out.println("CANETA:");
System.out.println("Modelo: " + caneta.getValue());
}
System.out.println("-------------------------------------------------");
List<Element> lapis = cesta.getChildren("lapis");
for (int i = 0; i < lapis.size(); i++) {
Element umLapis = lapis.get(i);
System.out.println("LÁPIS:");
System.out.println("Modelo: " + umLapis.getValue());
}
System.out.println("-------------------------------------------------");
}
}
Observe que entre as tags <cesta> e </cesta>, foram colocadas tags <caneta> e <lapis> muitas vezes. Logo, foram acessadas separadamente os elementos do tipo caneta e lápis pela função getChildren.
A SAX é prove uma forma direta de acessar elementos de um XML por seu identificador e não requer leitura de uma hierarquia para isso. Diferente da SAX, a DOM é uma maneira de ler o XML também, mas por meio de uma hierarquia.
No entanto, este artigo apresenta um exemplo de leitura de XML via SAX na linguagem Java.
Documento XML
Crie o arquivo cesta.xml. No exemplo, este arquivo foi criado no diretório c:\temp. Preencha com o conteúdo a seguir:
<cesta loja="Papelex">
<caneta>Caneta BIC</caneta>
<caneta>Caneta Kilometrica</caneta>
<caneta>Caneta Faber</caneta>
<caneta>Caneta Helios</caneta>
<caneta>Caneta Ferrari</caneta>
<caneta>Caneta Pentec</caneta>
<lapis>Caneta BIC</lapis>
<lapis>Caneta Kilometrica</lapis>
<lapis>Caneta Faber</lapis>
<lapis>Caneta Helios</lapis>
<lapis>Caneta Ferrari</lapis>
<lapis>Caneta Pentec</lapis>
</cesta>
Classe de leitura do XML
Crie a classe de leitura do XML chamada TestaXML.java.
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
public class TestaXML {
public static void main(String[] args) throws JDOMException, IOException {
File f = new File("c:/temp/cesta.xml");
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(f);
Element cesta = doc.getRootElement();
System.out.println("CESTA:");
System.out.println("Loja: " + cesta.getAttributeValue("loja"));
System.out.println("-------------------------------------------------");
List<Element> canetas = cesta.getChildren("caneta");
for (int i = 0; i < canetas.size(); i++) {
Element caneta = canetas.get(i);
System.out.println("CANETA:");
System.out.println("Modelo: " + caneta.getValue());
}
System.out.println("-------------------------------------------------");
List<Element> lapis = cesta.getChildren("lapis");
for (int i = 0; i < lapis.size(); i++) {
Element umLapis = lapis.get(i);
System.out.println("LÁPIS:");
System.out.println("Modelo: " + umLapis.getValue());
}
System.out.println("-------------------------------------------------");
}
}
Observe que entre as tags <cesta> e </cesta>, foram colocadas tags <caneta> e <lapis> muitas vezes. Logo, foram acessadas separadamente os elementos do tipo caneta e lápis pela função getChildren.
terça-feira, 26 de junho de 2012
TDC 2012
No próximo dia 4 até 8/07/2012, ocorrerá em São Paulo, na Universidade
Anhembi Morumbi a sexta edição TDC (The Developers Conference), evento
organizado pela GlobalCode para profissionais e estudantes iniciantes em
tecnologia.
Você pode se inscrever e selecionar uma trilha de palestra por dia. Por exemplo, se na segunda-feira você decidir participar da trilha sobre Java, passará o "dia inteiro" de palestra vendo somente Java. Além do Java, outras tecnologias atuais serão palestradas com Ruby, Python, PHP, C/C++, Scala, .NET, Arduino, Testes, Marketing Digital, Robótica, Banco de Dados, Android, iOS, WindowsPhone e muito mais.
As inscrições já estão abertas no site do evento.
Para mais informações, acesse:
Você pode se inscrever e selecionar uma trilha de palestra por dia. Por exemplo, se na segunda-feira você decidir participar da trilha sobre Java, passará o "dia inteiro" de palestra vendo somente Java. Além do Java, outras tecnologias atuais serão palestradas com Ruby, Python, PHP, C/C++, Scala, .NET, Arduino, Testes, Marketing Digital, Robótica, Banco de Dados, Android, iOS, WindowsPhone e muito mais.
As inscrições já estão abertas no site do evento.
Para mais informações, acesse:
Marcadores:
IT Events 2012,
Java Event,
Java Eventos 2012,
Palestra Android 2012.,
Palestras Java 2012,
TDC 2012,
The Developers Conference 2012,
TI Eventos 2012
sábado, 23 de junho de 2012
Java - Clonagem de objetos
Durante sua vida um programador pode lidar com a necessidade de manter um objeto matriz e reproduzir cópias deste objeto para fins de alterar estas cópias sem perder a originalidade da matriz.
Posso descrever uma situação onde tive uma rotina de envio de mensagem para um determinado host, mas esta rotina alterava propriedades da minha mensagem original porque seu comportamento normal era este. Foi desenhado para envio de mensagem sempre para um destinatário. Efeito:
Acrescentei ao sistema uma função a mais para enviar a mensagem destinada para mais de um host através de clonagem para preservar a original. Efeito:
Após implementar send_to_all, simplismente esta função passou a varrer a lista ativa de conexões de hosts e para cada iteração clona a mensagem original e passa o clone como parâmetro de send_to_some.
Vamos para a prática!
WTByteArrayInputStream
Esta classe representa um mecanismo para transformar uma série de bytes em um objeto. Será utilizada no processo de clonagem.
import java.io.InputStream;
public class WTByteArrayInputStream extends InputStream {
protected byte[] buf = null;
protected int count = 0;
protected int pos = 0;
public WTByteArrayInputStream(byte[] buf, int count) {
this.buf = buf;
this.count = count;
}
public final int available() {
return count - pos;
}
public final int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
public final int read(byte[] b, int off, int len) {
if (pos >= count)
return -1;
if ((pos + len) > count)
len = (count - pos);
System.arraycopy(buf, pos, b, off, len);
pos += len;
return len;
}
public final long skip(long n) {
if ((pos + n) > count)
n = count - pos;
if (n < 0)
return 0;
pos += n;
return n;
}
}
WTByteArrayOutputStream
Esta classe representa um mecanismo para transformar um objeto em uma série de bytes e oferece uma função para obter uma conexão com os bytes do objeto.
import java.io.InputStream;
import java.io.OutputStream;
public class WTByteArrayOutputStream extends OutputStream {
protected byte[] buf = null;
protected int size = 0;
public WTByteArrayOutputStream() {
this(5 * 1024);
}
public WTByteArrayOutputStream(int initSize) {
this.size = 0;
this.buf = new byte[initSize];
}
private void verifyBufferSize(int sz) {
if (sz > buf.length) {
byte[] old = buf;
buf = new byte[Math.max(sz, 2 * buf.length )];
System.arraycopy(old, 0, buf, 0, old.length);
old = null;
}
}
public int getSize() {
return size;
}
public byte[] getByteArray() {
return buf;
}
public final void write(byte b[]) {
verifyBufferSize(size + b.length);
System.arraycopy(b, 0, buf, size, b.length);
size += b.length;
}
public final void write(byte b[], int off, int len) {
verifyBufferSize(size + len);
System.arraycopy(b, off, buf, size, len);
size += len;
}
public final void write(int b) {
verifyBufferSize(size + 1);
buf[size++] = (byte) b;
}
public void reset() {
size = 0;
}
public InputStream getInputStream() {
return new WTByteArrayInputStream(buf, size);
}
}
WTCloneObject
Esta classe é a própria rotina de clonagem.
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class WTCloneObject {
public static Object copy(Object orig) {
Object obj = null;
try {
WTByteArrayOutputStream fbos = new WTByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(fbos);
out.writeObject(orig);
out.flush();
out.close();
ObjectInputStream in = new ObjectInputStream(fbos.getInputStream());
obj = in.readObject();
}
catch(IOException e) {
e.printStackTrace();
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
return obj;
}
}
DadosPessoais
Esta classe é um bean muito simples que auxiliará apenas o teste. Como será o tipo do objeto para clonar em nosso teste, esta deve implementar a interface Serializable. Observe a palavra-chave transient no parâmetro senha_Bancaria, pois indica que o atributo não será gravado quando o objeto de DadosPessoais for serializado em bytes. Esta mesma técnica pode ser utilizada se um dos atributos for tão complexo ou fechado que não te permite fazer suas classes ou sub-classes implementarem a interface Serializable.
import java.io.Serializable;
class DadosPessoais implements Serializable {
private String nome;
private int idade;
private String telefone;
private String sexo;
private transient String senha_Bancaria;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public int getIdade() {
return idade;
}
public void setIdade(int idade) {
this.idade = idade;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public String getSexo() {
return sexo;
}
public void setSexo(String sexo) {
this.sexo = sexo;
}
public String getSenha_Bancaria() {
return senha_Bancaria;
}
public void setSenha_Bancaria(String senha_Bancaria) {
this.senha_Bancaria = senha_Bancaria;
}
@Override
public String toString() {
return "DadosPessoais [nome=" + nome + ", idade=" + idade
+ ", telefone=" + telefone + ", sexo=" + sexo
+ ", senha_Bancaria=" + senha_Bancaria + "]";
}
}
Testando
Crie uma classe de teste com um método main para testar a clonagem:
DadosPessoais matriz = new DadosPessoais();
matriz.setNome("Jose");
matriz.setIdade(30);
matriz.setTelefone("+55 11 88888888");
matriz.setSexo("M");
matriz.setSenha_Bancaria("123#%!");
System.out.println("Esta é a matriz");
System.out.println(matriz.toString());
System.out.println("");
for (int i = 0; i < 10; i++) {
DadosPessoais clone = (DadosPessoais) WTCloneObject.copy(matriz);
System.out.println("Este é o clone id = " + i);
System.out.println(clone.toString());
System.out.println("");
}
Fique atento, a senha não foi clonada em nenhum momento porque foi marcada como transient na classe DadosPessoais.
Até a próxima.
Posso descrever uma situação onde tive uma rotina de envio de mensagem para um determinado host, mas esta rotina alterava propriedades da minha mensagem original porque seu comportamento normal era este. Foi desenhado para envio de mensagem sempre para um destinatário. Efeito:
d - Destino da mensagem (host).
m.o. - Mensagem original.
m.a. - Mensagem alterada.
send_to_some (d, m.o. -> m.a.) - Função de envio para d onde m.o. passa a ser m.a. ao final do processamento.
Acrescentei ao sistema uma função a mais para enviar a mensagem destinada para mais de um host através de clonagem para preservar a original. Efeito:
m.o. - Mensagem original.
m.c. - Mensagem clonada.
m.a. - Mensagem alterada.
send_to_all (m.o. -> m.a.) - Função de envio para todos m.c. passa a ser m.a. ao final do processamento, mas m.o. é preservado.
Após implementar send_to_all, simplismente esta função passou a varrer a lista ativa de conexões de hosts e para cada iteração clona a mensagem original e passa o clone como parâmetro de send_to_some.
Vamos para a prática!
WTByteArrayInputStream
Esta classe representa um mecanismo para transformar uma série de bytes em um objeto. Será utilizada no processo de clonagem.
import java.io.InputStream;
public class WTByteArrayInputStream extends InputStream {
protected byte[] buf = null;
protected int count = 0;
protected int pos = 0;
public WTByteArrayInputStream(byte[] buf, int count) {
this.buf = buf;
this.count = count;
}
public final int available() {
return count - pos;
}
public final int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
public final int read(byte[] b, int off, int len) {
if (pos >= count)
return -1;
if ((pos + len) > count)
len = (count - pos);
System.arraycopy(buf, pos, b, off, len);
pos += len;
return len;
}
public final long skip(long n) {
if ((pos + n) > count)
n = count - pos;
if (n < 0)
return 0;
pos += n;
return n;
}
}
WTByteArrayOutputStream
Esta classe representa um mecanismo para transformar um objeto em uma série de bytes e oferece uma função para obter uma conexão com os bytes do objeto.
import java.io.InputStream;
import java.io.OutputStream;
public class WTByteArrayOutputStream extends OutputStream {
protected byte[] buf = null;
protected int size = 0;
public WTByteArrayOutputStream() {
this(5 * 1024);
}
public WTByteArrayOutputStream(int initSize) {
this.size = 0;
this.buf = new byte[initSize];
}
private void verifyBufferSize(int sz) {
if (sz > buf.length) {
byte[] old = buf;
buf = new byte[Math.max(sz, 2 * buf.length )];
System.arraycopy(old, 0, buf, 0, old.length);
old = null;
}
}
public int getSize() {
return size;
}
public byte[] getByteArray() {
return buf;
}
public final void write(byte b[]) {
verifyBufferSize(size + b.length);
System.arraycopy(b, 0, buf, size, b.length);
size += b.length;
}
public final void write(byte b[], int off, int len) {
verifyBufferSize(size + len);
System.arraycopy(b, off, buf, size, len);
size += len;
}
public final void write(int b) {
verifyBufferSize(size + 1);
buf[size++] = (byte) b;
}
public void reset() {
size = 0;
}
public InputStream getInputStream() {
return new WTByteArrayInputStream(buf, size);
}
}
WTCloneObject
Esta classe é a própria rotina de clonagem.
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class WTCloneObject {
public static Object copy(Object orig) {
Object obj = null;
try {
WTByteArrayOutputStream fbos = new WTByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(fbos);
out.writeObject(orig);
out.flush();
out.close();
ObjectInputStream in = new ObjectInputStream(fbos.getInputStream());
obj = in.readObject();
}
catch(IOException e) {
e.printStackTrace();
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
return obj;
}
}
DadosPessoais
Esta classe é um bean muito simples que auxiliará apenas o teste. Como será o tipo do objeto para clonar em nosso teste, esta deve implementar a interface Serializable. Observe a palavra-chave transient no parâmetro senha_Bancaria, pois indica que o atributo não será gravado quando o objeto de DadosPessoais for serializado em bytes. Esta mesma técnica pode ser utilizada se um dos atributos for tão complexo ou fechado que não te permite fazer suas classes ou sub-classes implementarem a interface Serializable.
import java.io.Serializable;
class DadosPessoais implements Serializable {
private String nome;
private int idade;
private String telefone;
private String sexo;
private transient String senha_Bancaria;
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public int getIdade() {
return idade;
}
public void setIdade(int idade) {
this.idade = idade;
}
public String getTelefone() {
return telefone;
}
public void setTelefone(String telefone) {
this.telefone = telefone;
}
public String getSexo() {
return sexo;
}
public void setSexo(String sexo) {
this.sexo = sexo;
}
public String getSenha_Bancaria() {
return senha_Bancaria;
}
public void setSenha_Bancaria(String senha_Bancaria) {
this.senha_Bancaria = senha_Bancaria;
}
@Override
public String toString() {
return "DadosPessoais [nome=" + nome + ", idade=" + idade
+ ", telefone=" + telefone + ", sexo=" + sexo
+ ", senha_Bancaria=" + senha_Bancaria + "]";
}
}
Testando
Crie uma classe de teste com um método main para testar a clonagem:
DadosPessoais matriz = new DadosPessoais();
matriz.setNome("Jose");
matriz.setIdade(30);
matriz.setTelefone("+55 11 88888888");
matriz.setSexo("M");
matriz.setSenha_Bancaria("123#%!");
System.out.println("Esta é a matriz");
System.out.println(matriz.toString());
System.out.println("");
for (int i = 0; i < 10; i++) {
DadosPessoais clone = (DadosPessoais) WTCloneObject.copy(matriz);
System.out.println("Este é o clone id = " + i);
System.out.println(clone.toString());
System.out.println("");
}
Fique atento, a senha não foi clonada em nenhum momento porque foi marcada como transient na classe DadosPessoais.
Até a próxima.
Marcadores:
Java Buffer Stream,
Java Clonagem,
Java Clone,
Java Cópia de Objetos,
Java Copy Object,
Java I/O,
Java Object Input,
Java Object Output.,
Java Serializable,
Java Serialização
quarta-feira, 20 de junho de 2012
Java - Limpeza automática de coleções
Um dos problemas principais em aplicações do tipo serviço é a alocação e devolução de memória, porque aplicações do tipo serviço tendem a ter um ciclo de vida de horas, dias ou meses por terem como premissa uma longa disponibilidade e robustez.
A depender da complexidade do serviço em relação ao volume de requisições à processar, é costume do arquiteto definir o uso de pilhas sofisticadas (coleções) para enfileirar e controlar o fluxo do processamento de requisições. Não obstante, estas pilhas podem naturalmente receber novos elementos, porém por uma eventual falha (ou não tratada) fará alguns de seus elementos ficarem empilhados "para sempre" até o fim do serviço. O pior é que a gc (garbage collector) nesta situação não fara a limpeza porque os elementos perdidos estão vinculados a um objeto de pilha (elemento).
No exemplo deste artigo, coloco a disposição especializações das coleções ConcurrentHashMap e ConcurrentLinkedQueue por se tratar das pilhas mais comuns numa arquitetura de serviço com paralelismo para alta produtividade. As especializações a seguir tem o mesmo propósito, expirar elementos após 120 segundos (2 minutos) em fila. Perceba na constante KEY_TIMEOUT_VALUE, encontrada em cada uma das especializações, que este tempo pode ser aumentado ou diminuído.
Importante: O mecanismo proposto como base expira elementos da pilha e destrói objetos envelopados por WTTimeObject através de anulamento. Porém, se o objeto núcleo armazenado na pilha for muito complexo, como é o caso de um Socket ou File, este será removido do empilhamento, mas ficará em aberto mesmo com o anulamento enquanto o serviço estiver ativo. Para este caso, recomendo um controle transacional conciente para o correto encerramento do objeto complexo.
WTTimeObject
Esta classe é um invólucro usado por ambas as especializações citadas anteriormente. O WTTimeObject contém atributos para a guarda do tempo de último acesso ao elemento núcleo empilhado (aquele que realmente se pretente armazenar) generalizado em <V>. Se você não entende de Generics em Java, terá dificuldades de compreender este artigo.
}
WTConcurrentLinkedQueue
Esta é a especialização da coleção ConcurrentLinkedQueue, perceba que existe uma inner class na especialização chamada ClearExpiredRunnable que é do tipo Runnable, porque a cada adição a instância de ClearExpiredRunnable será acionada à verificar e eliminar elementos não acessados a mais de 120 segundos.
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
public class WTConcurrentLinkedQueue<V> {
public static final long CLEAR_EXPIRED_INTERVAL_VALUE = 60; // A cada 1 minuto verifica
public static final long KEY_TIMEOUT_VALUE = 120; // Elementos mais velhos do que 2 minutos
private ConcurrentLinkedQueue<WTTimeObject<V>> queue;
private long keyTimeout;
private String id;
private long clearExpiredTimeout;
private ClearExpiredValues clearExpiredRunnable;
public WTConcurrentLinkedQueue(String id) {
this(id, KEY_TIMEOUT_VALUE);
}
public WTConcurrentLinkedQueue(String id, Long keyTimeout) {
this.id = id;
this.queue = new ConcurrentLinkedQueue<WTTimeObject<V>>();
updateTimeoutOfClearExpired();
this.keyTimeout = keyTimeout;
clearExpiredRunnable = new ClearExpiredValues(this);
}
public boolean add(V value) {
boolean result = false;
result = this.queue.add(new WTTimeObject<V>(value));
// Chama evento para excluir elementos expirados
clearExpiredValues();
return result;
}
public V peek() {
WTTimeObject<V> result = null;
result = this.queue.peek();
if (result == null) {
return null;
} else {
return result.getValue();
}
}
public V poll() {
WTTimeObject<V> result = null;
result = this.queue.poll();
if (result == null) {
return null;
} else {
return result.getValue();
}
}
public boolean remove(V value) {
boolean result = false;
WTTimeObject<V> timeObj = null;
synchronized(queue) {
Iterator<WTTimeObject<V>> it = this.queue.iterator();
while (it.hasNext()) {
timeObj = it.next();
// A comparação deve ser feita com o conteúdo de DGTimeObject e nunca direto
if (value instanceof String) {
if (timeObj.getValue(false).equals(value)) {
result = this.queue.remove(timeObj);
it.remove();
break;
}
}else {
if (timeObj.getValue(false) == value) {
result = this.queue.remove(timeObj);
it.remove();
break;
}
}
}
}
return result;
}
public Iterator<WTTimeObject<V>> iterator() {
return this.queue.iterator();
}
public int size() {
return this.queue.size();
}
public void clearExpiredValues() {
if (clearExpiredTimeout < System.currentTimeMillis()) {
updateTimeoutOfClearExpired();
Thread r = new Thread(clearExpiredRunnable);
r.start();
}
}
public void clear() {
this.queue.clear();
}
class ClearExpiredValues implements Runnable {
private WTConcurrentLinkedQueue<V> queue;
public ClearExpiredValues(WTConcurrentLinkedQueue<V> queue) {
this.queue = queue;
}
public void run() {
int countRemove = 0;
Iterator<WTTimeObject<V>> it = this.queue.iterator();
WTTimeObject<V> value;
while (it.hasNext()) {
value = it.next();
// Verifica se o objeto venceu
if (value != null && value.getLastAccessTime() + (keyTimeout * 1000) < System.currentTimeMillis()) {
// ATENÇÃO: Nunca anular o valor dentro de value, porque o remove encontra
// o elemento para remoção através de value.getValue().
this.queue.remove(value.getValue());
it.remove();
value.setValue(null);
value = null;
countRemove++;
}
}
if (countRemove > 0)
System.out.println(String.format("Removido(s) %d elemento(s) vencidos de %s.", countRemove, id));
}
}
public void updateTimeoutOfClearExpired() {
clearExpiredTimeout = System.currentTimeMillis() + (CLEAR_EXPIRED_INTERVAL_VALUE * 1000);
}
}
WTConcurrentHashMap
Já esta é a outra especialização, agora da coleção ConcurrentHashMap, perceba que também existe uma inner class na especialização chamada ClearExpiredRunnable que é do tipo Runnable, porque a cada adição a instância de ClearExpiredRunnable será acionada à verificar e eliminar elementos não acessados a mais de 120 segundos.
Testando as coleções especializadas
Para finalizar, vamos testar as coleções para observar a limpeza de elementos com mais de 120 segundos sem acesso:
WTConcurrentLinkedQueue<String> map = new WTConcurrentLinkedQueue<String>("ColecaoTeste");
WTConcurrentHashMap<Integer, String> map1 = new WTConcurrentHashMap<Integer, String>("ColecaoTeste");
int contador = 0;
while (true) {
contador++;
map.add("Teste " + contador);
map1.put(contador, "Teste " + contador);
try {
Thread.sleep(1L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
A depender da complexidade do serviço em relação ao volume de requisições à processar, é costume do arquiteto definir o uso de pilhas sofisticadas (coleções) para enfileirar e controlar o fluxo do processamento de requisições. Não obstante, estas pilhas podem naturalmente receber novos elementos, porém por uma eventual falha (ou não tratada) fará alguns de seus elementos ficarem empilhados "para sempre" até o fim do serviço. O pior é que a gc (garbage collector) nesta situação não fara a limpeza porque os elementos perdidos estão vinculados a um objeto de pilha (elemento).
No exemplo deste artigo, coloco a disposição especializações das coleções ConcurrentHashMap e ConcurrentLinkedQueue por se tratar das pilhas mais comuns numa arquitetura de serviço com paralelismo para alta produtividade. As especializações a seguir tem o mesmo propósito, expirar elementos após 120 segundos (2 minutos) em fila. Perceba na constante KEY_TIMEOUT_VALUE, encontrada em cada uma das especializações, que este tempo pode ser aumentado ou diminuído.
Importante: O mecanismo proposto como base expira elementos da pilha e destrói objetos envelopados por WTTimeObject através de anulamento. Porém, se o objeto núcleo armazenado na pilha for muito complexo, como é o caso de um Socket ou File, este será removido do empilhamento, mas ficará em aberto mesmo com o anulamento enquanto o serviço estiver ativo. Para este caso, recomendo um controle transacional conciente para o correto encerramento do objeto complexo.
WTTimeObject
Esta classe é um invólucro usado por ambas as especializações citadas anteriormente. O WTTimeObject contém atributos para a guarda do tempo de último acesso ao elemento núcleo empilhado (aquele que realmente se pretente armazenar) generalizado em <V>. Se você não entende de Generics em Java, terá dificuldades de compreender este artigo.
public class WTTimeObject<V> {
private static long nextVal = 0L;
private long lastAccessTime = 0L;
private V value;
private long objectId;
public WTTimeObject() {
this.objectId = this.getNextVal();
}
public WTTimeObject(V value) {
this();
this.lastAccessTime = System.currentTimeMillis();
this.value = value;
}
public long getLastAccessTime() {
return lastAccessTime;
}
public V getValue() {
return this.getValue(true);
}
public V getValue(boolean doUpdateAccessTime) {
if (doUpdateAccessTime) {
this.lastAccessTime = System.currentTimeMillis();
}
return value;
}
public void setValue(V value) {
this.lastAccessTime = System.currentTimeMillis();
this.value = value;
}
public synchronized long getNextVal() {
if (WTTimeObject.nextVal < Long.MAX_VALUE) {
WTTimeObject.nextVal++;
} else {
WTTimeObject.nextVal=1;
}
return WTTimeObject.nextVal;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (objectId ^ (objectId >>> 32));
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
WTTimeObject<?> other = (WTTimeObject<?>) obj;
if (objectId != other.objectId)
return false;
return true;
}
private static long nextVal = 0L;
private long lastAccessTime = 0L;
private V value;
private long objectId;
public WTTimeObject() {
this.objectId = this.getNextVal();
}
public WTTimeObject(V value) {
this();
this.lastAccessTime = System.currentTimeMillis();
this.value = value;
}
public long getLastAccessTime() {
return lastAccessTime;
}
public V getValue() {
return this.getValue(true);
}
public V getValue(boolean doUpdateAccessTime) {
if (doUpdateAccessTime) {
this.lastAccessTime = System.currentTimeMillis();
}
return value;
}
public void setValue(V value) {
this.lastAccessTime = System.currentTimeMillis();
this.value = value;
}
public synchronized long getNextVal() {
if (WTTimeObject.nextVal < Long.MAX_VALUE) {
WTTimeObject.nextVal++;
} else {
WTTimeObject.nextVal=1;
}
return WTTimeObject.nextVal;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (objectId ^ (objectId >>> 32));
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
WTTimeObject<?> other = (WTTimeObject<?>) obj;
if (objectId != other.objectId)
return false;
return true;
}
WTConcurrentLinkedQueue
Esta é a especialização da coleção ConcurrentLinkedQueue, perceba que existe uma inner class na especialização chamada ClearExpiredRunnable que é do tipo Runnable, porque a cada adição a instância de ClearExpiredRunnable será acionada à verificar e eliminar elementos não acessados a mais de 120 segundos.
import java.util.concurrent.ConcurrentLinkedQueue;
public class WTConcurrentLinkedQueue<V> {
public static final long CLEAR_EXPIRED_INTERVAL_VALUE = 60; // A cada 1 minuto verifica
public static final long KEY_TIMEOUT_VALUE = 120; // Elementos mais velhos do que 2 minutos
private ConcurrentLinkedQueue<WTTimeObject<V>> queue;
private long keyTimeout;
private String id;
private long clearExpiredTimeout;
private ClearExpiredValues clearExpiredRunnable;
public WTConcurrentLinkedQueue(String id) {
this(id, KEY_TIMEOUT_VALUE);
}
public WTConcurrentLinkedQueue(String id, Long keyTimeout) {
this.id = id;
this.queue = new ConcurrentLinkedQueue<WTTimeObject<V>>();
updateTimeoutOfClearExpired();
this.keyTimeout = keyTimeout;
clearExpiredRunnable = new ClearExpiredValues(this);
}
public boolean add(V value) {
boolean result = false;
result = this.queue.add(new WTTimeObject<V>(value));
// Chama evento para excluir elementos expirados
clearExpiredValues();
return result;
}
public V peek() {
WTTimeObject<V> result = null;
result = this.queue.peek();
if (result == null) {
return null;
} else {
return result.getValue();
}
}
public V poll() {
WTTimeObject<V> result = null;
result = this.queue.poll();
if (result == null) {
return null;
} else {
return result.getValue();
}
}
public boolean remove(V value) {
boolean result = false;
WTTimeObject<V> timeObj = null;
synchronized(queue) {
Iterator<WTTimeObject<V>> it = this.queue.iterator();
while (it.hasNext()) {
timeObj = it.next();
// A comparação deve ser feita com o conteúdo de DGTimeObject e nunca direto
if (value instanceof String) {
if (timeObj.getValue(false).equals(value)) {
result = this.queue.remove(timeObj);
it.remove();
break;
}
}else {
if (timeObj.getValue(false) == value) {
result = this.queue.remove(timeObj);
it.remove();
break;
}
}
}
}
return result;
}
public Iterator<WTTimeObject<V>> iterator() {
return this.queue.iterator();
}
public int size() {
return this.queue.size();
}
public void clearExpiredValues() {
if (clearExpiredTimeout < System.currentTimeMillis()) {
updateTimeoutOfClearExpired();
Thread r = new Thread(clearExpiredRunnable);
r.start();
}
}
public void clear() {
this.queue.clear();
}
class ClearExpiredValues implements Runnable {
private WTConcurrentLinkedQueue<V> queue;
public ClearExpiredValues(WTConcurrentLinkedQueue<V> queue) {
this.queue = queue;
}
public void run() {
int countRemove = 0;
Iterator<WTTimeObject<V>> it = this.queue.iterator();
WTTimeObject<V> value;
while (it.hasNext()) {
value = it.next();
// Verifica se o objeto venceu
if (value != null && value.getLastAccessTime() + (keyTimeout * 1000) < System.currentTimeMillis()) {
// ATENÇÃO: Nunca anular o valor dentro de value, porque o remove encontra
// o elemento para remoção através de value.getValue().
this.queue.remove(value.getValue());
it.remove();
value.setValue(null);
value = null;
countRemove++;
}
}
if (countRemove > 0)
System.out.println(String.format("Removido(s) %d elemento(s) vencidos de %s.", countRemove, id));
}
}
public void updateTimeoutOfClearExpired() {
clearExpiredTimeout = System.currentTimeMillis() + (CLEAR_EXPIRED_INTERVAL_VALUE * 1000);
}
}
WTConcurrentHashMap
Já esta é a outra especialização, agora da coleção ConcurrentHashMap, perceba que também existe uma inner class na especialização chamada ClearExpiredRunnable que é do tipo Runnable, porque a cada adição a instância de ClearExpiredRunnable será acionada à verificar e eliminar elementos não acessados a mais de 120 segundos.
import java.util.Enumeration;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class WTConcurrentHashMap<K, V> {
public static final long CLEAR_EXPIRED_INTERVAL_VALUE = 60; // A cada 1 minuto verifica
public static final long KEY_TIMEOUT_VALUE = 120; // Elementos mais velhos do que 2 minutos
private ConcurrentHashMap<K, WTTimeObject<V>> map;
private long keyTimeout;
private String id;
private long clearExpiredTimeout;
private ClearExpiredKeys clearExpiredRunnable;
public WTConcurrentHashMap(String id) {
this(id, KEY_TIMEOUT_VALUE);
}
public WTConcurrentHashMap(String id, Long keyTimeout) {
this.id = id;
map = new ConcurrentHashMap<K, WTTimeObject<V>>();
updateTimeoutOfClearExpired();
this.keyTimeout = keyTimeout;
clearExpiredRunnable = new ClearExpiredKeys(map);
}
public V put(K key, V value) {
WTTimeObject<V> result = null;
synchronized(map) {
result = map.put(key, new WTTimeObject<V>(value));
}
if (this.keyTimeout > 0L) {
// Chama evento para excluir elementos expirados
clearExpiredKeys();
}
if (result == null) {
return null;
} else {
return value;
}
}
public V get(K key) {
WTTimeObject<V> result = null;
synchronized(map) {
result = map.get(key);
}
if (result == null) {
return null;
} else {
return result.getValue();
}
}
public V remove(K key) {
WTTimeObject<V> result = null;
synchronized(map) {
result = map.remove(key);
}
if (result == null) {
return null;
} else {
return result.getValue();
}
}
public void clearExpiredKeys() {
if (clearExpiredTimeout < System.currentTimeMillis()) {
updateTimeoutOfClearExpired();
Thread r = new Thread(clearExpiredRunnable);
r.start();
}
}
public void clear() {
synchronized(map) {
map.clear();
}
}
public Set<K> keySet() {
synchronized(map) {
return map.keySet();
}
}
public int size() {
synchronized(map) {
return map.size();
}
}
class ClearExpiredKeys implements Runnable {
private ConcurrentHashMap<K, WTTimeObject<V>> map;
public ClearExpiredKeys(ConcurrentHashMap<K, WTTimeObject<V>> map) {
this.map = map;
}
public void run() {
int countRemove = 0;
synchronized(map) {
Enumeration<K> en = map.keys();
K key;
WTTimeObject<V> value;
while (en.hasMoreElements()) {
key = en.nextElement();
value = map.get(key);
// Verifica se o objeto venceu
if (value != null && value.getLastAccessTime() + (keyTimeout * 1000) < System.currentTimeMillis()) {
value.setValue(null);
value = null;
map.remove(key);
countRemove++;
}
}
}
if (countRemove > 0)
System.out.println(String.format("Removido(s) %d elemento(s) vencidos de %s.", countRemove, id));
}
}
public void updateTimeoutOfClearExpired() {
clearExpiredTimeout = System.currentTimeMillis() + (CLEAR_EXPIRED_INTERVAL_VALUE * 1000);
}
}
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class WTConcurrentHashMap<K, V> {
public static final long CLEAR_EXPIRED_INTERVAL_VALUE = 60; // A cada 1 minuto verifica
public static final long KEY_TIMEOUT_VALUE = 120; // Elementos mais velhos do que 2 minutos
private ConcurrentHashMap<K, WTTimeObject<V>> map;
private long keyTimeout;
private String id;
private long clearExpiredTimeout;
private ClearExpiredKeys clearExpiredRunnable;
public WTConcurrentHashMap(String id) {
this(id, KEY_TIMEOUT_VALUE);
}
public WTConcurrentHashMap(String id, Long keyTimeout) {
this.id = id;
map = new ConcurrentHashMap<K, WTTimeObject<V>>();
updateTimeoutOfClearExpired();
this.keyTimeout = keyTimeout;
clearExpiredRunnable = new ClearExpiredKeys(map);
}
public V put(K key, V value) {
WTTimeObject<V> result = null;
synchronized(map) {
result = map.put(key, new WTTimeObject<V>(value));
}
if (this.keyTimeout > 0L) {
// Chama evento para excluir elementos expirados
clearExpiredKeys();
}
if (result == null) {
return null;
} else {
return value;
}
}
public V get(K key) {
WTTimeObject<V> result = null;
synchronized(map) {
result = map.get(key);
}
if (result == null) {
return null;
} else {
return result.getValue();
}
}
public V remove(K key) {
WTTimeObject<V> result = null;
synchronized(map) {
result = map.remove(key);
}
if (result == null) {
return null;
} else {
return result.getValue();
}
}
public void clearExpiredKeys() {
if (clearExpiredTimeout < System.currentTimeMillis()) {
updateTimeoutOfClearExpired();
Thread r = new Thread(clearExpiredRunnable);
r.start();
}
}
public void clear() {
synchronized(map) {
map.clear();
}
}
public Set<K> keySet() {
synchronized(map) {
return map.keySet();
}
}
public int size() {
synchronized(map) {
return map.size();
}
}
class ClearExpiredKeys implements Runnable {
private ConcurrentHashMap<K, WTTimeObject<V>> map;
public ClearExpiredKeys(ConcurrentHashMap<K, WTTimeObject<V>> map) {
this.map = map;
}
public void run() {
int countRemove = 0;
synchronized(map) {
Enumeration<K> en = map.keys();
K key;
WTTimeObject<V> value;
while (en.hasMoreElements()) {
key = en.nextElement();
value = map.get(key);
// Verifica se o objeto venceu
if (value != null && value.getLastAccessTime() + (keyTimeout * 1000) < System.currentTimeMillis()) {
value.setValue(null);
value = null;
map.remove(key);
countRemove++;
}
}
}
if (countRemove > 0)
System.out.println(String.format("Removido(s) %d elemento(s) vencidos de %s.", countRemove, id));
}
}
public void updateTimeoutOfClearExpired() {
clearExpiredTimeout = System.currentTimeMillis() + (CLEAR_EXPIRED_INTERVAL_VALUE * 1000);
}
}
Testando as coleções especializadas
Para finalizar, vamos testar as coleções para observar a limpeza de elementos com mais de 120 segundos sem acesso:
WTConcurrentLinkedQueue<String> map = new WTConcurrentLinkedQueue<String>("ColecaoTeste");
WTConcurrentHashMap<Integer, String> map1 = new WTConcurrentHashMap<Integer, String>("ColecaoTeste");
int contador = 0;
while (true) {
contador++;
map.add("Teste " + contador);
map1.put(contador, "Teste " + contador);
try {
Thread.sleep(1L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Assinar:
Postagens (Atom)