Mostrando postagens com marcador java. Mostrar todas as postagens
Mostrando postagens com marcador java. Mostrar todas as postagens

domingo, 15 de julho de 2012

Atenção para o Singleton - ambientes multi-thread

Ultimamente estou dando uma "relida" no livro Head First Design Patterns (o qual eu recomendo para programadores Java pela didática e exemplos de código).
Passando pelo capítulo que fala sobre o design do Singleton, acabei criando uns testes legais no meu PC que mostram como é possível cair em armadilhas achando que está criando uma classe corretamente.

 O design do Singleton é muito simples e acredito que isso motiva muitos programadores a não ir mais a fundo e acabam achando que são mestres em Design Patterns (e vendem isso como facinho - isso que é desmerecer o próprio trabalho).

Bom, a definição formal do Singleton é a seguinte (conforme a definição do GoF):
"O Singleton garante que uma classe tenha apenas uma instância e fornece um ponto global para acessar esta instância".

O problema em questão é o seguinte. Ao usar um singleton é possível ter a ilusão de que sempre haverá uma única instância de uma classe por toda eternidade enquanto o sistema estiver executando.

Geralmente, a definição geral para uma classe Singleton é a seguinte:

public class Singleton {
  private static Singleton instance;
  
  private Singleton() {    
  }
  
  public static Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}
Escrever uma classe da forma acima não garante que teremos uma única instância. Pensando no todo, devemos considerar esta classe sendo utilizada em um ambiente multi-thread.

A maioria dos projetos em que se trabalha com Java, são projetos para WEB, e como todos sabemos, para cada requisição realizada no servidor, uma nova thread é criada. Portanto, com esta classe Singleton que escrevi acima, não podemos afirmar que existirá apenas uma instância. Pode haver uma instância para cada thread.

Para simular esta situação, criei uma classe muito parecida com a do livro do HF (Head First) e criei outra classe que cria threads para utilização da primeira. Abaixo segue o código da primeira classe (O Singleton que queremos utilizar no projeto).

ChocolateBoiler.java

package br.com.testes.patterns.singleton;

public class ChocolateBoiler {
 private boolean empty;
 private boolean boiled;
 private static ChocolateBoiler instance;
 
 private ChocolateBoiler() {
  empty = true;
  boiled = false;
  System.out.println("criando instancia");
 }
 
 public static ChocolateBoiler getInstance() {
  if (instance == null) {
   instance = new ChocolateBoiler();
  }
  return instance;
 }
 
 public synchronized void fill() {
  if (isEmpty()) {
   System.out.println("filling the choco tank");
   empty = false;
   boiled = false;
  }
 }
 
 public synchronized void drain() {
  if (!isEmpty() && isBoiled()) {
   System.out.println("draining the chocolate");
   empty = true;
  }
 }
 
 public synchronized  void boil() {
  if (!isEmpty() && !isBoiled()) {
   System.out.println("boiling the chocolate");
   boiled = true;
  }
 }

 public boolean isEmpty() {
  return empty;
 }

 public boolean isBoiled() {
  return boiled;
 }

}
Agora a classe abaixo monta o cenário multi-threaded que queremos testar.

 ChocoHolic.java

package br.com.testes.patterns.singleton;

public class ChocoHolic {
 public static void main(String[] args) {
  
  Runnable task = new Runnable() {
   @Override
   public void run() {
    ChocolateBoiler boiler = ChocolateBoiler.getInstance();
    boiler.fill();
    
    try {
     Thread.sleep((long) (Math.random() * 3 * 1000));
    } catch (InterruptedException e) { }
    
    boiler.boil();
    boiler.drain();
   }
  };
  
  new Thread(task).start();
  new Thread(task).start();
  new Thread(task).start();
  
 }
}

Ao executar a classe ChocoHolic acima, um dos resultados que tive foi o seguinte:

criando instancia
filling the choco tank
criando instancia
filling the choco tank
criando instancia
filling the choco tank
boiling the chocolate
draining the chocolate
boiling the chocolate
draining the chocolate
boiling the chocolate
draining the chocolate

Repare na saída acima, como a frase "criando instancia" foi impressa 3 vezes. Logo, podemos concluir que foram criadas 3 instâncias =/ Além disso, uma confusão geral aconteceu por ali. O tanque de chocolate foi preenchido mais de uma vez (derramando tudo pela sala...). A imagem abaixo mostra as threads que temos em execução ao realizar um debug no eclipse.


Agora imagine que cada thread seja trocada a tempo de cada uma alcançar a instrução que cria a instância de ChocolateBoiler como mostrado no screenshot do debug abaixo:


O resultado é o que tivemos na saída mostrada como exemplo após executar o código.

Mas como corrigir isso?

Uma das formas de corrigir este problema, é fazer com que o método estático que retorna a instância da classe, seja sincronizado. Desta forma, apenas uma thread por vez poderá solicitar a criação do objeto.
Agora, temos o novo código da classe ChocolateBoiler sincronizando o método getInstance().

package br.com.testes.patterns.singleton;

public class ChocolateBoiler {
 private boolean empty;
 private boolean boiled;
 private static ChocolateBoiler instance;
 
 private ChocolateBoiler() {
  empty = true;
  boiled = false;
  System.out.println("criando instancia");
 }
 
 public synchronized static ChocolateBoiler getInstance() {
  if (instance == null) {
   instance = new ChocolateBoiler();
  }
  return instance;
 }
 
 public synchronized void fill() {
  if (isEmpty()) {
   System.out.println("filling the choco tank");
   empty = false;
   boiled = false;
  }
 }
 
 public synchronized void drain() {
  if (!isEmpty() && isBoiled()) {
   System.out.println("draining the chocolate");
   empty = true;
  }
 }
 
 public synchronized  void boil() {
  if (!isEmpty() && !isBoiled()) {
   System.out.println("boiling the chocolate");
   boiled = true;
  }
 }

 public boolean isEmpty() {
  return empty;
 }

 public boolean isBoiled() {
  return boiled;
 }

}

Executando o código novamente, tive a seguinte saída:

criando instancia
filling the choco tank
boiling the chocolate
draining the chocolate

Agora veja como a execução foi totalmente controlada. Nada de várias instâncias, e nada de chocolate derramado... Existem outras formas de corrigir este problema sem sincronizar o método getInstance, uma vez que chamar métodos sincronizados tem um custo muito alto em uma aplicação real. Uma alternativa citada pelo livro, é a de criar a instância diretamente na declaração do atributo da classe (not-lazily created instance).

public class ChocolateBoiler {
 private boolean empty;
 private boolean boiled;
 private static ChocolateBoiler instance = new ChocolateBoiler();
 
 private ChocolateBoiler() {
  empty = true;
  boiled = false;
  System.out.println("criando instancia");
 }
 
 public static ChocolateBoiler getInstance() {
  return instance;
 }
 ...
 ...

Outra alternativa que achei interessante foi a de só sincronizar a chamada quando a instância ainda não existir usando o conceito de double-checked locking.

package br.com.testes.patterns.singleton;

public class ChocolateBoiler {
 private boolean empty;
 private boolean boiled;
 private volatile static ChocolateBoiler instance;
 
 private ChocolateBoiler() {
  empty = true;
  boiled = false;
  System.out.println("criando instancia");
 }
 
 public static ChocolateBoiler getInstance() {
  
  if (instance == null) {
   synchronized (ChocolateBoiler.class) {
    if (instance == null) {
     instance = new ChocolateBoiler();
    }
   }
  }
  
  return instance;
 }
 
