You are currently browsing the category archive for the 'Java' category.
Palavras-chave: Java, classes não encontradas, classpath
Os erros NoClassDefFoundError e ClassNotFoundException acontecem com frequência em aplicações Java e é importante entendê-los com clareza, a fim de diagnosticar problemas mais rapidamente.
Ambos os erros significam essencialmente a mesma coisa: uma classe não foi encontrada. A diferença é a maneira de que esta classe estava sendo procurada.
No caso do NoClassDefFoundError, a JVM estava tentando carregar uma classe para invocar um método ou criar uma instância e não a encontrou. Isso acontece tipicamente quando a JVM encontra uma referência a uma classe que ainda não foi carregada.
Para provocar isso como exemplo, crie um programa com duas classes, chamadas Main e Pessoa:
public class Main {
public static void main(String[] args) {
new Pessoa();
}
}
class Pessoa {}
Compile este código manualmente (javac *.java) e execute-o. O programa deve rodar sem nenhum problema. Agora, remova o arquivo gerado chamado Pessoa.class e re-execute o programa. Isso gerará uma saída parecida com esta:
# java Main
Exception in thread "main" java.lang.NoClassDefFoundError: Pessoa
at Main.main(Main.java:3)
Quando você compilou o código, a classe Pessoa estava também sendo compilada (ela poderia estar no classpath) e instrução ‘new’ fez uma referência direta a ela. No momento de executar o código, a classe Pessoa não estava mais disponível. Isso é um erro inesperado, portanto NoClassDefFoundError é um java.lang.Error.
Já a ClassNotFoundException é uma java.lang.Exception, um tipo de erro mais “suave”. Esta exceção acontece quando alguma parte do código (seu ou de alguma classe que você utiliza) está carregando explicitamente uma classe, através de métodos como Class.forName(), ClassLoader.findSystemClass() ou ClassLoader.loadClass(). Neste caso, o código não precisa ter sido compilado junto a classe referenciada.
Considere o exemplo a seguir:
public class Main {
public static void main(String[] args) throws Exception {
Object o = Class.forName("pacote.NaoExiste").newInstance();
}
}
Isso produzirá uma saída parecida com:
# java Main
Exception in thread "main" java.lang.ClassNotFoundException: pacote.NaoExiste
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
No momento da compilação, a classe não estava presente, mas o código foi compilado sem problemas. Na execução, porém, a ausência da classe fez o carregamento explícito falhar.
Palavras-chave: Java, JavaMail, email, SMTP, SMTP autenticado
Enviar emails é uma tarefa corriqueira, mas sempre precisamos olhar um exemplo da JavaMail API para montar uma classe utilitária. A seguir, um exemplo mínimo do envio de emails através de um servidor SMTP autenticado:
public void enviarEmail(String emailDest, String nomeDest,
String emailRemet, String nomeRemet, String assunto, String corpo)
throws Exception {
Properties props = System.getProperties();
props.put("mail.smtp.host", "smtp.firma.com.br");
props.put("mail.smtp.auth", "true");
Authenticator auth = new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("fulano", "suasenha");
}};
Session session = Session.getInstance(props, auth);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(emailRemet, nomeRemet));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(emailDest, nomeDest));
message.setSubject(assunto);
message.setContent(corpo, "text/plain");
Transport.send(message);
}
Para executar este código, é preciso colocar o JavaMail (mail.jar) e o Java Activation Framework (activation.jar) em seu classpath. Se você usa Java 6, não vai precisar do JAF no classpath, pois este foi embutido nesta versão do Java.
Para testar o código, pode-se fazer algo como:
enviarEmail("todos@firma.com.br", "Lista da Firma", "fulano@firma.com.br",
"Fulano da Silva", "Codare", "Gosto do Codare. E vocês?");
Se você usa emails dentro de Servidores de Aplicação, pode fazer lookup da sessão no JNDI, ao invés de criá-la passando as propriedades. Você a receberá com as propriedades setadas pelo administrador do ambiente e, como desenvolvedor, não precisará se preocupar com coisas como “qual é o servidor de email do ambiente de produção”:
Session session =
(Session) new InitialContext().lookup("java:comp/env/mail/Sessao");
Palavras-chave: ping, ICMP, java.net.InetAddress, isReachable, Java 5
A partir do Java 5, fazer ping em um servidor usando Java ficou bastante simples. É só usar o método isReachable da classe java.net.InetAddress.
O método a seguir usa isReachable para pingar um servidor com tempo limite de cinco segundos (timeout igual a 5000 milisegundos):
private static void pingar(String host) {
try {
if (InetAddress.getByName(host).isReachable(5000))
System.out.println("Ping OK: " + host);
else
System.out.println("Ping FALHOU: " + host);
} catch (Exception e) {
System.err.println("Ping FALHOU: " + host + " - " + e);
}
}
Podemos testar este código da seguinte maneira:
pingar("www.google.com");
pingar("localhost");
pingar("www.umgooglequenaoexiste.com");
E uma possível execução é:
Ping FALHOU: www.google.com Ping OK: localhost Ping FALHOU: www.umgooglequenaoexiste.com - java.net.UnknownHostException: www.umgooglequenaoexiste.com
Neste caso, o servidor do Google foi encontrado no DNS, mas não se conseguiu pingá-lo. O host local pingou fácil e o último nome não foi encontrado no DNS.
O método isReachable funciona como um ping, mas é um pouco mais que isso. A implementação normalmente tentará enviar uma mensagem ICMP Echo Request. Caso não consiga, tentará abrir uma conexão TCP à porta 7.
Palavras-chave: classpath, wildcards, caractere curinga, Java 6
O Java 6 trouxe um recurso para facilitar a vida do programador: os caracteres curinga (wildcards) na definição do classpath. O que antes você fazia assim:
java -cp lib/codare-main.jar:lib/codare-utils.jar:lib/mail.jar \\ codare.MinhaClasse
Hoje pode fazer assim:
java -cp lib/\\* codare.MinhaClasse
Isso incluirá todos os arquivos *.jar dentro do diretório lib. Note bem: apenas os jars. Se você precisar de arquivos .class em diretórios, terá que incluí-los no classpath pela maneira tradicional:
java -cp lib/\\*:classes codare.MinhaClasse
Note também:
- A barra invertida (\) é apenas um caractere de escape para que o shell não faça a expansão do asterisco (*).
- A ordem dos arquivos .jar no classpath não é garantida.
- Este recurso, apesar de suportado pelo JDK 6 da Sun, não faz parte da especificação da JVM, portanto, você pode não encontrá-lo em outras implementações.
- Os curingas são expandidos na inicialização da VM. Assim, não adianta colocar novos arquivos .jar seguindo o padrão sem reiniciar a VM.
Porém, atenção: na versão atual, os caracteres curingas da JVM não são tão espertos como os do shell. A chamada a seguir, por exemplo, não funcionaria:
$ java -cp lib/\\*.jar codare.MinhaClasse Exception in thread "main" java.lang.NoClassDefFoundError: codare.MinhaClasse
Último detalhe: estes exemplos consideram um shell tipo UNIX. Em ambientes Windows, confesso, não consegui achar um comportamento previsível.
Palavras-chave: imagem, imagens, ler, salvar, carregar, editar, JPG, JPEG, GIF, PNG, imageio
Ler e escrever arquivos de imagens em Java é tão fácil que até nem parece Java:
BufferedImage imagem = ImageIO.read(new File("mariabonita.gif"));
// fazer algo com a imagem...
ImageIO.write(imagem, "PNG", new File("mariabonita.png"));
Já que foi fácil, vamos aproveitar esta dica para aprender também como alterar os pixels da imagem. Para isso, use os métodos getRGB()/setRGB() da classe BufferedImage. O método getRGB() retorna um array de int que representa os valores RGB de cada ponto da imagem.
Se você tivesse uma matriz, você poderia referenciar os pixels como matriz[lin][col]. Mas, aqui, como na maioria dos programas de processamento de imagens, temos um array para representar a imagem. Assim, temos que usar a notação pixels[w * lin + col], onde w é a largura da imagem.
O exemplo a seguir lê uma imagem, colore-a de maneira aleatório-psicodélica e a salva em outro arquivo:
BufferedImage imagem = ImageIO.read(new File("qualquer.jpg"));
int w = imagem.getWidth();
int h = imagem.getHeight();
int[] pixels = imagem.getRGB(0, 0, w, h, null, 0, w);
Random r = new Random();
for (int col = 0; col < w; col++) {
for (int lin = 0; lin < h; lin++) {
pixels[w * lin + col] =
new Color(r.nextInt(255), col % 255, lin % 255).getRGB();
}
}
imagem.setRGB(0, 0, w, h, pixels, 0, w);
ImageIO.write(imagem, "PNG", new File("arteabstrata.png"));
Palavras-chave: Java, jps, listagem de processos, parâmetros da JVM
O JDK 6 fornece algumas ferramentas de monitoração e diagnóstico de problemas que podem ser úteis no dia-a-dia de um ambiente de produção. A mais básica delas é o jps, que lista todos os processos Java que estão sendo executados na máquina.
Por exemplo, executando-se o comando sem parâmetros, são listados os identificadores (PIDs) e os nomes das classes que inicializaram cada processo (aquelas cujo método main foi chamado):
$ jps 12647 startup.jar 12777 Bootstrap 12972 Jps
Se você já sabe quais programas estão executando, provavelmente, só esta informação já lhe será suficiente. Mas, se quer mais detalhes, pode tentar as opções -l e -v, que mostram o nome completo da classe principal e os parâmetros da JVM:
$ jps -lv 12647 /opt/eclipse/./startup.jar -Xms40m -Xmx256m 12777 org.apache.catalina.startup.Bootstrap -Djava.util.lo... 13012 sun.tools.jps.Jps -Dapplication.home=/usr/lib/jvm/ja...
Nestes exemplos, os processos Java da máquina eram Eclipse (pid 12647), Tomcat (12777) e o próprio jps (12972 e 13012). Observe que a listagem de parâmetros foi abreviada.
Palavras-chave: Java 5, varargs, argumentos variáveis
A versão 5 da plataforma Java trouxe um recurso muito comum em diversas outras linguagens: número variável de argumentos na chamada de métodos.
A sintaxe é simples e intuitiva:
public class Varargs {
public static void main(String[] args) {
imprimirTipos("123", 123, 123F, 123L);
}
public static void imprimirTipos(Object... params) {
for (Object objeto : params) {
System.out.println(objeto.getClass().getName());
}
System.out.println("Número de parâmetros: "
+ params.length);
}
}
No corpo do método, o parâmetro de tamanho variável é visto como um array. Assim, nosso breve exemplo produzirá:
java.lang.String java.lang.Integer java.lang.Float java.lang.Long Número de parâmetros: 4
Palavras-chave: Java, hash, MD5, SHA-1, SHA-256, MessageDigest
A maneira mais comum de se gerar um código hash em Java é utilizando a classe java.security.MessageDigest. O método a seguir gera o hash MD5 de uma string:
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(frase.getBytes());
byte[] hashMd5 = md.digest();
O hash é retornado como um array de bytes, que pode ser impresso utilizando o método citado em um post anterior:
private static String stringHexa(byte[] bytes) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
int parteAlta = ((bytes[i] >> 4) & 0xf) << 4;
int parteBaixa = bytes[i] & 0xf;
if (parteAlta == 0) s.append('0');
s.append(Integer.toHexString(parteAlta | parteBaixa));
}
return s.toString();
}
Além do MD5, podemos também usar outros algoritmos de hash normalmente presentes nas distribuições da JVM. O exemplo a seguir gera os códigos MD5, SHA-1 e SHA-256 da mesma mensagem:
public static byte[] gerarHash(String frase, String algoritmo) {
try {
MessageDigest md = MessageDigest.getInstance(algoritmo);
md.update(frase.getBytes());
return md.digest();
} catch (NoSuchAlgorithmException e) {
return null;
}
}
public static void main(String[] args) {
String frase = "Quero gerar códigos hash desta mensagem.";
System.out.println(stringHexa(gerarHash(frase, "MD5")));
System.out.println(stringHexa(gerarHash(frase, "SHA-1")));
System.out.println(stringHexa(gerarHash(frase, "SHA-256")));
}
A saída deste programa será:
51408e7592917e916b55f8021a0f7ad3 5907736c4de6fdd70fcdc7517c3aebd257a1addc ed40c896d779f946956483d45e9efe08aac6373935d354c2be2136bf05bca42c
Palavras-chave: Java, Java 5, coleções, iteração, genéricos
A linguagem Java já tem mais de uma década de idade e naturalmente evoluiu neste período. Verifique se o jeito que você usa coleções está atualizado.
Antes do Java 1.2, a classe para representar arrays dinâmicos era a java.util.Vector:
Vector ps = new Vector();
ps.addElement(new Pessoa("Maria 1.1"));
for (Enumeration e = ps.elements(); e.hasMoreElements();)
System.out.println(((Pessoa) e.nextElement()).getNome());
O Java 1.2 trouxe as classes ArrayList, LinkedList, HashMap, HashSet, que, em conjunto com outras, formaram o Collections Framework, uma biblioteca padronizada e bastante completa para lidar com coleções de valores. Algumas das novidades:
-
Os nomes de métodos ficaram mais curtos.
-
A hierarquia de classes e interfaces está mais bem formada e útil. Por exemplo, LinkedList e ArrayList podem ser generalizadas como List.
-
Foram feitas algumas otimizações de performance. Por exemplo, ArrayList não tem todos os métodos synchronized, como Vector.
Exemplificando:
List ps = new ArrayList();
ps.add(new Pessoa("Maria 1.2"));
for (Iterator i = ps.iterator(); i.hasNext();)
System.out.println(((Pessoa) i.next()).getNome());
No Java 1.5 (ou Java 5), além das evoluções normais da API, houve também algumas mudanças na própria linguagem:
List<Pessoa> ps = new ArrayList<Pessoa>();
ps.add(new Pessoa("Maria 1.5"));
for (Pessoa p : ps)
System.out.println(p.getNome());
Como se vê, as coleções podem agora usar tipos genéricos e há uma nova estrutura de for, chamada for-each.
Portanto, se de vez em quando você ainda se encontra usando Vector, procure atualizar-se para poder evoluir junto com a linguagem daqui para frente.
Palavras-chave: java, permgen, falta de memória, geração permanente
A máquina virtual Java tem uma área de memória limitada chamada Geração Permanente (Perm Generation), onde são armazenados objetos cuja desalocação é rara (ex.: código de classes, objetos de reflexão, pool de strings etc). Dado que esta área de memória é limitada, seu uso intenso pode gerar erros com a seguinte mensagem:
java.lang.OutOfMemoryError: PermGen space
Como a mensagem diz, isto acontece porque não há mais espaço na memória reservada para a Geração Permanente. Para que este problema não aconteça mais, use a opção -XX:MaxPermSize na inicialização da JVM. Isso aumentará o tamanho máximo da memória de Geração Permanente, como no exemplo a seguir:
java -XX:MaxPermSize=128m pacote.MinhaClasse
Neste caso, configuramos o tamanho máximo da memória de geração permanente para 128 megabytes, sendo que o tamanho padrão é 64 megabytes.
Uma demonstração pode ser feita com o código a seguir:
public class TestePerm {
public static void main(String[] args) {
int numeroDeAlocacoes = 0;
try {
System.out.println("Alocando...");
ArrayList list = new ArrayList();
String grandeString = new
BigDecimal(1000).pow(1000).toString();
for (int i = 0; i < 1000000; i++)
list.add((grandeString + numeroDeAlocacoes++).intern());
System.out.println("Fim.");
} catch (OutOfMemoryError e) {
System.out.println(e);
System.out.println("Alocacoes antes do erro: " + numeroDeAlocacoes);
}
}
}
Este programa usa o método intern() da classe String para alocar strings no pool da JVM e fazer estourar a memória de geração permanente.
A seguir, vemos dois exemplos de execuções do programa TestePerm:
$ java codare.TestePerm Alocando... java.lang.OutOfMemoryError: PermGen space Alocacoes antes do erro: 11087 $ java -XX:MaxPermSize=128m codare.TestePerm Alocando... java.lang.OutOfMemoryError: PermGen space Alocacoes antes do erro: 22183
Na primeira execução, com o MaxPermSize configurado para 64 megabytes (tamanho padrão), conseguimos colocar apenas 11087 strings no pool antes de acabar a memória permanente. No segundo caso, com 128 megabytes de MaxPermSize, conseguimos fazer 22183 alocações.
A área de geração permanente é usada intensivamente em servidores de aplicação, pois estes executam milhares de classes e ficam meses no ar. Portanto, em um ambiente de produção, nunca se esqueça de configurar um tamanho máximo adequado para a memória permanente da JVM.
Palavras-chave: java, byte, conversão, hexadecimal
Em Java, as conversões e promoções numéricas às vezes fazem tarefas simples parecerem complicadas. A seguir, um método rápido para imprimir um array de bytes.
public class ImpressaoBytes {
public static void main(String[] args) {
System.out.println(paraStringHexa(
new byte[] { 0x0F, 0x7f, 0x3d, 0x00, -0x23 }));
}
private static String paraStringHexa(byte[] bytes) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
int parteAlta = ((bytes[i] >> 4) & 0xf) << 4;
int parteBaixa = bytes[i] & 0xf;
if (parteAlta == 0) s.append('0');
s.append(Integer.toHexString(parteAlta | parteBaixa));
}
return s.toString();
}
}
Para cada byte, o truque é simples: deslocamos para a direita, desprezamos o que não seja a quadra menos significativa (& 0xf) e deslocamos de volta para a esquerda; para a parte baixa, é só desprezar o que não for a quadra menos significativa.
Então basta usar o conversor de int para string hexa. A saída do programa será:
0f7f3d00dd
Uma provável tentativa seria substituir o laço por:
for (int i = 0; i < bytes.length; i++) {
s.append(Integer.toHexString(bytes[i]));
}
Mas isso falharia ao se tentar imprimir bytes “negativos”, como o último de nosso array de exemplo. A saída incorreta seria:
f7f3d0ffffffdd







Comentários Recentes