Cache não é algo que os programadores desconhem. Quem esteve numa universidade, e teve uma aula séria de Arquitetura de Computadores, sabe que a memória é disposta numa hierarquia de caches. E quem já programou em Java, já ouviu falar de inicialização tardia, muito usado em singletons (apesar de não ser só para isso): » Read the rest of this entry «
Já usou Memcached?
maio 2nd, 2009 § 1
Aprenda Java EE 6, agora! (2)
fevereiro 24th, 2009 § 2
Dessa vez, estarei utilizando a versão b37 do Glassfish v3. Por causa disso, você vai ter que fazer alterações à mão no plugin do Eclipse para o servidor Glassfish. Na versão b36 havia as pastas web e ejb dentro de module. Agora, todos os jars estão dentro de module, sem distinção de pastas.
Primeiro, feche o Eclipse. Iremos copiar o plugin do Glassfish e descompactá-lo, para alterar o seu arquivo de configurações. No Linux eu fiz assim, supondo que ECLIPSE_HOME seja uma variável onde aponta para a pasta do Eclipse: » Read the rest of this entry «
Aprenda Java EE 6, agora!
fevereiro 15th, 2009 § 4
Quando fiquei craque no Java, a versão do “Enterprise Edition” era ainda 1.4. Eu aprendi a versão 5, logo depois do lançamento do Glassfish. Mas agora estou fazendo algo diferente, não vou esperar a versão Java EE 6 sair, vou aprender as coisas tão logo as novidades apareçam.
E você também pode fazer isso. Pegue uma versão promoted do Glassfish no site deles, que, assim como o Lost, tem sempre uma versão nova nas quartas à noite, que você baixa na quinta. A última versão, que estou usando nesta semana, é a b36.
Baixe a última versão disponível e descompacte num lugar que você achar apropriado. No Eclipse, na hora de instalar o servidor, basta escolher a opção “Glassfish v3 Prelude” (se você não usava Glassfish, na tela de novo servidor há uma opção de baixar os adaptadores para outros servidores). Pronto!
Eu vou fazer uma aplicação de exemplo, que é uma agenda de consultório médico. É babaca, mas tem uma regras óbvias que não dá pra ser tratado com o velho CRUD.
Vamos lá. Crie um “Dynamic Web Project” cujo Runtime seja o “Glassfish v3 Prelude”, vou chamá-lo de AgendaMedica. Não sei como vocês lidam com testes no Eclipse, mas costumo criar um novo projeto Java que referencia o projeto que estou interessado em testar. No caso, criarei também o projeto Java AgendaMedicaTeste. Após criado, dê <Alt> + <Enter> sobre o projeto, clique em “Java Build Path”, vá na aba “Projects” e adicione o projeto AgendaMedica.
No projeto AgendaMedicaTeste, eu já criei um método de testes pra realizar a consulta:
package br.com.objectzilla.agendamedica; import java.util.Calendar; import org.junit.Assert; import org.junit.Test; public class AgendamentoTeste { @Test public void pacienteMarcaHoraComMedico() { // um paciente... Paciente paciente = new Paciente(); paciente.setNome("Maria"); // um médico... Medico medico = new Medico(); medico.setNome("Dr. Gregory House"); // ambos agendam um horário Agendamento agendamento = new AgendamentoImpl(); Calendar horario = Calendar.getInstance(); horario.set(2009, Calendar.MARCH, 5, 17, 00, 00); agendamento.marcaConsulta(medico, paciente, horario); // agora, médico tem consulta com paciente marcada às 5 Paciente pacienteMarcado = medico.consulta(horario); Assert.assertEquals(paciente.getNome(), pacienteMarcado.getNome()); } }
Beleza, mas o código não compila, né? Ora, não seja por isso. Vá criando as classes clicando nos erros com o botão direito do mouse, só não se esqueça de colocar as classes no projeto AgendaMedica. No caso, Agendamento é uma interface e o restante são classes.
Bom, tente rodar o script de teste (clique com botão direito sobre a classe e dê Run As… > JUnit Test). Vai dar erro, óbvio! Por isso, convido a você a escrever código nas classes até o script rodar com sucesso. Não se preocupe, se você não conseguir ou não tiver paciência, no final do post eu mostro o link onde você pode baixar o código.
Agora, vamos transformar a interface Agendamento em um Stateless Session Bean. Não precisa nem mover pra um projeto próprio, pois agora os EJBs podem ser “deployados” em aplicações WAR. Basta adicionar @Local na interface (a v3 ainda não possui suporte a @Remote), assim:
@Local public interface Agendamento { // ... }
E adicionar @Stateless na classe que implementa:
@Stateless public class AgendamentoImpl implements Agendamento { // ... }
Pronto, faça o deploy do projeto web no servidor Glassfish v3 e veja o seguinte log aparecer:
INFO: Bound Java:Global name [business view] : java:global/AgendaMedica/AgendamentoImpl#br.com.objectzilla.agendamedica.Agendamento INFO: Bound Java:Global name [single business view] : java:global/AgendaMedica/AgendamentoImpl
Repare em como é realizado o binding com o JNDI. A partir da versão 6, não será mais uma implementação específica de contêiner. Todos os servidores de aplicações, se quiserem ser homologados para Java EE 6, deverão dispor seus EJBs, em contextos JNDI, de acordo com o seguinte padrão:
java:global[/<application-name>]/<module-name>/<bean-name>#<interface-name>
Onde:
<application-name> é opcional, refere-se ao nome do pacote EAR (não tem no nosso caso).
<module-name> é o módulo onde está o EJB (AgendaMedica).
<bean-name> é o nome do bean (AgendamentoImpl).
<bean-name> é o nome do bean (AgendamentoImpl).
<interface-name> é o nome completo da interface que implementa o bean (br.com.objectzilla.agendamedica.Agendamento).
O leitor atento deve ter reparado: está sendo feito dois bindings para o EJB! Isso também faz parte do padrão. Se o bean implementar apenas uma interface, um alias será criado onde o nome da interface não aparece.
Já que criamos o Session Bean, seria interessante se usássemos, não é não? Vamos fazer um Servlet, mas não precisa tomar o susto de configurar o wer.xml, pois basta criar um classe que herde de HttpServlet e adicionar uma anotação de acordo com o seguinte código:
@WebServlet(value="/agendamento") public class AgendamentoServlet extends HttpServlet { @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
Após o deploy, basta digitar http://localhost:8080/AgendaMedica/agendamento e você vai ver… uma tela em branco. Normal, a gente não fez nada mesmo. Agora, vamos preencher o doGet() com uma chamada ao método de marcarConsulta() de agendamento. Eu também injetei uma instância de EJB.
@WebServlet(value="/agendamento") public class AgendamentoServlet extends HttpServlet { @EJB private Agendamento agendamento; @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String nomeMedico = req.getParameter("medico"); String nomePaciente = req.getParameter("paciente"); int hora = Integer.parseInt(req.getParameter("hora")); // criando médico Medico medico = new Medico(); medico.setNome(nomeMedico); // criando paciente Paciente paciente = new Paciente(); paciente.setNome(nomePaciente); // criando horário Calendar horario = Calendar.getInstance(); horario.set(Calendar.HOUR, hora); horario.set(Calendar.MINUTE, 0); horario.set(Calendar.SECOND, 0); // chamando método de agendamento agendamento.marcaConsulta(medico, paciente, horario); PrintWriter pw = resp.getWriter(); pw.format("Eu juro que marquei consulta para o paciente %s, com o médico %s, na hora %d.", medico.getNome(), paciente.getNome(), horario.get(Calendar.HOUR)); } }
Se você rodar http://localhost:8080/AgendaMedica/agendamento?medico=Jose&paciente=Adolfo&hora=14, vai ver que a aplicação mostra uma mensagem na tela que não diz se foi, ou não foi, feito algo. Precisaria de uma tela de consulta, mas só dá pra fazer isso se nossa aplicação persistisse os objetos entre as requisições, o que atualmente não ocorre.
Vou fazer isso no próximo post, onde apresentarei mais novos recursos e novas funcionalidades na aplicação pra que ela finalmente funcione de verdade.
Quem quiser, pode ir alterando a aplicação por conta própria, deixei no repositório GitHub em http://github.com/leonardoverissimo/javaee-6-application/tree/master.
Desempenho / Escalabilidade: a última carta dos idiotas
janeiro 12th, 2009 § 1
Meu, hoje, 12/01, quase tive um ataque cardíaco (não, não tive não, é brincadeira). Duas coisas que eu vi, uma na Mundo Java, outra no GUJ, me deixaram com raiva.
Vamos à Mundo Java primeiro que, como sempre, está excelente. Hoje vi só a matéria sobre EJB e Spring (muito boa), porém houve um pequeno trecho que me deixou de estômago embrulhado (ênfases são minhas):
Anti-pattern em injeção de dependência
Com a facilidade e praticidade do EJB 3.0, alguns desenvolvedores acabam deixando de lado a preocupação com a escalabilidade e gerenciamento de recursos do container, anotando toda e qualquer classe do sistema para se beneficiar da injeção de dependência. Isso não é uma boa prática porque cada objeto anotado com @Stateless existirá um novo recurso para ser gerenciado pelo servidor de aplicações.Um exemplo prático dessá má utilização de injeção de dependências no ambiente EJB é a implementação do design pattern Data Access Object (DAO) como um EJB. Dependendo do design do software, cada funcionalidade do sistema irá conter um DAO e essa funcionalidade será um Session Bean para se beneficiar do recurso de injeção de dependência do servidor de aplicações. Para evitar esse problema, uma solução seria injetar o EntityManager diretamente nos session beans, por meio da anotação @PersistenceContext, ou utilizar o design pattern Abstract Factory.
Primeiro, vamos à bronca: você não está programando em C, nem precisa ouvir os malucos que programam nessa linguagem. Fim da bronca.
Segundo, existe dois problema na definição de um Stateless Session Bean (SLSB): um que a anotação não é sobre o objeto (isso ainda não existe na linguagem), mas na classe; e dois que, quando você cria um SLSB, não há a criação de um recurso, mas de váááários recursos em pool. Se você olhar na configuração do Glassfish, verá que por default é possível ter até 1024 instâncias de SLSB no pool.
Terceiro, sabe qual a justificativa para isso ser um anti-pattern? Escalabilidade, só isso! Não é problema de code smell, de código que gera resultados imprevisíveis, de problemas de manutenção…, nem nada! Simplesmente ES-CA-LA-BI-LI-DA-DE! Agora, o pool de instâncias de SLSB não existe para se ganhar escalabilidade? Se tiver problemas com isso, vai nas configurações de seu servidor e mude os parâmetros do container EJB! Outra solução é comprar mais memória ou, se for mais sério, fazer load balance de mais de um servidor.
Pior ainda, a alternativa ao “anti-pattern” é “injetar o EntityManager diretamente nos session beans”, o que fatalmente acarretaria códigos macarrônicos com métodos gigantescos e difíceis de ler. Ou então, “[utilize] o design pattern Abstract Factory” que, pelo que já vi usado no contexto do EJB, é POG!; pois é usado um único EJB para o Factory, que devolveria os DAOs necessários, que por sua vez, tem código muito mais idiota para se pegar o EntityManager do que se fosse feito por SLSB!
Gente, use SLSB para DAO também! Que mal há nisso? Escalabilidade não é! Acho que no fundo, as pessoas sentem saudades do tempo em que cada camada tinha seu framework, tipo: era Struts para a apresentação, EJB para a camada de negócio, Hibernate para a persistância… Hoje estamos se aproximando da era onde os SLSB podem ser usados até na camada de apresentação (Seam / Web Beans)! E ninguém passou a escrever código horrível por causa disso! Por que raios ainda ficar nesse pensamento?
Vamos ao tópico do GUJ, maldito! (Não você que é maldito! O tópico!) (link) O cara parecia que quer defender com unhas e dentes que usar static é melhor que criar instâncias, polimorfismo…, enfim, essa coisa linda que é o OO.
Bom, o que sei é que usar static a torto e a direito no Java EE dá merda, pois é um static por class loader, e os servidores de aplicações possuem vários class loaders, cada um com potencialidade de carregar a classe e ter seu próprio static. Segundo, que com métodos static não dá pra fazer muita coisa que o Java EE oferece (EJB, por exemplo). Terceiro, que é idiota mesmo, coisa de gente que não sabe OO.
Agora, sabe a justificativa para usar static? O cara acredita que é mais rápido, simplesmente isso! Quer dizer, dava muito bem pra gerar um código bom, usando OO, e comprar uma máquina de configuração muito boa. Muito melhor do que se matar em otimizações minúsculas de veracidade duvidosa.
Aliás, vou dar um guideline para se fazer otimizações:
1. Pergunte ao cliente se está achando o sistema lento. Se a resposta for não, fim do guideline.
2. A compra de mais memória, mais disco ou mais blade custa menos que o pagamento de uma equipe de desenvolvedores? Se sim, vá na Santa Efigênia e fim de guideline.
3. Meça a duração usando vários casos possíveis.
4. Faça a alteração e meça de novo.
5. Se a nova medida não for muito menor que a medida antiga, jogue suas alterações no lixo e volte ao passo 4.
6. Caso contrário, comite a alteração e dê por encerrado (por enquanto).
Os dois casos me deixam perplexos: é como se para se justificar alguma coisa que não sabe bem o porquê, ao invés de ir mais a fundo e pesquisar, diz-se apenas que a altenativa “ruim” tem problemas de desempenho ou escalabilidade! Simples assim! E esquece que esses dois tipos de problemas está mais relacionado à arquitetura, à característica do sistema, ao “conjunto da obra” e muito pouco sobre a tecnologia ou o paradigma em si. Por muito escarcéu, muita gente escolhe o Barrabás.
Agora, por que as pessoas usam o argumento do desempenho / escalabilidade? Tenho duas teorias:
1. Alguns programadores aprenderam apenas uma determinada tecnologia em sua carreira. Quando esta tecnologia começa a sumir, eles passam a ter um certo desdém pelo novo, classificando pelos mais variados adjetivos. Alguns deles simplesmente não colam. Porém, o que, às vezes, fica marcado, é dizer que tal coisa não tem desempenho ou não tem escalabilidade, e por uma única razão: é difícil provar a menos que se meça com os próprios olhos. E como muita gente não faz isso, fica-se no benefício da dúvida.
2. Alguns programadores trabalham em ambientes péssimos de trabalho, a principal é a ausência de decisão e autonomia. Normalmente, as decisões são concentradas em pouquíssimas pessoas, e que nem sempre são as mais qualificadas. Os “artefatos” que saem das mãos dessas pessoas são os famosos diagramas de UML, bastando o programador codificar. (O mais imbecil nisso é que os “eleitos” escrevem diagramas tão detalhados que poderiam ter feito o negócio direto em código, e que haveria a mesma abstração. Mas não fazem código porque delegar essa atividade pra alguém é a melhor maneira de camuflar seus próprios erros.) E como o programador não tem a liberdade necessária para fazer seu trabalho, só lhe resta uma coisa intelectual para fazer: otimizar. Pena que será imperceptível para os olhos dos outros. Pena que o próximo a dar manutenção será ele mesmo.
Entre exceção checada e não-checada… fique com os dois!
janeiro 10th, 2009 § 0
Já viu aquelas discussões no Reddit ou GUJ sobre usar ou não exceções checadas? Sempre tem uns proponentes e oponentes defendendo um jeito ou de outro. Invariavelmente, a discussão não dá em nada. Primeiro porque não é uma questão de escolher um jeito e usá-lo por todo o sistema. Segundo porque, já que isso existe, a menos que você queira mudar pra C#, você é obrigado a conviver.
Se eu tivesse a oportunidade, do tipo, sei lá: viesse um cara, chamado James e me dissesse:
Leo, seguinte, eu tô a fim de fazer uma nova linguagem aí e, sei lá, achei interessante forçar o tratamento de exceções pra que ninguém esquecesse de tratá-lo, tipo uma exceção checada. O que você acha?
Minha resposta:
Horrível!
Sim, existe um limite prático pra tipagens verificadas por compilador, o aumento da complexidade. Isso certamente acaba complicando mais pra quem começa na linguagem. Mas enfim, já que não me consultaram…. o melhor é usá-las, e usá-las com sabedoria.
Mas Leo, como seria?
Boa pergunta. Uma definição legal é do livro Effective Java, que diz, na página 244:
… use checked exceptions for conditions from which the caller can reasonably be expected to recover… use runtime exceptions to indicate programming errors…
Lendo essas duas definições, pode parecer confuso, veja o que alguém poderia pensar: “Exceção checada só ocorre quando há erros de programação. Nunca cometo erros, logo sempre usarei exceções checadas!”
Não, não e NÃO!!!
Pra entender o que é um erro de programa, você deveria entender que os métodos de seus objetos só podem ser executados se determinadas pré-condições sejam satisfeitas. O problema é que miuta gente também nunca ouviu falar em pré e pós-condições. E dos que ouviram, aplicam de um jeito errado. Meu, vou contar uma história: uma vez eu estava vendo um “documento” de “caso de uso” e lá tinha a seção “pós-condições”. Em todas as vezes, juro, em TODAS as vezes, estava escrito: “Pós-condição: caso de uso executado com sucesso.”. O que?! Significa que é uma condição sine qua non de que NUNCA vai dar falha?
Vamo lá: pré-condição e pós-condição refere-se ao mundo externo do método (ou do fluxo ou do processo, depende do seu ponto de vista). Pré é como o estado do “mundo” deve estar antes da execução. Pós é como o “mundo” irá ficar.
Exemplo: um método de inserção. A pré-condição é de que o objeto exista e que seja de um tipo específico. A pós-condição é que no banco existirá um registro a mais, ou ficará como antes (em caso de erro).
E se eu chamar o método e a pré-condição não estiver sido respeitada? Erro! O método nem é chamado. E se a pós-condição não for respeitada? Erro também! O sistema, pelo menos, deve voltar ao estado anterior da execução.
Claro que isso não é implementado nativamente nas linguagens mais populares, nem nas mais-ou-menos populares. Em Java, por causa disso, você trata as pré-condições usando exceções. (As pós poderiam ser a captura de exceções de outros métodos que o método chama.) E como violação de pré-condição é um erro, você usa exceção não-checada, pois esta deve ser usada em caso de erros de programa.
Quer ver um exemplo de onde se usa exceção não-checada como violação de pré-condição? HttpServletResponse! Existe uma condição que é o seguinte: uma vez aberto um OutputStream ou um Writer, não se pode abrir um outro stream, nem executar um forward. O que acontece quando alguém, “sem querer”, comete essa cagada esse erro?
É lançada uma exceção não-checada, a IllegalStateException.
Como guia: use exceções não-checadas quando o chamador tem meios de checar se os parâmetros ou o estado do objeto estão corretos antes da execução do método (claro, não é uma ciência exata, podem haver, err…, exceções). No caso acima, a pessoa poderia muito bem verificar se, no Response, é possível criar um novo fluxo chamando o método isCommitted().
E quando usar exceções checadas? Existem casos em que o método não tem qualquer meio de verificar determinada pré-condição, ou tem mas o meio de verificar é praticamente a própria execução do método. O chamador então deve se arriscar chamando o método, e este deve dizer depois se deu certo ou não, lançando exceção, checada!
Exemplo: método parse() de DateFormat, que vai pegar uma String, e devolver a representação dela como um Date. Repare que é lançando exceção checada ParseException caso a String não seja uma data. Por que exceção checada? Porque verificar se uma String é realmente uma data não é tarefa trivial, não dando pra contar com uma verificação prévia antes da execução do método. Por outro lado, parseInt() da classe Integer não lança exceção checada, pois é possível facilmente verificar se uma String contém somente números.
Outro exemplo são os métodos write() de BufferedWriter ou prepareStatement() de Connection que lançam exceções checadas por uma razão simples: nem o chamador nem o método tem a mínima noção se o sistema de arquivos ou o banco de dados está em condições de realizar a ação desejada (e verificar isso pode resultar em outra exceção!).
Como guia: use exceções checadas quando o chamador precisa tomar uma “rota alternativa” após o método ser chamado, pois não dá pra evitar o pior antes.
Uma outra coisa, nem sempre dá pra saber a priori se determinada exceção deveria ser checada ou não. Se você criou uma exceção checada, mas seus chamadores não fazem outra coisa a não ser relançar a exceção ou fazer o log dele, é porque você tem que converter a exceção checada em exceção não checada! Exceções checadas é para quando o chamador precisa tomar uma atitude a respeito. Se nenhum chamador precisou fazer isso, é porque, muito provavelmente, a exceção nunca precisaria ser tratada e o lançamento dela significa simplemente um erro de programa.
Outra coisa, exceções precisam ter nomes significativos. Evite as famosas “exceções arquiteturais” ou “exceções de camadas”, que você identifica por DaoException, BusinessException, ApplicationException ou NomeDaEmpresaException. Todas elas não dão significado ao erro que ocorreu e o programador certamente não irá tratá-las adequadamente, pois não tem a mínima idéia do que fazer, dada a ausência de informações contidas nessa exceção.
E evite criar novas exceções quando existem algumas delas já prontas pra você, como é o caso de NullPointerException (sim, se você receber um parâmetro nulo, não há problema nenhum em você lançar essa exceção, colocando uma mensagem talvez), IllegalArgumentException ou IllegalStateException. Elas não são exclusivas das bibliotecas Java, nem são exceções reservadas. Use-as quando for o caso.
Closure: em C++ (?)
janeiro 7th, 2009 § 1
Esse post é uma continuação da série Closure.
C++ tem closure? Como assim? Viajou, né? Bom, vou tentar fazer algo, só não sei se vai dar certo. A minha idéia é fazer uma classe Observer onde é possível registrar uma closure. Quando for chamado a função notify() do objeto dessa classe, a closure será chamada e esta terá inclusive referencia de um contexto que não existe pro objeto Observer. » Read the rest of this entry «
Você sabe o que é: Closure?
janeiro 4th, 2009 § 4
Existem três propostas de closures em Java, BGGA, CICE/ARM e FCM. A primera proposta é a mais forte mas também a mais criticada, e, ao que parece, não haverá espaço para closures no Java 7. Mas o que é closure? » Read the rest of this entry «