  ...
  ...
  // o resto do codigo continua igual

Bom... a idéia aqui foi alertar quanto a utilização do Singleton.
Até a próxima.

cuidado com maven-archetype-webapp

Bizarro!

Em primeiro lugar, este post eu criei com o principal intuito de documenção para que eu não caia mais na mesma armadilha, e se cair, já sei onde está a solução mastigada. =P

Palavras-chave: EL, Expression-Language, Spring-MVC, projeto criado com Maven archetype maven-archetype-webapp.

Bom, iniciei um projeto utilizando Spring-MVC. Criei um projeto web com o archetype de projeto web via Maven.

Criei uma JSP conforme abaixo:

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>

This page lists all available products:<br>
<c:forEach items="${products}" var="product">
    <c:out value="${product.name}"/>
    <a href="view.html?id=<c:out value="${product.id}"/>">[View]</a> 
    <a href="edit.html?id=<c:out value="${product.id}"/>">[Edit]</a> <br>
    <hr>
</c:forEach>
<br>
<a href="edit.html">[Add]</a>
Porém, quando eu acessava a página, o resultado era:
This page lists all available products:
${product.name} [View]  [Edit] 

[Add]
Olhando a saída acima é fácil perceber que as EL (Expression Languages) não estavam sendo processadas. Para resolver isso, acabei adicionando a diretiva isELIgnored=false no topo da minha JSP:
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>

This page lists all available products:<br>
<c:forEach items="${products}" var="product">
    <c:out value="${product.name}"/>
    <a href="view.html?id=<c:out value="${product.id}"/>">[View]</a> 
    <a href="edit.html?id=<c:out value="${product.id}"/>">[Edit]</a> <br>
    <hr>
</c:forEach>
<br>
<a href="edit.html">[Add]</a>
E assim a saída foi como esperado:
This page lists all available products:
test [View]  [Edit] 
Pro Spring Appes [View]  [Edit] 
Pro Velocity [View]  [Edit] 
Pro VS.NET [View]  [Edit] 

[Add]
Mas espera aí? Eu teria que ficar adicionando esta diretiva em todas minhas JSPs? Isso é muita gambiarra! Foi então que achei o seguinte post:

JSP 2.0 and losing the c:out tags

onde uma pessoa diz o seguinte:

"What version of the web-app.xml are you using? If you are using the older 2.3 version the EL expressions are ignored by default and you'll need a jsp page directive to set isElIgnored = false."

Nem precisei abrir o web.xml p/ saber se eu estava usando uma "older 2.3 version" na declaração do meu web-app.xml. Bastou eu lembrar que estou usando o archetype do maven: maven-archetype-webapp
O archetype acima gerou um web.xml da seguinte maneira:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name> 
</web-app>

Bastou corrigir a forma de declaração do web-app para utilizar um schema atualizado e acabou a gambiarra:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
    id="WebApp_ID" version="2.5">

  <display-name>Archetype Created Web Application</display-name>
  
</web-app>
É isso aí... se alguém tiver o mesmo problema já fica mais fácil resolver.

sábado, 14 de julho de 2012

Configurando um projeto Web Maven no Eclipse com Spring-MVC

Ultimamente tenho estudado o framework Spring utilizando o livro Pro Spring 2.5 (eu sei que já existe o livro 3.0).
Estudando este framework, cheguei no capítulo sobre spring-mvc e me deparei com alguns problemas utilizando o eclipse. Tudo aconteceu porque aproveitando que estou estudando spring, tenho criado todos os exemplos utilizando o maven para gerenciar os projetinhos de teste.

Ao tentar usar o Maven no eclipse acabei tendo problema com as estruturas de diretórios, além de o projeto não ser nem reconhecido pelo WTP.

Pesquisei em alguns sites, e consultei um amigo também. (Que me chama de teimoso por ser detalhista). Alguns diziam que bastava digitar o seguinte comando que o projeto criado com o maven seria reconhecido pelo WTP:
mvn -Dwtpversion=2.0 compile eclipse:eclipse

Em outros sites que pesquisei, haviam soluções muito complicadas também. Outra solução sugerida, foi iniciar criando um projeto Dynamic Web Project que também funciona, porém não terá a estrutura padrão utilizada pelas convenções do Maven.

Todas opiniões e links que li foram importantes pois ajudaram nos insights de como criar o projeto da forma como eu realmente queria: um projeto maven que mantenha todas as convenções do maven de diretórios e que funcione dentro do eclipse ao utilizar o plugin WTP.
É capaz que existam pessoas "teimosas" assim como eu, e que talvez este post possa ajudar.
Primeiro passo: criação do projeto maven
1 - Para criar o projeto maven basta clicar com o botão direito na view Project Explorer e seguir a sequencia: New / Other / Maven project

Figura 1

2 - Manter os dados padrão nesta janela (eu selecionei minha working-set personalizada web onde trabalho com os projetos web).
Figura 2

3 - Para um projeto web, selecionei All Catalogs (na opção Catalog), e procurei pelo archetype: maven-archetype-webapp. Com este archetype, já temos todas as convenções do maven para trabalhar com um projeto web.
Figura 3

4 - Configurar os dados do projeto.
Group Id: br.com.wtek
Artifact Id: finank
Version: 0.0.1-SNAPSHOT
Package: br.com.wtek.finank

Figura 4

5 - finish.
Podemos executar a aplicação de várias formas. Duas formas possíveis são:
  • Gerar um war do projeto com o Maven (gerar um pacote)
  • Adicionar o projeto no Tomcat utilizando o Eclipse.

A primeira opção é mais interessante quando vamos fazer o deploy do projeto para um servidor em produção, mas em tempo de desenvolvimento é muito provável que iremos precisar das ferramentas de Debug disponibilizadas pelo Eclipse WTP.

Gerando o war do projeto utilizando o maven através de linha de comando Para isso é necessário ir até o diretório do seu projeto via terminal e digitar a seguinte linha de comando:
astronauta@skynet:~/workspace/finank$ mvn package
E se tudo der certo, você terá como parte da resposta, a seguinte saída:
....
[INFO] Building war: /home/astronauta/workspace/finank/target/finank.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.800s
[INFO] Finished at: Sat Jul 14 21:11:55 BRT 2012
[INFO] Final Memory: 5M/100M
[INFO] ------------------------------------------------------------------------

Como podemos ver, o war foi gerado em /home/astronauta/workspace/finank/target/finank.war e o mesmo pode ser copiado para o diretório webapps do seu Tomcat.
astronauta@skynet$ cp /home/astronauta/workspace/finank/target/finank.war $CATALINA_HOME/webapps/
astronauta@skynet$ cd $CATALINA_HOME
astronauta@skynet$ bin/startup.sh

Saída:
Using CATALINA_BASE:   /home/astronauta/Develop/java/apache-tomcat-7.0.27
Using CATALINA_HOME:   /home/astronauta/Develop/java/apache-tomcat-7.0.27
Using CATALINA_TMPDIR: /home/astronauta/Develop/java/apache-tomcat-7.0.27/temp
Using JRE_HOME:        /home/astronauta/Develop/java/jdk1.6.0_26
Using CLASSPATH:       /home/astronauta/Develop/java/apache-tomcat-7.0.27/bin/bootstrap.jar:/home/astronauta/Develop/java/apache-tomcat-7.0.27/bin/tomcat-juli.jar
Abra o browser e digite http://localhost:8080/finank e deverá ter a saída: Hello World

Gerando o war do projeto utilizando o maven através do eclipse
Para aqueles que não gostam de linha de comando (o que é muito feio para um developer), é possível utilizar o próprio plugin do eclipse para gerar o war. Basta pressionar botão direito do mouse no projeto e selecionar Run as Maven Build... conforme a figura 5. A saída deverá ser a mesma da linha de comando. Obs.: repare que foi digitado package no campo "Goal".

Figura 5

Executando o projeto através do eclipse utilizando o plugin WTP
Conforme dito anteriormente, gerar o war apenas, é mais interessante para realização do deploy do projeto para o servidor. Como estamos em tempo de desenvolvimento, o ideal é trabalhar da melhor maneira com nossa IDE que irá oferecer boas opções de Debug. Mas......... Se tentarmos adicionar o projeto no Tomcat, podemos perceber que não é possível conforme mostrado na figura 6.

Figura 6

Porque o projeto não aparece na lista de projetos web? Para poder adicionar um projeto web no Tomcat utilizando as ferramentas do WTP, é necessário adicionar o "facet" Dynamic Web Project no projeto conforme os passos abaixo:
  • clicar com direito no projeto e selecionar properties ou utilizar o atalho Alt + Enter com o projeto selecionado.
  • selecionar o item Java Compiler e então selecionar a JDK compliance level de acordo com o que for adequado para seu projeto. No meu caso utilizo a JDK 1.6 (ver figura 7).

Figura 7

  • agora selecione Project Facets conforme mostrado na figura 8. Selecione o link: Convert to faceted form...

Figura 8

  • selecione Dynamic Web Module e a versão adequada. (Selecionei a versão 2.5).
  • aplicar as modificações

Agora já é possível adicionar o projeto no eclipse clicando no server + add and remove.
Tente iniciar o servidor e então tente abrir a aplicação no browser...

HTTP Status 404 - /finank/

Agora lascou! Esse é o momento de maior marotagem aqui.

A grande Marotagem
Para resolver isso, é necessário abrir o arquivo org.eclipse.wst.common.component localizado no diretório .settings do projeto. A maneira mais fácil de visualizar este arquivo é utilizar a View Navigator do eclipse.
substitua source-path="/WebContent" por source-path="/src/main/webapp", salve o arquivo e botão direito no projeto: Maven, Update Project Configurations.
Agora tente iniciar o servidor novamente e tente acessar a aplicação.
Pronto, a aplicação passou a funcionar normalmente.
Remova o diretório WebContent e agora você possui um projeto que segue todas as convenções do Maven e é reconhecido perfeitamente pelo WTP.

* caso o projeto fique com algum erro, pode ser devido a JDK Compliance que altera quando eu rodo Update Project Configurations. Basta alterar nas propriedades do projeto.
Agora para ficar mais bonito, vamos criar um diretório para os fontes do projeto web, afinal não iremos criar tudo em jsp.
Crie um diretório java dentro de src/main e defina-o como Source Folder em propriedades do projeto.
Com este projeto maroto em mãos, vou aproveitar a utilizaçao do maven e preparar um projeto Hello World com Spring-MVC.
Adicionando o Spring-MVC
Adicione as seguintes dependências no arquivo pom.xml
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-beans</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context-support</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-expression</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-web</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>servlet-api</artifactId>
     <version>2.5</version>
    </dependency>
    <dependency>
     <groupId>javax.servlet.jsp</groupId>
     <artifactId>jsp-api</artifactId>
     <version>2.2.1-b03</version>
    </dependency>
  </dependencies>

Crie um controller conforme a classe abaixo:
package br.com.finank.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class IndexController implements Controller {

 @Override
 public ModelAndView handleRequest(HttpServletRequest request,
   HttpServletResponse response) throws Exception {
  response.getWriter().write("Hello World");
  return null;
 }

}

Configure o servlet DispatcherServlet que intercepta as requisições e descobre qual controller chamar. Esta configuração é realizada no web.xml (Básico de web hein =P ).
  <servlet>
   <servlet-name>finank</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    
  </servlet>
  <servlet-mapping>
   <servlet-name>finank</servlet-name>
   <url-pattern>*.html</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
   <servlet-name>finank</servlet-name>
   <url-pattern>*.tile</url-pattern>
  </servlet-mapping>
Agora crie o xml src/main/webapp/WEB-INF/finank-servlet.xml e adicione o conteúdo abaixo:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
 http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="/index.html"
          class="br.com.finank.controller.IndexController">
    </bean>
</beans>
Obs.: o nome do arquivo finank-servlet.xml, segue a convenção de que o nome antes de -servlet.xml deve ser o nome dado ao DispatcherServlet que foi configurado láaa no web.xml.
Agora, reinicie o servidor e tente acessar a URL: http://localhost:8080/finank/index.html
Oops! Houve um problema e a root cause é:
java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
 org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1711)
 org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1556)
 org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
 org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
 org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
 org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)
 org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
 org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:307)
 java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
 java.lang.Thread.run(Thread.java:662)

Quando o eclipse fez o build do projeto, ele não considerou as dependências que adicionamos no pom.xml.
Então... marotagem final (Essa foi a dica do brother que me chama de teimoso):
Vá em Propriedades do projeto, selecione Deployment Assembly e clique em Add... Selecione Java Build Path Entries e pressione Next. Selecione Maven Dependencies e finish him.

Figura 9

Restart no projeto e tente acessar a url novamente.
Teoricamente, tudo tem que funcionar, pois conforme uma das frases mais faladas no mundo da programação:
NA MINHA MÁQUINA FUNCIONA. =P
Até a próxima...

Referências
Livros
- Better Builds With Maven - Vincent Massol & Jason Van Zyl
- Pro Spring 2.5 - Jan Machacek, Aleksa Vukotic, Anirvan Chakraborty and Jessica Ditt

Links
- http://weblogs.java.net/blog/felipegaucho/archive/2009/12/28/webapps-eclipse-and-maven-maven-war-plugin
- http://www.devx.com/Java/Article/36785/0/page/2
- http://maven.apache.org/plugins/maven-eclipse-plugin/wtp.html

terça-feira, 1 de maio de 2012

Criando uma calculadora usando BroadcastReceiver

Hoje, estudei um pouco sobre BroadcastReceiver com auxilio do livro "Google Android de R. Lecheta". Para reforçar aproveitei para usar alguns outros conceitos já estudados nesta pequena aplicação de teste que será explicada neste post.

A aplicação desenvolvida é algo muito simples e no mundo real ninguém criaria uma calculadora como esta. Lembre-se, o aplicativo foi criado apenas para estudar alguns conceitos (Principalmente o BroadcastReceiver).

Mas de que se trata o BroadcastReceiver?
Assim como as Activities podem ser chamadas por outras Activities através da utilização de Intents, Receivers também podem ser "chamados".

A principal diferença entre uma Activity e um Receiver é que o Receiver é executado em background e geralmente não deve realizar interação com o usuário (para evitar interrompe-lo). Além disso, um receiver possui um tempo máximo para executar alguma tarefa determinado em 10 segundos. Caso sua tarefa ultrapasse este tempo o Receiver é interrompido pelo sistema operacional.

Para as Activities que disponibilizamos, geralmente utilizamos um IntentFilter que diz o nome da Action e Category que permitem ao android encontrar nossa aplicação.

O exemplo clássico de um IntentFilter, é o que já é declarado quando criamos uma aplicação de HelloWorld no eclipse:
  <activity
      android:name=".LivroAndroidCap7Activity"
      android:label="@string/app_name" >
      <intent-filter>
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
  </activity>
Para um Receiver responder à uma Intent, o mesmo também precisa ser declarado com um IntentFilter. Um Receiver pode atender a várias Actions nativas como BOOT, recebimento de SMS, eventos relacionados à bateria além de poder atender a Actions customizadas (strings definidas pelo próprio desenvolvedor).
Criando a calculadora
Bom, então vamos logo para a criação da calculadora. A idéia aqui é criar um aplicativo que possui uma tela inicial que mostra apenas um botão com o rótulo Calcular conforme mostrado na figura abaixo:
Ao pressionar este botão, o usuário receberá um formulário com 3 campos dentro de um AlertDialog. O formulário poderia ter sido criado na mesma tela que o botão calcular, mas eu quis complicar um pouco mais só para brincar um pouco mais com outras funcionalidades do android (neste caso usar um AlertDialog).

Caso o usuário selecione o botão Calcular neste formulário, os dados para o cálculo são enviados para o sistema operacional através de uma Intent definida com o seguinte intent filter:
  <intent-filter>
      <action android:name="CALCULO" />
      <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
Considerando este IntentFilter, outros aplicativos podem ser criados para enviar um broadcast com dados para CALCULO. Outros Receivers também podem ser criados para receber dados para calcular e efetuar estes cálculos de forma diferente (por exemplo algum tipo de cálculo alienígena).
A primeira coisa que criei neste projeto foram os layouts definidos no diretório res/layout do projeto.
main.xml (layout que possui apenas o botão calcular - tela inicial)
  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:orientation="vertical" >

      <TextView
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:text="@string/calculadora" />
 
   <Button 
       android:id="@+id/btCalcular" 
       android:layout_width="wrap_content" android:layout_height="wrap_content" 
       android:text="@string/calcular" />
 
  </LinearLayout>
layout_form.xml (layout que possui os campos do formulário - usado no AlertDialog)
  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical" >
      
      <TextView 
          android:layout_width="fill_parent" android:layout_height="wrap_content"
          android:text="@string/primeiro_numero"
           />
      
   <EditText 
       android:id="@+id/numero1"
       android:inputType="number"
       android:layout_width="fill_parent" android:layout_height="wrap_content" 
       />
 
      <TextView 
          android:layout_width="fill_parent" android:layout_height="wrap_content"
          android:text="@string/segundo_numero"
           />
      
   <EditText 
       android:id="@+id/numero2"
       android:inputType="number"
       android:layout_width="fill_parent" android:layout_height="wrap_content" 
       />

   <TextView 
          android:layout_width="fill_parent" android:layout_height="wrap_content"
          android:text="@string/operacao"
           />
 
   <Spinner 
       android:id="@+id/operacao" 
       android:layout_width="fill_parent" android:layout_height="wrap_content"/>    

  </LinearLayout>
Após definir os layouts, criei as classes para a Activity e uma para o Receiver. CalculoReceiver.java
  package br.com.livro.cap9;

  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.os.Bundle;
  import android.util.Log;

  public class CalculoReceiver extends BroadcastReceiver {

   @Override
   public void onReceive(Context ctx, Intent intent) {
  
    // recupera os dados para calculo que qualquer Intent  
    // recebida para este fim precisa enviar
    Bundle extras = intent.getExtras();
    if (extras != null) {
     String n1 = extras.getString("numero1");
     String n2 = extras.getString("numero2");
     String op = extras.getString("operacao");
   
     // recebe os valores
     // faz uma pequena validação
     // e então decide qual operaçao executar guardando o valor em resultado
     if (!isEmpty(n1) && !isEmpty(n2) && !isEmpty(op)) {
      int in1 = Integer.parseInt(n1);
      int in2 = Integer.parseInt(n2);
      int iop = Integer.parseInt(op);
      int resultado = 0;
    
      switch (iop) {
       case 0: // soma
        resultado = in1 + in2;
        break;
       case 1: // subtracao
        resultado = in1 - in2;
        break;
       case 2: // multiplicacao
        resultado = in1 * in2;
        break;
       case 3: // divisao
        resultado = in1 / in2;
        break;
      }
    
      // após efetuar o cálculo apenas gera um log 
      // que poderá ser verificado pelo LogCat no eclipse.
      Log.i("CALCULO", "resultado do calculo: " + resultado);
     }
    }
   }

   private boolean isEmpty(String v) {
    return (v == null || v.length() == 0);
   }
  }
CalculadoraActivity.java
  package br.com.livro.cap9;

  import android.app.Activity;
  import android.app.AlertDialog;
  import android.content.Context;
  import android.content.DialogInterface;
  import android.content.Intent;
  import android.os.Bundle;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.View.OnClickListener;
  import android.widget.AdapterView;
  import android.widget.AdapterView.OnItemSelectedListener;
  import android.widget.ArrayAdapter;
  import android.widget.Button;
  import android.widget.EditText;
  import android.widget.Spinner;
  import android.widget.Toast;

  public class CalculadoraActivity extends Activity implements OnItemSelectedListener, OnClickListener {

   protected static final int SOMA = 0;
   private int codigoOperacao = SOMA;
 
   // lista de operações disponíveis
   private String[] operacoes = {
     "Soma", "Subtração", "Multiplicação", "Divisão"
   };
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
  
    // adicionando o comportamento para o botão calcular
    Button btCalcular = (Button) findViewById(R.id.btCalcular);
    btCalcular.setOnClickListener(this);
  
   }

   /**
    * Ao selecionar um item no Spinner (famoso combobox em outras plataformas),
    * a posição da operação selecionada é guardada em codigoOperacao que podera ser
    * encaminhada através da Intent posteriormente quando o usuário solicitar o calculo.
    */
   public void onItemSelected(AdapterView adapterView, View view,
    int position, long id) {
    codigoOperacao = position;
   }

   @Override
   public void onNothingSelected(AdapterView arg0) {
    codigoOperacao = SOMA; // operação padrao é a soma
   }

   @Override
   public void onClick(View v) {
  
    // criando a janela de dialogo
    AlertDialog.Builder alert = new AlertDialog.Builder(this);
  
    // Uma das coisas interessantes em utilizar este AlertDialog no exemplo, foi 
    // utilizar também o LayoutInflater conforme mostrado abaixo.
    // O LayoutInflater cria uma instancia de View baseando-se
    // no layout informado em inflater.inflate
    LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    final View viewForm = inflater.inflate(R.layout.layout_form, null);
  
    // Desta forma é possivel definir o conteúdo do AlertDialog
    // como uma View ao invés de uma mensagem texto.
    alert.setView(viewForm);
  
    // Criamos entao os botoes para uma açao positiva e uma açao negativa  
    // utilizando os devidos Listeners para click nos botoes.
    // Repare que a classe OnClickListener está dentro de DialogInterface 
    // e não dentro de View como é utilizado para Buttons.
    alert.setPositiveButton("Calcular", new DialogInterface.OnClickListener() {
   
     public void onClick(DialogInterface dialog, int which) {
      // aqui dentro recuperamos os dados registrados na view do formulário
      // por isso, para recuperar cada EditText, foi utilizado o viewForm.findViewById.
      // Pois apenas esta view possui os componentes registrados.
      // Tente utilizar findViewbyId apenas para ver o que acontece =P
      EditText txtNumero1 = (EditText) viewForm.findViewById(R.id.numero1);
      EditText txtNumero2 = (EditText) viewForm.findViewById(R.id.numero2);
    
      Bundle extras = new Bundle();
      extras.putString("numero1", txtNumero1.getText().toString());
      extras.putString("numero2", txtNumero2.getText().toString());
      extras.putString("operacao", Integer.toString(codigoOperacao));
    
      // Este é o ponto principal para o estudo do BroadcastReceiver.
      // É aqui que criamos uma intent para calculo que será 
      // recebida pelo nosso Receiver CalculoReceiver.
      Intent itCalculo = new Intent();
      itCalculo.setAction("CALCULO");
      itCalculo.putExtras(extras);
      sendBroadcast(itCalculo);
    
      Toast.makeText(CalculadoraActivity.this, "calculo enviado para broadcast", Toast.LENGTH_SHORT).show();
     }
    });
  
    alert.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() {

     public void onClick(DialogInterface dialog, int which) {
      Toast.makeText(CalculadoraActivity.this, "calculo cancelado", Toast.LENGTH_SHORT).show();
     }
    });
  
    // carregando a lista de operações utilizando um ArrayAdapter
    // Existe muito conteúdo na net explicando o conceito de adapter para utilização em listas.
    ArrayAdapter adapter = new ArrayAdapter(
     this, android.R.layout.simple_spinner_item, operacoes);
    Spinner spnOperacoes = (Spinner) viewForm.findViewById(R.id.operacao);
    spnOperacoes.setAdapter(adapter);
  
    // guarda a posicao da operacao pois deve ser enviado para o broadcast receiver
    // o código da operação a ser realizada e não a descrição
    spnOperacoes.setOnItemSelectedListener(this);

    // exibindo o alert com o formulario para o usuário.
    alert.show();
   }
 
  }
Para entender o código, leia os comentários colocados no próprio código. Não vou detalhar o código aqui para não ficar repetitivo. Agora que criamos tudo que é necessário, é preciso registrar a Activity e o Receiver dentro no arquivo AndroidManifest.xml
  <?xml version="1.0" encoding="utf-8"?>
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="br.com.livro.cap9"
      android:versionCode="1"
      android:versionName="1.0" >

      <uses-sdk android:minSdkVersion="8" />

      <application
          android:icon="@drawable/ic_launcher"
          android:label="@string/app_name" >
          <activity android:name="CalculadoraActivity" android:label="calculadora">
              <intent-filter>
                  <action android:name="android.intent.action.MAIN"/>
                  <category android:name="android.intent.category.LAUNCHER"/>
              </intent-filter>
          </activity>
          
          <receiver android:name="CalculoReceiver" android:label="calculo receiver">
              <intent-filter>
                  <action android:name="CALCULO" />
                  <category android:name="android.intent.category.DEFAULT" />
              </intent-filter>
          </receiver>
      </application>

  </manifest>
Ao executar a aplicação, teremos a seguinte saída no emulador:

E teremos a seguinte saída no LogCat:


Bom, é isso aí... o post acaba por aqui e espero que alguém também possa aprender algo com este exemplo simples.
Até a próxima.

sábado, 14 de abril de 2012

Adicionando conteúdo em um ListView dinamicamente

Atualmente tenho estudado um pouco de Android nas horas vagas, então vou colocar aqui no blog, alguns exemplos criados enquanto estudo.
Neste post, será mostrado um exemplo de como carregar um ListView de forma dinâmica.

Para a criação deste exemplo precisamos criar apenas um projeto Android simples.
Criei meu projeto no eclipse gerando o layout main.xml e para minha Activity principal dei o nome de AcademiaActivity (pois estou criando um projeto simples para academias).

O layout do aplicativo pode ser visualizado na imagem abaixo:


Abaixo segue o código do layout main.xml


<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1,2">

<TableRow
android:id="@+id/tableRow1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >

<TextView
android:id="@+id/labelSerie"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/serie" />

<EditText
android:id="@+id/txtSerie"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:ems="5" >
<requestFocus />
</EditText>

<Button
android:id="@+id/btAdicionar"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:text="@string/adicionar" />

</TableRow>

<ListView
android:id="@+id/listaSeries"
android:smoothScrollbar="true"
android:background="#909BD3">
</ListView>

</TableLayout>


E abaixo segue o código da Atividade: AcademiaActivity.java


package br.com.academia;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class AcademiaActivity extends Activity {
private ArrayList series = new ArrayList();

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// define o layout como o criado em main.xml
setContentView(R.layout.main);

// recuperando a listview declarada em main.xml para poder definir o adapter
ListView listaSeries = (ListView) findViewById(R.id.listaSeries);

// definindo a implementação ArrayAdapter como ListAdapter da ListView
// series é a lista de séries de treino definida como um ArrayList através de uma variável de instância
final ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, series);
listaSeries.setAdapter(adapter);

// adicionando comportamento para o evento click do botão adicionar
Button btAdicionar = (Button) findViewById(R.id.btAdicionar);
btAdicionar.setOnClickListener(new OnClickListener() {
public void onClick(View view) {

// recuperando o texto digitado pelo usuario
EditText txtSerie = (EditText) findViewById(R.id.txtSerie);
String serie = txtSerie.getText().toString();

// caso o texto for preenchido, adiciona na lista e atualiza o adapter
// caso contrario exibe uma mensagem solicitando ao usuário que digite uma série
if (serie.length() > 0) {
txtSerie.setText("");
txtSerie.findFocus();
series.add(serie);
adapter.notifyDataSetChanged();
} else {
Toast.makeText(AcademiaActivity.this, "Digite o nome da série", Toast.LENGTH_SHORT).show();
}
}
});

}
}


Para criar uma Atividade que exibe apenas uma lista, a maioria dos exemplos indica criar a Atividade como uma classe que estende ListActivity ao invés de Activity pois ListActivity já possui uma ListView definida internamente, e no método onCreate não é necessário nem atribuir a contentView.
Como neste exemplo, temos alguns componentes para interagir com o usuário além da lista, acabei seguindo a abordagem acima definindo meu próprio ListView no layout principal.

A chave aqui para entender o funcionamento do ListView está em entender o seguinte trecho de código:


// definindo a implementação ArrayAdapter como ListAdapter da ListView
// series é a lista de séries de treino definida como um ArrayList através de uma variável de instância
final ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, series);
listaSeries.setAdapter(adapter);


O conceito utilizado neste trecho de código é o conceito de Adapter. O Adapter permite fazer um bind entre os dados e a View, ou seja, o ArrayList e o ListView respectivamente.
Outra parte do código que é importante frisar aqui, é a atualização da ListView quando um item é adicionado no ArrayList. Esta atualização é realizada através do código abaixo:


adapter.notifyDataSetChanged();


Bom, este foi apenas um exemplo simples para mais uma vez documentar o que ando estudando. Talvez possa ajudar alguém.

domingo, 27 de março de 2011

Question about overloading

When studying for the OCP Java SE 6 Programmer I've get wrong in a question from ExamLab that follows:

Given the code, what will be the result of attempting to compile and run the following program?


public class Lanka {
public Lanka(Object o) {
System.out.println("Object In");
}
public Lanka(Integer o) {
System.out.println("Integer In");
}
public Lanka(Number o) {
System.out.println("Number In");
}
public static void main(String[] args) {
new Lanka(null);
}
}


A - Compilation fails
B - Runtime Exception
C - Prints "Object In"
D - Prints "Integer In"
E - Prints "Number In"
F - Prints nothing.

Well. I thought that the first method that appears in my class will be invoked so the answer will be C.
But the right answer is D.
Reading the explanation from ExamLab, I've got that the constructor/method that can take the most subtype instances, will be executed.
But if there was another constructor outside the hierarchy Object<-Number<-Integer, a compile time error will be generated definitely.

It was very tricky question for me, so I've decided to post here.
I hope I can remember it always when coding and doing my certification exam.

domingo, 16 de janeiro de 2011

Ambiguous Varargs Declaration

Preparing to my certification OCPJP 6, I realized the inquisition mock exam provided on coderanch forum. This mock exam arrises a lot of doubts that I thought I woudn't have when doing my exam.
At this post, I will describe a really tricky situation. So tricky that it lead me to wrong two questions at this mock exam.

Take a look at the code that follows:


public class Test1 {
public static void main(String[] args) {
Test1 inq = new Test1();
inq.method(1, 1, 1); // < -- this code wont compile
}
public void method(Integer ...i) {
System.out.println("eye in the sky");
}
public void method(int... i){
System.out.println("fly in the pie");
}
}


According to the inquisition explanation:
"to the compiler Integer... and int... are pretty much the same. This results in ambiguous state, which is an error. The jvm wont know which method to call if this is allowed to be compiled."

When trying to compile the code above you can get the following error:
The method method(Integer[]) is ambiguous for the type Test1

So considering the code and explanation above when trying to solve another question that presents the code below I thought that it was the same situation... But... as I will show... It isn't the same. The next code, won't compile even if we aren't trying to use some overloaded method.


public class Ambiguous {
public static void test(int[] input) {
System.out.println("int[]");
}
public static void test(int... input) {
System.out.println("int...");
}
}


At the first case, the compiler claims when trying to use a method while at the last case, the compiler claims when declaring those methods.

For the first case, I found a good explanation at JSL - Java Specification Language - Third Edition.
The summary below, is a brief description of how a method name is resolved at compile time regarding the JSL.

"Determining the method that will be invoked by a method invocation expression involves three main steps:

  • Determine the Class or Interface to Search: figure out the name of the method and which class or interface to check for definitions of methods of that name. Methods will be figured out regarding the current class invoking the method, super classes, interfaces, enclosing classes (considering inner classes case).

  • Determine Method Signature: After determining where to take the method, the second step will consider the argument expressions to locate the method that are both accessible and applicable. The most specific method will be chosen when more than one method could be used. How to know if a method is applicable? The process of determining applicability begins by determining the potentially applicable methods. The remainder of the process is split into three phases (this division into phases is to ensure compatibility with older versions of the java programming language):

  • Phase 1: Performs overload resolution without permitting boxing or unboxing or the use of variable arity method invocation. If no applicable method is found, proceeds with second phase.
    Phase 2: Performs overload resolution allowing boxing and unboxing but prevents the use of varargs.This prevents the invocation of a method defined with varargs if a fixed arguments method is found. If no applicable method is found, proceeds with third phase.
    Phase 3: Allows overloading to be combined with varargs, boxing and unboxing.

  • Determine if the chosen method is appropriate

When determining the method to be used, more than one method could be maximally specific. In this case, the method invocation can be defined as ambiguous so a compile-time error occurs."

Considering the description above, the compile time error claiming for ambiguity, was thrown because of compatibility issues.
Let's take another look into the first code:


public class Test1 {
public static void main(String[] args) {
Test1 inq = new Test1();
inq.method(1, 1, 1); // ERROR LINE
}
public void method(Integer ...i) {
System.out.println("eye in the sky");
}
public void method(int... i){
System.out.println("fly in the pie");
}
}


Considering the ERROR LINE and phase 1 (from second step of JSL definition): boxing, unboxing and varargs are not permitted. So, in this case, where two methods are potentially applicable, the values 1, 1, 1 cannot be boxed, unboxed and cannot be used to call the varargs definition method, so let's go to phase 2 where the boxing and unboxing are permitted but varargs is not permitted yet. Even though we can box and unbox those values (1,1,1) it still needs to use a vararg version of the method. So, phase 2 fails too and the compiler goes to phase 3 where varargs can be applied. Well... using the vararg we still have 2 maximally specific methods which leads to a compile-time error.

So, this is the difference between the first case and second case where a compile-time error occurs when trying to use a method or a compile-time error occurs when declaring 2 overloaded methods.

The code could compile if it was wrote as follows:


public class Ambiguous {
public static void main(String[] args) {
Ambiguous inq = new Ambiguous();
inq.test(new Integer[]{1,2,3});
}
public static void test(Integer... input) {
System.out.println("Integer...");
}
public static void test(int... input) {
System.out.println("int...");
}
}


Considering the code above, the method will be determined in phase 1 where there is no need to box, unbox or use var-args since Integer[] and Integer... is the same thing at byte-code level.

segunda-feira, 2 de novembro de 2009

Message Digest com JAVA

Acostumado com a função md5 do php que gera uma String como resultado após um digest, acabei apanhando um pouco no Java.

Baseado no post "Java md5 example with MessageDigest", adaptei uma classe para gerar md5 de uma forma um pouco parecida com a do PHP.

Abaixo segue o código fonte da classe:


import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* SimpleMessageDigest
*
* this class has static methods to ease the way to use message digest functions
* @author adolfo
*/
public final class SimpleMessageDigest {

/**
* md5 algorithm message digest method
* @param text
* @return
*/
public static final String md5 (String text) {

// starts digesting the received text
StringBuffer hexString = new StringBuffer();

try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(text.getBytes());
byte[] messageDigest = md.digest();

for (int i = 0; i < messageDigest.length; i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
} catch (NoSuchAlgorithmException e) {
// I know that java implements md5 algorithm
}

return hexString.toString();

}

/**
* sha1 algorithm message digest method
* @param text
* @return
*/
public static final String sha1 (String text) {

// starts digesting the received text
StringBuffer hexString = new StringBuffer();

try {
MessageDigest md = MessageDigest.getInstance("SHA1");
md.reset();
md.update(text.getBytes());
byte[] messageDigest = md.digest();

for (int i = 0; i < messageDigest.length; i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
} catch (NoSuchAlgorithmException e) {
// I know that java implements sha1 algorithm
}

return hexString.toString();
}

}

sexta-feira, 7 de agosto de 2009

Found class com.sun.image.codec.jpeg.JPEGImageEncoder, but interface was expected

Realizando um trabalho com iReport 0.4.1 recebi a mensagem de erro que é título deste post:
Found class com.sun.image.codec.jpeg.JPEGImageEncoder, but interface was expected.

Tudo isso porque adicionei um código de barras no meu relatório, e que apesar de compilar, não executava.
Depois de algum tempo achando que o problema era com o iReport e que poderia ser um bug do mesmo, encontrei um post na net relacionado à mensagem de erro que eu recebi. Porém tal erro não ocorria por uso do iReport!!!!

Neste post, o autor sugere 2 causas para o problema:

1 - usando OpenJDK ao invés da Sun JDK
2 - remoção do pacote com.sun.image.jpeg.codec da JDK 6



Após verificar isso, baixei a JDK 5 e utilizei com meu iReport.
Pronto! caso resolvido...


Para mais informações à respeito da retirada do pacote com.sun.image.codec.jpeg.JPEGImageEncoder, clique aqui


Referências
http://codingexplorer.wordpress.com/2009/07/12/interface-was-expected-for-jpegimageencoder/

quarta-feira, 11 de março de 2009

Java(web) + Eclipse + SVN

Configurando um projeto Java(web) + Eclipse + SVN:

Este é um case do desenvolvimento de um projeto Web com Java no Eclipse + Subversion.
A partir de um projeto "Dynamic Web Project" já criado no Eclipse, o Subversion foi definido
como o software controlador de versões para o projeto.

Estrutura do diretório do projeto
Para isso, foi criada uma cópia do projeto em outro diretório para que fosse possível criar a
estrutura para armazenamento no Subversion.
Ex.:


[root@skynet]# mkdir /develop/work
[root@skynet]# cp -r /develop/myproject /develop/work/.


Os diretórios do projeto adicionados no controle de versão foram:


.classpath
.project
.settings/
WebContent/*
build/classes
src/*


Apesar de o diretório build ser adicionado, o conteúdo do mesmo não foi adicionado, pois o SVN (Subversion)
não será utilizado para controlar binários.
Obs.: As libs foram adicionadas para poupar trabalho para quem for baixar uma cópia de trabalho do projeto, ou seja,
o mesmo não precisará baixar as bibliotecas. Apenas referenciá-las no projeto.


Configuração do SVN
Abaixo seguem os passos seguidos para a configuração do svn:

1 - criado o repositório em um diretório ( no meu caso /develop/svnroot ) e iniciado o serviço svnserve
para o repositório a ser criado. Abaixo seguem os comandos:


[root@skynet]# cd /develop
[root@skynet]# mkdir svnroot
[root@skynet]# svnserve -d -r /develop/svnroot
[root@skynet]# svnadmin create /develop/svnroot


2 - após criar o repositório, vamos adicionar o projeto no SVN através do comando svn import:


[root@skynet]# cd /develop/work
[root@skynet]# svn import myproject file:///develop/svnroot/myproject -m "Projeto adicionado"


3 - Uma vez configurado o repositório e adicionado o projeto no SVN, foi configurado o modo de autenticação no svnserve.


[root@skynet]# cd /develop/svnroot
[root@skynet]# vim conf/svnserve.conf


Abaixo um exemplo de configuração do arquivo

### This file controls the configuration of the svnserve daemon, if you
### use it to allow access to this repository. (If you only allow
### access through http: and/or file: URLs, then this file is
### irrelevant.)

### Visit http://subversion.tigris.org/ for more information.

[general]
### These options control access to the repository for unauthenticated
### and authenticated users. Valid values are "write", "read",
### and "none". The sample settings below are the defaults.
#anon-access = read
auth-access = write
### The password-db option controls the location of the password
### database file. Unless you specify a path starting with a /,
### the file's location is relative to the conf directory.
### Uncomment the line below to use the default password file.
password-db = passwd
### The authz-db option controls the location of the authorization
### rules for path-based access control. Unless you specify a path
### starting with a /, the file's location is relative to the conf
### directory. If you don't specify an authz-db, no path-based access
### control is done.
### Uncomment the line below to use the default authorization file.
#authz-db = authz
### This option specifies the authentication realm of the repository.
### If two repositories have the same authentication realm, they should
### have the same password database, and vice versa. The default realm
### is repository's uuid.
realm = myproject


Diretivas que foram descomentadas:


auth-access = write
password-db = passwd
realm = myproject


Logo em seguida foi alterado o arquivo passwd


[root@skynet]# vim conf/passwd

[users]
juca = senha1
epaminondas = 2213


Pronto! o SVN está configurado e o projeto pronto para ser compartilhado.

Baixando o projeto através do Eclipse
A versão do eclipse utilizada foi Eclipse 3.3.
Para integração com o SVN, foi necessário baixar o plugin Subclipse. Abaixo seguem os passos para a
instalação do Subclipse no Eclipse.


1 - Selecionado o menu : Help/ Software Updates/ Find and Install
2 - Selecionada a opção: Search for new features to install
3 - Next
4 - New Remote Site
5 - Adicionada a URL no campo URL http://subclipse.tigris.org/update_1.4.x
6 - Finish


Caso for utilizado proxy em sua rede, habilite a utilização do proxy em Window/ Preferences/ General/ Network Connection.
O caminho para configuração do proxy mostrado acima foi utilizado no eclipse 3.3 (Europa).

Após configurar o Subclipse reinicie o eclipse e altere a perspectiva para SVN Repository Explorer.
Então adicione um Repositorio através da opção New/ Repository Location. (clicar com o direito na aba SVN Repositories).
Lá basta selecionar o projeto e clicar em checkout.

Abaixo segue uma lista de comandos básicos que sempre esqueço:

# criando um repositorio
---------------------------
svnadmin create /develop/svnroot

# subindo o servico do subversion
----------------------------------
svnserve -d -r /develop/svnroot

# checkout do projeto
--------------------------------
svn checkout svn://localhost/project

# importando o projeto para o svn
------------------------------------
svn import aplicacao_java file:///usr/local/repositories/aplicacao_java -m "initial projetc"

domingo, 21 de setembro de 2008

JAVA2D

Estudando mais um pouco sobre JAVA2D, resolvi fazer um programa bem simples para testar a utilização da classe GeneralPath. Como aplicação resolvi implementar o gráfico da função Seno.

Abaixo segue o código fonte:


package com.deitel.graphics2d;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.GraphicAttribute;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;

public class Trigonometria extends JFrame
{
final int FRAME_WIDTH = 400, FRAME_HEIGHT = 300;

public Trigonometria()
{
super("Trigonometria");

setSize(FRAME_WIDTH, FRAME_HEIGHT);
setVisible(true);
}

@Override
public void paint( Graphics g )
{
Graphics2D g2d = (Graphics2D)g;
super.paint(g2d);

GeneralPath gp = new GeneralPath();
Float w = 350f, h = 200f;

Point2D.Float newZero = new Point2D.Float((FRAME_WIDTH - w)/2, (FRAME_HEIGHT - h)/2);

g2d.setColor( new Color(255,255,255) );
g2d.fill( new Rectangle2D.Float(newZero.x, newZero.y, w, h) );

// drawing x axis
g2d.setColor( new Color( 0, 0, 0 ));
g2d.draw( new Line2D.Float( newZero.x, h / 2 + newZero.y, newZero.x + w, h / 2 + newZero.y ) );

// starting to draw sin function
double y = 0.0; // used to draw
double x = 0.0; // used to calc

// initial point
//gp.moveTo(newZero.x, h / 2 + newZero.y);
double f = 100;
while (x <= (2 * Math.PI + 0.1))
{
y = Math.sin(x);

if (x == 0.0)
gp.moveTo( (x * f / 2 + newZero.x) , (y * -f / 2 + ((newZero.y + h / 2))) );
else
gp.lineTo( (x * f / 2 + newZero.x) , (y * -f / 2 + ((newZero.y + h / 2))) );

x += 0.1;
}
//gp.closePath();

g2d.setColor( new Color(0,0,255) );
g2d.draw( gp );

}

public static void main(String[] args)
{
Trigonometria app = new Trigonometria();
}
}


E abaixo a saída do programa:

sábado, 13 de setembro de 2008

String Interning and Equality

I was studying about Strings at Deitel book, and the String static method, internal, raised up my attention about the Java String pool mechanism.
When we create a literal String in Java, this string is placed at the String pool. So if we create other String with the same content, it will not be placed at the String pool because it already exists on memory. So the reference will point to the same object. In this case, these 2 references should be compared using the == operator. Comparing strings that are the same object with == will return true!

So, if we create 2 strings with different contents like follows:


String a = "foo";
String b = "bar";


Comparing both with == will return true? Of course not! But isn't because the difference in contents. If the contents are different theses strings will be differente objects in String pool.

Now, lets take a look in other example:


String a = "foo";
String b = new String("foo");


( a == b ) will return false!!!! both references have Strings with the same content, but they point to different objects.

The better way to compare Strings is to use equals method.

( a.equals(b) ) will return true, regardless if they are different or equal objects in memory.

But if you don't want to use equals, you can use

( a == b.intern() ), that will return true because 'a' points to String in the String pool, and b.intern returns the value that already exists in String pool. So you will be comparing the same object.

The following code shows an example of a function that compare strings using == regardless the strings are the same object or not, because if the content are the same, they will be placed at same object using intern method.


public class StringIntern {

public static void main(String[] args) {

String output;
String a = "teste";
String b = new String("teste");

// even comparing different objects this code will return true
if (StringIntern.comparaStrings(a, b)) {
output = "a and b are equal";
} else {
output = "a and b are NOT equal";
}

System.out.println(output);
}

private static boolean comparaStrings( String a, String b )
{
String c = a.intern();
String d = b.intern();
return c == d;
}
}

domingo, 22 de junho de 2008

PermGen Error using Eclipse JEE + TomCat6 + Jdk6

Ao começar a desenvolver sistemas para WEB com JAVA (jsp + servlet), um dos problemas que tive foi um erro gerado pela JVM quando eu executava o eclipse para programar.
Eu estava utilizando o Eclipse JEE (64 bits) + JDK6(64) + TomCat6. Após alguns minutos editando algum arquivo no eclipse ou mesmo tentando executar a aplicação, o seguinte erro era gerado e meu eclipse travava:


java.lang.OutOfMemoryError: PermGen space
Exception in thread "Timer-2" java.lang.OutOfMemoryError: PermGen space



Este erro pode ocorrer devido aos recursos que são utilizados pela aplicação, e que estouram o limite de memória definido pela JVM para armazenar recursos como classes e métodos que são carregados durante a execução de um programa em JAVA. Esta limitação está associada ao conceito de Permanent Generation da JVM. Abaixo segue a definição do Permanent Generation segundo a própria sun:

The permanent generation is special because it holds data needed by the virtual machine to describe objects that do not have an equivalence at the Java language level. For example objects describing classes and methods are stored in the permanent generation.


Porém esta limitação não é fixa e pode ser alterada durante a execução dos programas através de opções passadas para à JVM. A opção a ser alterada neste caso é a opção XX:MaxPermSize=512m.

Porém como estamos falando da execução do eclipse com os recursos necessários para uma aplicação web, para passar estas opções para o mesmo, basta editar o arquivo eclipse.ini no root do eclipse. Adicione as opções abaixo:


-Xms128m
-Xmx1024m
-XX:MaxPermSize=512m


Agora inicie o eclipse novamente e assim como no meu caso, deverá funcionar.

Mais detalhes sobre Generations e Permanent Generations podem ser encontrados no link abaixo:
http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html

Referências:
http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html

sexta-feira, 20 de junho de 2008

Criando uma conexão via jdbc com o Oracle através de tnsname definido em string

Criando uma conexão via jdbc com o Oracle através de tnsname definido em string

Diversos tutoriais mostram como conectar no Oracle através de JDBC,
criando strings de conexão, carregando drivers, mas para conectar usando
um tnsname do oracle definido em uma string não foi tão fácil para mim.

Então para que eu nunca mais tenha dificuldade com isso, abaixo segue um exemplo
de uma classe que fabrica conexões com JDBC + Oracle:

Lembre-se: é necessário ter o client do oracle instalado ou o InstantClient


package br.dbclasses;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionFactory
{
public static Connection getConnection() throws SQLException
{
Connection conn;

String tnsname = "(DESCRIPTION = ";
tnsname += "(ADDRESS = (PROTOCOL = TCP)";
tnsname += "(HOST = seuhost)(PORT=1521)) ";
tnsname += " (CONNECT_DATA = ";
tnsname += " (SERVER = DEDICATED) ";
tnsname += " (SERVICE_NAME = seuSID) ";
tnsname += " ) ";
tnsname += " ))";

try {
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@" +
tnsname,"usuario", "senha");
return conn;
} catch (SQLException e) {
System.out.println("Erro - SQLException!");
System.out.println(e.getMessage());
} catch (ClassNotFoundException e) {
System.out.println("Erro! - ClassNotFoundException!");
System.out.println(e.getMessage());
}

return null;

}
}


É isso!!!

domingo, 9 de março de 2008

JAVA 2D

Comecei a estudar um pouco de JAVA2D, para ajudar nos estudos com JAVA e computação gráfica. Estudei algumas formas básicas, e os objetos principais para criar imagens com o Java (Graphics, Graphics2D, Component, Container, Panel, JComponent e etc).
O programa que segue neste post, é apenas uma pequena desmonstração da utlização do Java2d. Para este estudo fiz apenas um relógio que conta os segundos.
Neste programa, foram empregados os conceitos de rotação e utilizadas 2 formas básicas do java2d (Line2D e Ellipse2D)


package javagraphics;

import java.applet.Applet;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.text.DecimalFormat;
import java.text.NumberFormat;

import javax.swing.Timer;

public class Relogio extends Applet {

Graphics2D g2d;
Point centro;
Point prop;
Line2D line;
int largura;
int altura;
int second;
TextField txt;
Container ct;
NumberFormat formatter = new DecimalFormat("00");

public void init ()
{
setSize(300,300);
setBackground(new Color(210,255,0));

ct = new Container();
ct.setLayout(new FlowLayout());

txt = new TextField(20);
ct.add(txt);
add(ct);
}

public void paint ( Graphics g )
{
second = 0;

g2d = (Graphics2D)g.create();
super.paint(g2d);

Dimension size = getSize();
centro = new Point(size.width / 2, size.height / 2);
prop = new Point((int)(centro.x * 0.3), (int)(centro.y * 0.3));

largura = (centro.x - prop.x)*2;
altura = (centro.y - prop.y)*2;

// fundo do relogio
g2d.setPaint(Color.lightGray);
g2d.fill( new Ellipse2D.Float(prop.x, prop.y, largura, altura) );

// ponteiro do segundo
line = new Line2D.Double(centro.x, centro.y, centro.x, prop.y / 0.7);

g2d.setPaint(Color.red);
g2d.setStroke(new BasicStroke(2.0f));
g2d.draw(line);

txt.setText("00");

int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {

g2d.rotate(Math.toRadians(6), centro.x, centro.y);

// fundo do relogio
g2d.setPaint(Color.lightGray);
g2d.fill( new Ellipse2D.Float(prop.x, prop.y, largura, altura) );

// ponteiro do segundo
g2d.setPaint(Color.red);
g2d.setStroke(new BasicStroke(2.0f));
g2d.draw(line);


second++;
txt.setText(formatter.format( (second == 60) ? 0 : second) );

second = (second == 60) ? 0 : second;
}
};
new Timer(delay, taskPerformer).start();

System.out.println(prop.y);
}

}


abaixo segue a imagem do programa em execução:

segunda-feira, 25 de fevereiro de 2008

Genéricos em JAVA - avançado

No post anterior; Genéricos em JAVA - Básico, foi mostrada a utilização de Genéricos através da definição dos tipos que as Collections conteriam. Além deste tipo de utilização de genéricos, pode-se utilizar este conceito para definir métodos genéricos!!!

Este tipo de declaração permite definir um tipo que um método pode retornar, e/ou até mesmo
os tipos de dados dos parâmetros de um método podem ser genéricos.

Vamos direto a um exemplo:

public class TesteMetodoGenerico {

public static <T extends Book> void imprime(T book) {
book.setContent("conteudo1");
book.setName("myamotto musashi");
book.setId(1);

System.out.println(book.getId() + " " + book.getName());
}
}


Exemplo de utilização:
TesteMetodoGenerico m = new TesteMetodoGenerico();
m.imprime(new SecretBook()); // secretBook é um subtipo de Book


O exemplo acima declara um método que pode receber como parâmetro um objeto do tipo Book ou qualquer subtipo do mesmo.
Este tipo de declaração é definido como limite superior, ou Upper Bounds.

Também pode-se utilizar métodos genéricos porém com limites inferiores, ou seja, Lower Bounds.

Para lower bounds, ao invés de utilizar a palavra chave extends no generics, utiliza-se super. Vamos analisar o código abaixo:

...

este artigo ainda não está completo,
aguarde o restante em breve...

Genéricos em JAVA - Básico

Estudando JDBC, acabei tendo interesse em entender o que acontece quando usamos o Class.forName para carregar um Driver para um Database. Para isso, comecei a pesquisar na documentação da Sun, mais detalhes sobre a classe Class. Com isso acabei caindo no método getClass da classe Object (que retorna a classe de um objeto em execução).
Observando a definição deste método encontrei o código que segue abaixo:

public final Class<? extends Object> getClass()


Para começar a entender o código acima, tive que então estudar sobre tipos genéricos em Java para entender o "<? extends Object>".
Então abaixo segue o que entendi sobre os genéricos:

Em primeira instância pode-se notar que a utilização de genéricos tras algumas vantagens como:

1 - simplificar o código eliminando typecasts
2 - segurança na utilização de objetos, tornando ainda mais forte a tipagem do Java.

Vejamos o exemplo de código abaixo:


Map mapa = new HashMap();
List livros = new ArrayList();

livros.add(new Book(1, "java"));
livros.add(new Book(2, "Nietzche"));

mapa.put("livros", livros);
mapa.put(new Integer(0), "coisa que nao tem nada a ver");
Book x = (Book)((List)mapa.get("livros")).get(0); //pegando o livro de java do array list livros

System.out.println("Nome do livro:" + x.getName());


O código acima compila tranquilamente mesmo atribuindo coisas que não tem nada a ver com o contexto dentro do mapa "mapa".


No código acima podemos notar 2 desvantagens:
1 - código ruim em termos de legibilidade
2 - o código está permitindo construir um mapa com chaves e valores incompatíveis o que poderia
trazer dores de cabeça caso for necessário fazer uma iteração neste mapa. (poderia até usar um instanceof para tratar isso, poré, além de não ser boa idéia em termos de performance, os puristas de OO iriam dizer que o sistema está mal modelado, pois uma boa modelagem permite resolver isso com polimorfismo)

Para resolver estes problemas os genéricos do Java5 são utilizados no código conforme abaixo:

/* testes de tipos genericos em java */
Map<String, List<Book<> mapa = new HashMap<String, List<Book>>();
List<Book> livros = new ArrayList<Book>();

livros.add(new Book(1, "java"));
livros.add(new Book(2, "Nietzche"));

mapa.put("livros", livros);
mapa.put(new Integer(0), "coisa que nao tem nada a ver"); // este codigo ja nao compila mais
Book x = mapa.get("livros").get(0); // sem typecasts
System.out.println("Nome do livro:" + x.getName());