225

Java Na Pratica

Embed Size (px)

DESCRIPTION

Java

Citation preview

JAVA NA PRÁTICATópicos Avançados

JDBC Threads Redes RMI CORBA Servlets JSP

Alcione de Paiva OliveiraVinícius Valente Maciel

8 de Novembro de 2003

Direitos AutoraisTodos os direitos sobre esta obra estão reservados para os auto-res do livro. Texto registrado na Biblioteca Nacional: registro297.373, livro 540, folha 33.

O48JOLIVEIRA, Alcione de Paiva

Java na prática, tópicos avançados: RMI CORBAJDBC threads redes servlets JSP / Alcione de Paiva Oliveira,Vinícius Valente Maciel. - Viçosa : Fábrica de Livros Ed.,2003.

225 p. il.

1. Linguagem de programação. 2. Java. I. MACIEL,Vinícius Valente. II. Título.

CDD: 005.133CDU: 519.682

Sobre os AutoresAlcione de Paiva Oliveira é Doutor em Informática pela

PUC-Rio, Mestre em Ciências pelo Instituto Militar de Engenha-ria e Bacharel em Oceanogra�a pela UERJ. Ex-diretor técnicoda INFAX Tecnologia e Sistemas e ex-coordenador do curso deEngenharia de Computação do Instituto Militar de Engenharia.Atualmente exerce o cargo de professor Adjunto do Departamentode Informática da Universidade Federal de Viçosa. Suas áreas deinteresse são Inteligência Arti�cial, Linguagens de Programação eEngenharia de Software.

Vinícius Valente Maciel é Mestrando em Ciência da Com-putação pela Universidade Federal Fluminense e Bacharel em Ci-ência da Computação pela Universidade Federal de Viçosa. Atu-almente exerce o cargo de Analista de Sistemas da INFAX Tec-nologia e Sistemas Ltda. Suas áreas de interesse são Especi�ca-ção Formal de Sistemas, Linguagens de Programação, Sistemas deTempo-Real e Engenharia de Software.

AGRADECIMENTOS

A elaboração do presente trabalho contou com a colaboração di-reta e indireta de diversas pessoas. Primeiramente gostaríamos deagradecer nossas mulheres e companheiras Alexandra e Andréiaque nos apoiaram (e nos toleraram) em todos os momentos. Damesma forma gostaríamos de agradecer a nossos pais que sem-pre estão ao nosso lado. Não podemos esquecer também todos osnossos alunos que ajudaram com sugestões quando este materialainda era uma apostila. Finalmente, gostaríamos de agradecer atodos que direta e indiretamente ajudaram na concretização destesonho.

PREFÁCIO

O propósito deste livro é tentar preencher um espaço não cobertopelos livros relacionados com a linguagem Java. Não existe, atéo momento, um livro que aborde um conjunto de tópicos avança-dos da linguagem e que seja, ao mesmo tempo, didático e prático.De modo a promover a didática procuramos nos ater aos princi-pais aspectos dos assuntos tratados, sem tentar esgotar todos osaspectos do tema. Já o lado prático advém do uso intensivo deexemplos. Além disso, existe um exemplo, o da agenda eletrônica,que é apresentado com um grau crescente de complexidade, o quepermite que o leitor dirija sua atenção para as novas técnicas queestão sendo introduzidas. O exemplo da agenda eletrônica possuium grau de complexidade su�ciente para ser usado como embriãode sistemas mais so�sticados.

O livro aborda os recursos da linguagem para o tratamento deconcorrência, acesso a banco de dados, programação em redes eprogramação para a Web. Estes são assuntos que não são abor-dados em muitos livros com um número bem maior de páginas.Mesmo os livros existentes em tópicos avançados cobrem apenasum ou dois dos tópicos mencionados.

Esperamos que o livro cumpra o objetivo proposto e permitaque os leitores possam tirar o máximo de proveito da linguagemJava.

LISTA DE SIGLAS

ASP ActiveServer PagesAWT Abstract Window ToolkitCGI Common Gateway InterfaceGUI graphical user interfaceCORBA Common Object Request Broker ArchitectureCPU Central Processing UnitDMZ Demilitarized ZoneFAPESP Fundação de Amparo à Pesquisa do Estado de

São PauloHTML Hypertext Markup LanguageHTTP Hypertext Transfer ProtocolIP Internet ProtocolJDBC Java Database ConnectivityJDK Java Development ToolkitJSP Java Server PagesMIME Multipurpose Internet Mail ExtensionsMVC modelo-visão-controleODBC Open Database ConnectivityORB Object Request BrokerPHP Personal Home PagesRMI Remote Method InvocationTCP Transmission Control ProtocolUDP User Datagram ProtocolURI Uniform Resource Identi�erURL Uniform Resource Locator

Conteúdo

1 Introdução 11.1 Convenções . . . . . . . . . . . . . . . . . . . . . . 6

2 Concorrência 72.1 Criando threads em Java . . . . . . . . . . . . . . . 10

2.1.1 Usando a interface Runnable . . . . . . . . 132.2 A classe Thread . . . . . . . . . . . . . . . . . . . . 14

2.2.1 Variáveis públicas . . . . . . . . . . . . . . 172.3 Ciclo de Vida dos Threads . . . . . . . . . . . . . . 18

2.3.1 sleep(), yield(), join(), stop(), suspend() eresume() . . . . . . . . . . . . . . . . . . . . 19

2.4 Daemon Threads . . . . . . . . . . . . . . . . . . . 232.5 A In�uência do Sistema Operacional sobre os Threads 25

2.5.1 Forma de escalonamento de threads . . . . 262.5.2 Relacionamento entre os níveis de priorida-

des de�nidas na linguagem Java e os níveisde prioridades de�nidas nos Sistemas Ope-racionais . . . . . . . . . . . . . . . . . . . . 27

2.6 Compartilhamento de Memória e Sincronização . . 282.6.1 Atomicidade de Instruções e Sincronização

do Acesso à Sessões Críticas . . . . . . . . . 322.6.2 Comunicação entre Threads: wait() e notify() 39

3 Programação em rede 553.1 Conceitos Sobre Protocolos Usados na Internet . . 55

3.1.1 TCP . . . . . . . . . . . . . . . . . . . . . . 57

v

3.1.2 UDP . . . . . . . . . . . . . . . . . . . . . . 583.1.3 Identi�cação de Hosts (Número IP) . . . . . 583.1.4 Identi�cação de Processos (Portas) . . . . . 59

3.2 Programação em Rede com Java . . . . . . . . . . 603.2.1 Comunicação Básica Entre Aplicações . . . 613.2.2 Comunicação orientada a conexão (cliente) 613.2.3 Comunicação orientada à conexão (servidor) 633.2.4 Comunicação Sem Conexão (UDP) . . . . . 663.2.5 Comunicação por meio de URL . . . . . . . 693.2.6 Manipulando URLs em Java . . . . . . . . 703.2.7 Comunicando por meio de URLConnection 72

4 Acesso a Banco de Dados 754.1 Modelos de Acesso a Servidores . . . . . . . . . . . 764.2 Tipos de Drivers JDBC . . . . . . . . . . . . . . . 77

4.2.1 Obtendo os Drivers JDBC . . . . . . . . . . 794.3 Preparando o Banco de Dados . . . . . . . . . . . 794.4 Exemplo Inicial . . . . . . . . . . . . . . . . . . . . 84

4.4.1 Carregando o Driver . . . . . . . . . . . . . 854.4.2 Estabelecendo a conexão . . . . . . . . . . . 854.4.3 Criando e Executando Comandos . . . . . . 86

4.5 Recuperando Valores . . . . . . . . . . . . . . . . . 874.6 Trabalhando com Metadados . . . . . . . . . . . . 894.7 Trabalhando com datas . . . . . . . . . . . . . . . 914.8 Transações e Nível de Isolamento . . . . . . . . . 92

4.8.1 Transação . . . . . . . . . . . . . . . . . . . 924.8.2 Níveis de isolamento . . . . . . . . . . . . . 94

4.9 Prepared Statements . . . . . . . . . . . . . . . . . 974.10 Procedimentos Armazenados (Stored Procedures) . 984.11 Agenda Eletrônica versão JDBC . . . . . . . . . . 1004.12 Como con�gurar a ponte JDBC-ODBC . . . . . . 107

5 RMI 1115.1 Arquitetura RMI . . . . . . . . . . . . . . . . . . . 1125.2 Criando nossa agenda distribuída . . . . . . . . . . 112

5.2.1 Passo a Passo . . . . . . . . . . . . . . . . . 1125.2.2 Implementando interface do objeto remoto 1135.2.3 Escrevendo objeto remoto . . . . . . . . . . 114

5.2.4 Gerando Stub . . . . . . . . . . . . . . . . . 1155.2.5 Desenvolvendo o código que disponibiliza o

objeto . . . . . . . . . . . . . . . . . . . . . 1155.2.6 Escrevendo o cliente . . . . . . . . . . . . . 116

5.3 Testando tudo . . . . . . . . . . . . . . . . . . . . . 1175.3.1 No Windows . . . . . . . . . . . . . . . . . 1175.3.2 No Linux . . . . . . . . . . . . . . . . . . . 118

6 CORBA 1196.1 O que é CORBA? . . . . . . . . . . . . . . . . . . 1196.2 Exemplo CORBA em Java . . . . . . . . . . . . . . 126

6.2.1 Escrevendo a IDL . . . . . . . . . . . . . . 1266.2.2 Compilando a IDL . . . . . . . . . . . . . . 1276.2.3 Implementando nosso Objeto . . . . . . . . 1276.2.4 Escrevendo o servidor . . . . . . . . . . . . 1286.2.5 Escrevendo o cliente . . . . . . . . . . . . . 1296.2.6 Rodando o exemplo . . . . . . . . . . . . . 130

6.3 Exemplo CORBA (Java + C) . . . . . . . . . . . . 1306.3.1 Compilando a IDL . . . . . . . . . . . . . . 1316.3.2 Implementando nosso Objeto . . . . . . . . 1326.3.3 Escrevendo o Servidor . . . . . . . . . . . . 1326.3.4 Escrevendo o Cliente . . . . . . . . . . . . . 1346.3.5 Compilando e Rodando o Exemplo . . . . . 135

7 Servlets e JSP 1377.1 Servlets . . . . . . . . . . . . . . . . . . . . . . . . 137

7.1.1 Applets X Servlets . . . . . . . . . . . . . . 1397.1.2 CGI X Servlets . . . . . . . . . . . . . . . . 139

7.2 A API Servlet . . . . . . . . . . . . . . . . . . . . . 1397.2.1 Exemplo de Servlet . . . . . . . . . . . . . . 142

7.3 Compilando o Servlet . . . . . . . . . . . . . . . . 1437.3.1 Instalando o Tomcat . . . . . . . . . . . . . 143

7.4 Preparando para executar o Servlet . . . . . . . . . 1487.4.1 Compilando o Servlet . . . . . . . . . . . . 1487.4.2 Criando uma aplicação no Tomcat . . . . . 148

7.5 Executando o Servlet . . . . . . . . . . . . . . . . . 1497.5.1 Invocando diretamente pelo Navegador . . . 1497.5.2 Invocando em uma página HTML . . . . . 150

7.5.3 Diferenças entre as requisições GET e POST 1507.6 Concorrência . . . . . . . . . . . . . . . . . . . . . 1517.7 Obtendo Informações sobre a Requisição . . . . . 1547.8 Lidando com Formulários . . . . . . . . . . . . . . 1567.9 Lidando com Cookies . . . . . . . . . . . . . . . . . 1577.10 Lidando com Sessões . . . . . . . . . . . . . . . . . 1617.11 JSP . . . . . . . . . . . . . . . . . . . . . . . . . . 164

7.11.1 PHP X JSP . . . . . . . . . . . . . . . . . . 1667.11.2 ASP X JSP . . . . . . . . . . . . . . . . . . 1667.11.3 Primeiro exemplo em JSP . . . . . . . . . . 1677.11.4 Executando o arquivo JSP . . . . . . . . . . 1687.11.5 Objetos implícitos . . . . . . . . . . . . . . 1697.11.6 Tags JSP . . . . . . . . . . . . . . . . . . . 1697.11.7 Extraindo Valores de Formulários . . . . . . 1757.11.8 Criando e Modi�cando Cookies . . . . . . . 1777.11.9 Lidando com sessões . . . . . . . . . . . . . 1787.11.10O Uso de JavaBeans . . . . . . . . . . . . . 1807.11.11Escopo do bean . . . . . . . . . . . . . . . . 183

7.12 Reencaminhando ou Redirecionando requisições . . 1877.13 Uma Arquitetura para comércio eletrônico . . . . . 189

7.13.1 Tipos de aplicações na WEB . . . . . . . . 1897.13.2 Arquitetura MVC para a Web . . . . . . . 1907.13.3 Agenda Web: Um Exemplo de uma aplica-

ção Web usando a arquitetura MVC . . . . 192

Capítulo 1

Introdução

Java é uma linguagem de programação desenvolvida pela Sun Mi-crosystems e lançada em versão beta em 1995. O seu desenvolvi-mento foi iniciado em 1991 pela equipe liderada por James Gos-ling visando o mercado de bens eletrônicos de consumo. Por issofoi projetada desde o início para ser independente de hardware,uma vez que as características dos equipamentos variam ampla-mente neste nicho de desenvolvimento. Outro objetivo estabele-cido desde sua concepção foi o de ser uma linguagem segura. Se-gura tanto no sentido de evitar algumas falhas comuns que os pro-gramadores costumam cometer durante o desenvolvimento, comono sentido de evitar ataques externos. Isto é importante no mer-cado de bens eletrônicos de consumo porque ninguém gostaria deadquirir um produto que necessitasse desligar e religar para quevoltasse a funcionar corretamente. Estas características desper-taram o interesse para utilização de Java em outro ambiente quetambém necessitava de uma linguagem com este per�l: a Internet.

A Internet também é um ambiente constituído por equipamen-tos de diferentes arquiteturas e necessita muito de uma linguagemque permita a construção de aplicativos seguros. Muitas pessoasargumentarão que estas características podem ser encontradas emoutras linguagens e portanto isto não explica o súbito sucesso dalinguagem. Podemos arriscar alguns palpites apesar de este serum terreno um pouco pantanoso para se aventurar, até porque

1

2 Java na prática

as linguagens de programação tendem assumir um caráter quasereligioso. Uma das razões que na nossa opinião favoreceram arápida adoção da linguagem foi a sintaxe. Java é sintaticamentemuito semelhante à linguagem C/C++, apesar de existirem dife-renças fundamentais na �loso�a de implementação entre as duaslinguagens. Isto facilitou a migração de uma legião imensa deprogramadores C/C++ para a nova linguagem. Outra razão quenão pode ser desprezada é o momento atual onde os desenvolve-dores estão ansiosos para se libertarem de sistemas proprietários.Portanto, apesar de não serem novas as idéias embutidas na lin-guagem Java, a reunião delas em uma só linguagem, juntamentecom a facilidade de migração dos programadores e o momentoatual, contribuíram para o rápido sucesso da linguagem.

Hoje, segundo a International Data Corp. (IDC), existem maisde 2 milhões de programadores Java no mundo e a estimativaé que o número de desenvolvedores ultrapasse os 5 milhões em2004. Segunda a Gartner, 62% das grandes companhias do Brasilpossuem algum tipo de aplicação em Java e em 2005 é previstoque este número chegará a 80%. Os pro�ssionais que dominam alinguagem estão entre os mais bem pagos da área de Tecnologiada Informação (TI), segundo a revista Info Exame (dezembro de2001). A lista abaixo apresenta as principais características deJava, de modo que o leitor tenha uma visão geral da linguagem:

• Orientação a objetos. Java não é uma linguagem to-talmente orientada a objetos como Smalltalk, onde tudo éobjeto ou métodos de objetos. Por questões de e�ciência fo-ram mantidos alguns tipos primitivos e suas operações. Noentanto Java possui um grau de orientação a objetos bemmaior que C/C++, o que a torna bem mais harmoniosa efácil de assimilar, uma vez que o programador tenha com-preendido esta forma de desenvolvimento.

• Compilação do código fonte para código de uma má-quina virtual (Bytecodes). Esta característica visa tor-nar a linguagem independente de plataforma de Hardwaree Sistema Operacional. Obviamente é necessário que existaum programa capaz de interpretar o código em Bytecodespara cada Sistema Operacional, denominado de Máquina

Tópicos Avançados 3

Virtual. Porém, nada impede que o código fonte seja tra-duzido diretamente para o código executável na máquinade destino. Já existem ambientes de desenvolvimento queapresentam este tipo de opção. Alternativamente é possívelprojetar equipamentos que processem em hardware os Byte-codes. O diagrama da �gura 1.1 ilustra as etapas envolvidasna execução de um código Java.

Figura 1.1 Fases para execução de um programa fonte emJava.

• Ausência de manipulação explícita de ponteiros. Emlinguagens como C/C++ e Pascal existe o tipo ponteirocomo tipo primitivo da linguagem. A especi�cação origi-nal de Pascal é restritiva no uso de ponteiros, permitindoque sejam usados apenas para referenciar memória obtidana área de alocação dinâmica (heap) e não permite que oprogramador examine o valor da variável do tipo ponteiro,nem que realize operações aritméticas com ponteiros. Jáa linguagem C/C++ permite que o valor armazenado navariável do tipo ponteiro faça referência a qualquer área dememória, inclusive à área estática e automática (pilha), alémde permitir aritmética de ponteiros e o exame direto do va-lor armazenado. A manipulação do tipo ponteiro exige umagrande dose de atenção por parte do programador e mesmoprogramadores experientes frequentemente cometem errosno seu uso. Além disso, o uso de ponteiros é uma fontede insegurança na linguagem, uma vez que permite que ousuário faça acesso a memória que pode pertencer a outrosprocessos, abrindo a possibilidade para desenvolvimento deprogramas hostis ao sistema. A linguagem Java não possuio tipo ponteiro. Isto não quer dizer que não seja possível

4 Java na prática

realizar alocação dinâmica de memória. Todo objeto criadoé alocado na área de heap (memória de alocação dinâmica),mas o usuário não pode manipular a referência ao objetoexplicitamente.

• Recuperação automática de memória não utilizada(Coleta de Lixo - Garbage Collection). Nas linguagensonde existe alocação dinâmica de memória, o programadoré responsável pela liberação de memória previamente obtidana área de alocação dinâmica e que não está sendo mais uti-lizada. Se houver falhas na execução desta responsabilidadeocorrerá o problema que é conhecido sob a denominação de"vazamento de memória". Este problema faz com que, apartir de certo ponto, o programa não consiga obter me-mória para criação de novos objetos, apesar de existir áreaque não está sendo mais usada mas que não foi devolvidaao gerente de memória. Outro erro comum é a tentativa deacesso a áreas de memória já liberadas. Todos os programa-dores que trabalham com linguagens que permitem alocaçãodinâmica conhecem bem estes problemas e sabem o quantoé difícil implementar programas que não possuam estes ti-pos de erros. A maior parte dos erros que ocorrem no usodestas linguagens é devido a problemas na alocação/libera-ção de memória. Visando o desenvolvimento de aplicaçõesrobustas, livres deste tipo de falha, os projetistas de Java in-corporaram um procedimento de coleta automática de lixoà máquina virtual. Deste modo, os objetos que não estãosendo mais usados são identi�cados pelo procedimento, quelibera a memória para ser utilizada na criação de novos ob-jetos.

• Segurança. As pessoas costumam dizer que Java é uma lin-guagem segura. Mas o que é uma linguagem de programaçãosegura? Segurança possui signi�cados distintos para pessoasdiferentes. No caso da linguagem Java na versão 1.0, segu-rança signi�ca impedir que programas hostis possam causardanos ao ambiente computacional ou busquem informaçõessigilosas em computadores remotos para uso não autorizado.Na versão 1.1, foi adicionada a capacidade de permitir a ve-

Tópicos Avançados 5

ri�cação da identidade dos programas (autenticação) e, naversão 1.2, os dados que os programas enviam e recebem po-dem ser criptografados por meio do uso de um pacote adici-onal. Na versão 1.4, o pacote de criptogra�a JCE (JavaTMCryptography Extension) foi incorporado ao J2SDK.

• Suporte à Concorrência. A construção de servidores,a criação de programas com interfaces grá�cas, e progra-mas semelhantes que tem em comum a necessidade de que oatendimento de uma solicitação não incapacite o sistema deresponder a outras solicitações concorrentemente, deman-dam o uso de uma linguagem que facilite o desenvolvimentodeste tipo de programa. As linguagens projetadas antes dosurgimento destas necessidades, como C/C++, não previamfacilidades para este tipo de programação, o que obrigou aincorporação destes recursos posteriormente, por meio defunções adicionais. Como a programação concorrente é umaforma de programação que difere bastante da programaçãosequencial convencional, a simples adição de novas funções,para tentar adaptar a linguagem a esta forma de codi�ca-ção, não cria um ajuste perfeito com a linguagem subja-cente. Por outro lado, Java foi projetada visando facilitara programação concorrente. Isto faz com que a criação delinhas de execução (threads) seja bem mais natural dos quenas linguagens tradicionais.Programação em rede. Java pos-sui em seu núcleo básico classes para comunicação em redepor meio dos protocolos pertencentes à pilha de protocolosTCP/IP. A pilha de protocolos TCP/IP é a utilizada pelaInternet e tornou-se o padrão de fato para comunicação en-tre computadores em uma rede heterogênea. Isto torna Javaparticularmente atrativa para o desenvolvimento de aplica-ções na Internet. Além disso Java está incorporando um am-plo conjunto de soluções para computação distribuída, comoCORBA (Common Object Request Broker Architecture),RMI (Remote Method Invocation) e Servlets/JSP (aplica-ções Java que são executadas por servidores Web).

Após o lançamento da versão beta da linguagem em 1995, aSun tem liberado diversas evoluções da linguagem na forma de

6 Java na prática

versões e releases de um conjunto de ferramentas denominado deJava Development Kit (JDK) até a versão 1.2, quando se passoua denominar Java 2 SDK (Standard Development Kit). Isto ocor-reu porque outros kits de desenvolvimento com propósitos espe-cí�cos foram lançados, como o J2EE (Java 2 Enterprise Edition),voltado para aplicações distribuídas escaláveis e o J2ME (Java 2Micro Edition), voltado para aplicações embutidas em dispositi-vos eletrônicos (Celulares, handheld, etc.). Durante a elaboraçãodeste livro, a última versão estável do SDK era a de número 1.4que pode ser obtida gratuitamente no site http://java.sun.com/.

1.1 ConvençõesAs seguintes convenções são usadas neste livro.

1. Fontes com larguras constantes são usadas em:

• exemplos de código

public class Ponto{

private int x , y ;}

• nomes de métodos, classes e variáveis mencionadas notexto.

2. Fontes com larguras constantes em negrito são usadas dentrode exemplos de códigos para destacar palavras chave.

3. Fontes em itálico são usadas:

• em termos estrangeiros;• na primeira vez que for usado um termo cujo signi�cado

não for conhecimento generalizado.

Capítulo 2

Concorrência

Um sistema operacional é dito concorrente se permite que mais deuma tarefa seja executada ao mesmo tempo. Na prática a concor-rência real ou paralelismo só é possível se o hardware subjacentepossui mais de um processador. No entanto, mesmo em computa-dores com apenas um processador é possível obter um certo tipode concorrência fazendo com que o processador central executeum pouco de cada tarefa por vez, dando a impressão de que astarefas estão sendo executadas simultaneamente.

Dentro da nomenclatura empregada, uma instância de um pro-grama em execução é chamada de processo. Um processo ocupaum espaço em memória principal para o código e para as variáveistransientes (variáveis que são eliminadas ao término do processo).Cada processo possui pelo menos uma linha de execução (Thread).Para ilustrarmos o que é uma linha de execução suponha um de-terminado programa prog1. Ao ser posto em execução é criadoum processo, digamos A, com uma área de código e uma área dedados e é iniciada a execução do processo a partir do ponto deentrada. A instrução inicial assim como as instruções subsequen-tes formam uma linha de execução do processo A. Portanto, umthread nada mais é que uma sequência de instruções que está emexecução de acordo com que foi determinado pelo programa. Oestado corrente da linha de execução é representada pela instru-ção que está sendo executada. A �gura 2.1 mostra a relação entre

7

8 Java na prática

estes elementos.

Figura 2.1 Relação entre Programa, Processo e Thread.

É possível existir mais de uma linha de execução em um únicoprocesso. Cada linha de execução pode também ser vista como umprocesso, com a diferença que enquanto cada processo possui suaárea de código e dados separada de outros processos, os threadsem um mesmo processo compartilham o código e a área de dados.O que distingue um thread de outro em um mesmo processo é ainstrução corrente e uma área de pilha usada para armazenar ocontexto da sequência de chamadas de cada thread. Por isso osthreads também são chamados de processos leves (light process).A �gura 2.2 mostra esquematicamente a diferença entre processose threads.

Figura 2.2 (a) Processos; (b) Threads.

Tópicos Avançados 9

Sistemas monotarefas e monothreads como o MS-DOS pos-suem apenas um processo em execução em um determinado ins-tante e apenas um thread no processo. Sistemas multitarefas emonothreads como o Windows 3.1 permitem vários processos emexecução e apenas um thread por processo. Sistemas multitare-fas e multithread como o Solaris, OS/2, Linux, QNX e Windows98/NT/XP/2000 permitem vários processos em execução e váriosthreads por processo.

Como os threads em um mesmo processo possuem uma área dedados em comum, surge a necessidade de controlar o acesso a essaárea de dados, de modo que cada thread não leia ou altere dados nomomento que estão sendo alterados por outro thread. A inclusãode instruções para controlar o acesso a áreas compartilhadas tornao código mais complexo do que o código de processosmonothreads.

Uma pergunta pode surgir na mente do leitor: se a inclusãode mais de um thread torna o código mais complexo, então por-que razão alguém projetaria código multithread? A resposta é:processos com vários threads podem realizar mais de uma tarefasimultaneamente e, por isso, são úteis na criação de processosservidores, criação de animações e no projeto de interfaces como usuário que não �cam travadas durante a execução de algumafunção. Por exemplo, imagine um processo servidor a espera derequisições de serviços. Podemos projetá-lo de modo que, ao sur-gir uma solicitação de um serviço por um processo cliente, ele crieum thread para atender a solicitação enquanto volta a esperar arequisição de novos serviços. Com isto os processos clientes nãoprecisam esperar o término do atendimento de alguma solicitaçãopara ter sua requisição atendida.

O mesmo pode ser dito em relação ao projeto de interfaces como usuário. O processo pode criar threads para executar as funçõessolicitadas pelo usuário, enquanto aguarda novas interações. Casocontrário, a interface �caria impedida de receber novas solicitaçõesenquanto processa a solicitação corrente, o que poderia causaruma sensação de travamento ao usuário.

Outra aplicação para processos multithread é a animação deinterfaces. Nesse caso cria-se um ou mais threads para gerenciaras animações enquanto outros threads cuidam das outras tarefas,como por exemplo, a entrada de dados.

10 Java na prática

A rigor todas as aplicações acima como outras aplicações deprocessosmultithread podem ser executados por meio de processosmonothreads. No entanto, o tempo gasto na mudança de contexto1entre processos na maioria dos sistemas operacionais é muito maislenta que a simples alternância entre threads, uma vez que a maiorparte das informações contextuais são compartilhadas pelos thre-ads de um mesmo processo.

Mesmo que você não crie mais de um thread, todo processoJava possui vários threads: thread para garbage collection, threadpara monitoramento de eventos, thread para carga de imagens,etc.

2.1 Criando threads em JavaProcesso Multithread não é uma invenção da linguagem Java. Épossível criar processos multithread em quase todas as linguagensdo mercado, como C++ e Object Pascal. No entanto, Java foiprojetada para trabalhar com threads e incorporou threads ao nú-cleo básico da linguagem tornando, desta forma, mais natural oseu uso. Na verdade o uso de threads está tão intimamente ligadoa Java que é quase impossível escrever um programa útil que nãoseja multithread.

A classe Thread agrupa os recursos necessários para a criaçãode um thread. A forma mais simples de se criar um thread é criaruma classe derivada da classe Thread. Por exemplo:

class MeuThread extends Thread{

. . .}

É preciso também sobrescrever o método run() da classeThread. O método run() é o ponto de entrada do thread, da

1Mudança de Contexto (task switch): é o conjunto de operações necessá-rias para gravar o estado atual do processo corrente e recuperar o estado deoutro processo de modo a torná-lo o processo corrente.

Tópicos Avançados 11

mesma forma que o método main() é ponto de entrada de umaaplicação. O exemplo 2.1 mostra uma classe completa.

public class MeuThread extends Thread{

St r ing s ;public MeuThread ( St r ing as ){

super ( ) ;s = new St r ing ( as ) ;

}public void run ( ){

for ( int i = 0 ; i < 5 ; i++)System . out . p r i n t l n ( i+" "+s ) ;System . out . p r i n t l n ( "FIM ! "+s ) ;

}}

Exemplo 2.1 Subclasse da classe Thread.

No exemplo 2.1, o método run() contém o código que seráexecutado pelo thread. Ele possui um comando for que imprimecinco vezes o atributo s. Para iniciar a execução de um thread cria-se um objeto da classe e invoca-se o método start() do objeto.O método start() cria o thread e inicia sua execução pelo mé-todo run(). Se o método run() for chamado diretamente, entãonenhum thread novo será criado e o método run() será executadono thread corrente. O exemplo 2.2 mostra uma forma de se criarum thread usando a classe de�nida no exemplo 2.1.

public class TesteThread1{

public stat ic void main ( St r ing [ ] a rgs ){

new MeuThread ( "Linha1" ) . s t a r t ( ) ;}

}

Exemplo 2.2 Criação de um Thread.

12 Java na prática

No exemplo anterior apenas um thread, além do principal é cri-ado. Nada impede que sejam criados mais objetos da mesma classepara disparar um número maior de threads. O exemplo 2.3 mos-tra a execução de dois threads sobre dois objetos de uma mesmaclasse.

public class TesteThread2{

public stat ic void main ( St r ing [ ] a rgs ){

new MeuThread ( "Linha1" ) . s t a r t ( ) ;new MeuThread ( "Linha2" ) . s t a r t ( ) ;

}}

Exemplo 2.3 Criação de dois threads.Cada thread é executado sobre uma instância da classe e, por

consequência, sobre uma instância do método run(). A saída ge-rada pela execução do exemplo 2.3 depende do sistema operacionalsubjacente. Uma saída possível é a seguinte:

0 Linha20 Linha11 Linha21 Linha12 Linha22 Linha13 Linha23 Linha14 Linha24 Linha1FIM! Linha2FIM! Linha1

Esta saída mostra que os threads executam intercaladamente.No entanto, em alguns sistemas operacionais os threads do exem-plo 2.3 executariam um após o outro. A relação entre a sequênciade execução e o sistema operacional e dicas de como escrever pro-gramas multithread com sequência de execução independente deplataforma operacional serão nas seções 2.5 e 2.6.

Tópicos Avançados 13

2.1.1 Usando a interface RunnableAlgumas vezes não é possível criar uma subclasse da classe Threadporque a classe já deriva outra classe, por exemplo a classe Applet.Outras vezes, por questões de pureza de projeto, o projetista nãodeseja derivar a classe Thread simplesmente para poder criar umthread, uma vez que isto viola o signi�cado da relação de classe-subclasse. Para esses casos existe a interface Runnable. A in-terface Runnable possui apenas um método para ser implemen-tado: o método run(). Para criar um thread usando a interfaceRunnable é preciso criar um objeto da classe Thread, passandopara o construtor uma instância da classe que implementa a inter-face. Ao invocar o método start() do objeto da classe Thread,o thread criado inicia sua execução no método run() da instânciada classe que implementou a interface. O exemplo 2.4 mostra acriação de um thread usando a interface Runnable.

public class TesteThread2 implements Runnable{

private St r ing men ;public stat ic void main ( St r ing args [ ] ){

TesteThread2 ob1 = new TesteThread2 ( " o la " ) ;Thread t1 = new Thread ( ob1 ) ;t1 . s t a r t ( ) ;

}public TesteThread2 ( St r ing men) { this .men=men ; }public void run ( ){

for ( ; ; ) System . out . p r i n t l n (men ) ;}

}

Exemplo 2.4 Criação de um thread por meio da interfaceRunnable.

Note que agora ao invocarmos o método start() o threadcriado iniciará a execução sobre o método run() do objeto passadocomo parâmetro, e não sobre o método run() do objeto Thread.Nada impede que seja criado mais de um thread executando sobre

14 Java na prática

o mesmo objeto:

Thread t1 = new Thread ( ob1 ) ;Thread t2 = new Thread ( ob1 ) ;

Neste caso alguns cuidados devem ser tomados, uma vez queexiste o compartilhamento das variáveis do objeto por dois threads.Os problemas que podem advir de uma situação como esta serãotratados mais adiante.

2.2 A classe Thread

A classe Thread é extensa, possuindo vários construtores, métodose variáveis públicas. Aqui mostraremos apenas os mais usados.

Hierarquia

A classe Thread deriva diretamente da classe Object.

java.lang.Object|+--java.lang.Thread

Construtores

A tabela 2.1 mostra os principais construtores da classe Thread.Podemos notar que é possível nomear os threads e agrupá-los. Istoé útil para obter a referência de threads por meio do seu nome.

Tópicos Avançados 15

Construtor Descrição

Thread(ThreadGroup g, String nome)Cria um novo thread com onome especi�cado dentro dogrupo g.

Thread(Runnable ob, String nome)Cria um novo thread para exe-cutar sobre o objeto ob, com onome especi�cado.

Thread(ThreadGroup g,Runnable ob, String nome)

Cria um novo thread para exe-cutar sobre o objeto ob, dentrodo grupo g, com o nome espe-ci�cado.

Thread(String nome) Cria um novo thread com onome especi�cado.

Thread() Cria um novo thread com onome default.

Thread(Runnable ob) Cria um novo thread para exe-cutar sobre o objeto ob.

Thread(ThreadGroup g, Runnable ob)Cria um novo thread para exe-cutar sobre o objeto ob, dentrodo grupo g.

Tabela 2.1 Principais construtores da classe Thread.

Métodos

A tabela 2.2 apresenta os principais métodos da classe Thread.Alguns métodos muito usados nas versões anteriores do SDK1.2estão sendo descontinuados (deprecated) por serem consideradosinseguros ou com tendência a causarem deadlock2. Os métodosdescontinuados são: stop(), suspend() e resume().

2Travamento causado pela espera circular de recursos em um conjunto dethreads. O travamento por deadlock mais simples é o abraço mortal onde umthread A espera que um thread B libere um recurso, enquanto que o threadB só libera o recurso esperado por A se obter um recurso mantido por A.Desta forma os dois threads são impedidos inde�nidamente de prosseguir.

16 Java na prática

Método Descriçãostatic Thread currentThread() Retorna uma referência para o

thread corrente em execução.static int enumerate(Thread[] v) Copia para o array todos os th-

read ativos no grupo do thread.String getName() Obtém o nome do thread.int getPriority() Obtém a prioridade do thread.ThreadGroup getThreadGroup() Retorna o grupo do thread.void interrupt() Interrompe este thread.

void run()

Se o thread foi construídousando um objeto Runnableseparado então o método doobjeto Runnable é chamado.Caso contrário nada ocorre.

void setName(String name) Muda o nome do thread.void setPriority(int p) Muda a prioridade do thread.

static void sleep(long milis)Suspende o thread em execuçãoo número de milissegundos es-peci�cados.

static void sleep(long milis,int nanos)

Suspende o thread em execu-ção o número de milissegundosmais o número de nanossegun-dos especi�cados.

void start()Inicia a execução do thread. Amáquina virtual chama o mé-todo run() do thread.

static void yield()Faz com que o thread correnteinterrompa permitindo que ou-tro thread seja executado.

Tabela 2.2 Principais métodos da classe Thread.

Existem alguns métodos da classe Object que são importantespara o controle dos threads, como mostra a tabela 2.3. O leitorpode estar se perguntando porque métodos relacionados a threadsestão na superclasse Object que é �mãe� de todas as classe emJava. A razão disso é que esses métodos lidam com um elementoassociado a todo objeto e que é usado para promover o acessoexclusivo aos objetos. Esse elemento é chamado de monitor. Naseção 2.6 os monitores serão discutidos mais detalhadamente.

Tópicos Avançados 17

Método Descriçãovoid notify() Noti�ca um thread que está espe-

rando sobre um objeto.void notifyAll() Noti�ca todos os threads que estão

esperando sobre um objeto.void wait() Espera para ser noti�cado por ou-

tro thread.

void wait(long milis,int nanos)

Espera para ser noti�cado por ou-tro thread ou até que se passe otempo em milissegundos expressopor milis, adicionado ao tempoem nanossegundos expresso pornanos.

void wait(long milis)

Espera para ser noti�cado por ou-tro thread ou até que se passe otempo em milissegundos expressopor milis.

Tabela 2.3 Métodos da classe Object relacionados comthreads.

2.2.1 Variáveis públicasAs variáveis públicas da classe Thread de�nem valores máximo,mínimo e default para a prioridade de execução dos threads. Javaestabelece dez valores de prioridade. Como essas prioridades sãoassociadas às prioridades do ambiente operacional então, cadaimplementação de máquina virtual pode ter uma associação di-ferente, o que pode in�uenciar no resultado �nal da execução doprograma. Na seção 2.5 abordaremos a in�uência do ambienteoperacional na execução de programas multithread.

Método Descriçãostatic final int MAX_PRIORITY A prioridade máxima que um th-

read pode ter.static final int MIN_PRIORITY A prioridade mínima que um th-

read pode ter.static final int NORM_PRIORITY A prioridade default associado a

um thread.

Tabela 2.4 Variáveis públicas.

18 Java na prática

2.3 Ciclo de Vida dos ThreadsUm thread pode possuir quatro estados conforme mostra a �-gura 2.3. Podemos observar que uma vez ativo o thread alternaos estados em execução, suspenso e pronto até que passe parao estado morto. A transição de um estado para outro pode serdeterminada por uma chamada explícita a um método ou devidaa ocorrência de algum evento no nível de ambiente operacional oude programa.

Figura 2.3 Estados de um thread.

A transição de um thread do estado novo para algum estadoativo é sempre realizada pela invocação do método start() doobjeto Thread. Já as transições do estado em execução para oestado suspenso, do suspenso para o estado pronto e dessespara o estado morto podem ser disparadas tanto pela invocaçãode variados métodos como pela ocorrência de eventos. O exem-plo 2.5 mostra as ocorrências de transição em um código.

public class TesteThread3 extends Thread{

public TesteThread3 ( S t r ing s t r ) { super ( s t r ) ; }public void run ( ){

for ( int i = 0 ; i < 10 ; i++){

System . out . p r i n t l n ( i + " " + getName ( ) ) ;try {

// Comando para suspender o thread por

Tópicos Avançados 19

// 1000 mi l i segundos ( 1 segundo )// Transição do es tado em execução// para o es tado suspensos l e e p ( 1000 ) ;

} catch ( Inter ruptedExcept ion e ) {}// Evento : fim do tempo de suspensão// Transição do es tado em suspenso// para o es tado pronto e de s t e p/execução

}System . out . p r i n t l n ( "FIM ! " + getName ( ) ) ;// Evento : fim da execução do thread// Transição do es tado a t i v o suspenso para o// es tado morto

}public stat ic void main ( St r ing args [ ] ){

TesteThread3 t1 = new TesteThread3 ( args [ 0 ] ) ;t1 . s t a r t ( ) ; // Transição para um estado a t i v o

}}

Exemplo 2.5 Alguns comandos e eventos que acarretamtransição de estados.

2.3.1 sleep(), yield(), join(), stop(), suspend()e resume()

Agora que vimos os estados que podem ser assumidos por umthread em seu ciclo de vida vamos examinar mais detalhadamentealguns dos métodos responsáveis pela mudança de estado de umthread.

sleepO método sleep() é um método estático e possui as seguintesinterfaces:

static void sleep(long ms)throws InterruptedException

ou

20 Java na prática

static void sleep(long ms, int ns)throws InterruptedException

onde ms é um valor em milissegundos e ns é um valor em nanos-segundos.

O método sleep() faz com que o thread seja suspensopor um determinado tempo, permitindo que outros threadssejam executados. Como o método pode lançar a exceçãoInterruptedException, é preciso envolver a chamada em umbloco try/catch ou propagar a exceção. O exemplo 2.6 de�neuma espera mínima de 100 milisegundos entre cada volta da ite-ração. Note que o tempo de suspensão do thread pode ser maiorque o especi�cado, uma vez que outros threads de maior ou mesmode igual prioridade podem estar sendo executados no momento emque expira o tempo de suspensão solicitado.

public class ThreadComSleep extends Thread{

St r ing s ;public ThreadComSleep ( St r ing as ){

super ( ) ;s = new St r ing ( as ) ;

}public void run ( ){

for ( int i = 0 ; i < 5 ; i++){

System . out . p r i n t l n ( i+" "+s ) ;try{

Thread . s l e e p ( 1 0 0 ) ;catch ( Inter ruptedExcept ion e ){}

}System . out . p r i n t l n ( "FIM ! "+s ) ;

}}

Exemplo 2.6 Uso do método sleep().

Outro problema com o sleep() é que a maioria dos sistemasoperacionais não suportam resolução de nanossegundos. Mesmo a

Tópicos Avançados 21

resolução na unidade de milissegundo não é suportada em algunssistemas operacionais. No caso do sistema operacional não supor-tar a resolução de tempo solicitada, o tempo será arredondadopara a nível de resolução suportado pela plataforma operacional.

yield

O método yield() é um método estático com a seguinte interface:

static void yield()

Uma chamada ao método yield() faz com que o thread cor-rente libere automaticamente a CPU (Central Processing Unit)para outro thread de mesma prioridade. Se não houver nenhumoutro thread de mesma prioridade aguardando, então o thread cor-rente mantém a posse da CPU. O exemplo 2.7 altera o exemplo 2.1de modo a permitir que outros threads de mesma prioridade sejamexecutados a cada volta da iteração.

public class ThreadComYield extends Thread{

St r ing s ;public ThreadComYield ( S t r ing as ){

super ( ) ;s = new St r ing ( as ) ;

}public void run ( ){

for ( int i = 0 ; i < 5 ; i++){

System . out . p r i n t l n ( i+" "+s ) ;Thread . y i e l d ( ) ;

}System . out . p r i n t l n ( "FIM ! "+s ) ;

}}

Exemplo 2.7 Uso do método yield().

22 Java na prática

joinO método join() é um método de instância da classe Thread e éutilizado quando existe a necessidade do thread corrente esperarpelo término da execução de outro thread. As versões do métodojoin() são as seguintes:

public final void join();public final void join(long millisecond);public final void join(long millisecond, intnanosecond);

Na primeira versão o thread corrente espera inde�nidamentepelo encerramento da execução do segundo thread. Na segunda eterceira versão o thread corrente espera pelo término da execuçãodo segundo thread até no máximo um período de tempo pre�xado.O exemplo 2.8 mostra como usar o método join().

class ThreadComJoin extends Thread{

St r ing s ;public ThreadComJoin ( St r ing as ){

super ( ) ;s = new St r ing ( as ) ;

}public void run ( ){

for ( int i = 0 ; i < 10 ; i++)System . out . p r i n t l n ( i+" "+s ) ;

System . out . p r i n t l n ( "Fim do thread ! " ) ;}

}

public c lass TestaJoin{

public stat ic void main ( St r ing args [ ] ){

ThreadComJoin t1 = new ThreadComJoin ( args [ 0 ] ) ;t1 . s t a r t ( ) ; // Transição para um estado a t i vot1 . j o i n ( ) ; // Espera pe lo término do threadSystem . out . p r i n t l n ( "Fim do programa ! " ) ;

}}

Exemplo 2.8 Uso do método join().

Tópicos Avançados 23

stop, suspend e resumeA partir da versão 1.2 do SDK os métodos stop(), suspend() eresume() tornaram-se deprecated (serão descontinuados) uma vezque a utilização desses métodos tendia a gerar erros. No entanto,devido a grande quantidade de código que ainda utiliza estes mé-todos, acreditamos que seja importante mencioná-los.

O método stop() é um método de instância que encerra aexecução do thread ao qual pertence. Os recursos alocados aothread são liberados. É recomendável substituir o método stop()pelo simples retorno do método run().

O método suspend() é um método de instância que suspendea execução do thread ao qual pertence. Nenhum recurso é libe-rado, inclusive os monitores que possua no momento da suspensão(os monitores serão vistos na seção 2.6 e servem para controlar oacesso à variáveis compartilhadas). Isto faz com que o métodosuspend() tenda a ocasionar deadlocks.

O método resume() é um método de instância que reassumea execução do thread ao qual pertence. Os métodos suspend() eresume() devem ser substituídos respectivamente pelos métodoswait() e notify(), como veremos na seção 2.6.

2.4 Daemon ThreadsDaemon threads são threads que rodam em background com afunção de prover algum serviço mas não fazem parte do propósitoprincipal do programa. Quando só existem threads do tipo dae-mon o programa é encerrado. Um exemplo de daemon é o threadpara coleta de lixo.

Um thread é de�nido como daemon por meio do método deinstância setDaemon(). Para veri�car se um thread é um daemoné usado o método de instância isDaemon(). O exemplo 2.9 mostracomo usar esses métodos.

import java . i o . ∗ ;class ThreadDaemon extends Thread{

public ThreadDaemon ( )

24 Java na prática

{setDaemon ( true ) ;s t a r t ( ) ;

}public void run ( ){

for ( ; ; ) y i e l d ( ) ;}

}

public class TestaDaemon{

public stat ic void main ( St r ing [ ] a rgs ){

Thread d = new ThreadDaemon ( ) ;System . out . p r i n t l n ( "d . isDaemon () = "+

d . isDaemon ( ) ) ;BufferedReader s td in = new BufferedReader (

new InputStreamReader ( System . in ) ) ;System . out . p r i n t l n ( " D ig i t e qualquer c o i s a " ) ;try{

s td in . readLine ( ) ;} catch ( IOException e ) {}

}}

Exemplo 2.9 Uso dos métodos relacionados com daemons.

No exemplo 2.9 o método main() da classe TestaDaemoncria um objeto da classe ThreadDaemon. O construtor da classeThreadDaemon de�ne o thread como daemon por meio do métodosetDaemon() e inicia a execução do thread. Como é apenas umthread de demonstração o método run() da classe ThreadDaemonnão faz nada, apenas liberando a posse da CPU toda vez quea adquire. Após a criação da instância da classe ThreadDaemonno método main() o objeto é testado para veri�car se é um da-emon, utilizando para esse �m o método isDaemon(). Depoisdisso o programa simplesmente espera o usuário pressionar a te-cla <enter>. O programa termina logo após o acionamento datecla, mostrando dessa forma que o programa permanece ativoapenas enquanto existem threads não daemons ativos.

Tópicos Avançados 25

2.5 A In�uência do Sistema Operacionalsobre os Threads

Apesar da linguagem Java prometer a construção de programasindependentes de plataforma operacional, o comportamento dosthreads pode ser fortemente in�uenciado pelo sistema operacionalsubjacente. Portanto, o programador deve tomar alguns cuidadosse deseja construir programas que funcionem da mesma forma,independente do ambiente onde serão executados.

Alguns sistemas operacionais não oferecem suporte a execuçãode threads. Neste caso, cada processo possui apenas um thread.Mesmo em sistemas operacionais que oferecem suporte a execuçãode múltiplos threads por processo, o projetista da máquina virtualpode optar por não usar o suporte nativo a threads. Deste modo,é responsabilidade da máquina virtual criar um ambiente multith-read. Threads implementados desta forma, no nível de usuário,são chamados de green-threads. As in�uências da plataforma ope-racional podem ser agrupadas em dois tipos:

1. Forma de escalonamento de threads. O ambiente podeadotar um escalonamento não preemptivo ou preemptivo.No escalonamento não preemptivo (também chamado de co-operativo) um thread em execução só perde o controle daCPU se a liberar voluntariamente ou se necessitar de algumrecurso que ainda não está disponível. Já no escalonamentopreemptivo, além das formas acima, um thread pode per-der o controle da CPU por eventos externos, como o �mdo tempo máximo de�nido pelo ambiente para a execuçãocontínua de um thread (fatia de tempo) ou porque um th-read de mais alta prioridade está pronto para ser executado.Exemplos de sistemas operacionais não preemptivos são MS-Windows 3.1 e IBM OS/2. Exemplos de sistemas operacio-nais preemptivos são MS-Windows 95/98/2000/XP, Linux,QNX e muitos outros. Alguns sistemas operacionais ado-tam uma abordagem híbrida, suportando tanto o modelocooperativo como o preemptivo, como o Solaris da Sun.

2. Relacionamento entre os níveis de prioridades de�-

26 Java na prática

nidas na linguagem Java e os níveis de prioridadesde�nidas nos Sistemas Operacionais. Em um SO pre-emptivo um thread de uma determinada prioridade perde aposse da CPU para um thread de prioridade mais alta queesteja pronto para ser executado. A linguagem Java prevêdez níveis de prioridades que podem ser atribuídas aos th-reads. No entanto, cada SO possui um número de priorida-des diferente e o mapeamento das prioridades da linguagemJava para as prioridades do SO subjacente pode in�uenciaro comportamento do programa.

2.5.1 Forma de escalonamento de threadsA especi�cação da máquina virtual Java determina que a formade escalonamento de threads seja preemptiva. Portanto, mesmoem ambientes operacionais cooperativos a máquina virtual devegarantir um escalonamento preemptivo. No entanto, um esca-lonamento preemptivo não obriga a preempção por �m de fatiade tempo. Podemos ter um escalonamento preemptivo onde umthread de mais alta prioridade interrompe o thread que detema posse da CPU mas não existe preempção por �m de fatia detempo. Um escalonamento onde threads de mesma prioridade in-tercalam a posse da CPU por força do �m da fatia de tempo échamado de escalonamento Round-Robin. A especi�cação da má-quina virtual Java não prevê o escalonamento Round-Robin, mastambém não o descarta, abrindo a possibilidade de implementa-ções distintas de máquinas virtuais e introduzindo o não deter-minismo na execução de programas multithread. O exemplo 2.3poderia ter uma saída distinta da apresentada anteriormente casoseja executado por uma máquina virtual que não implementa oescalonamento Round-Robin. Nesse caso a saída seria a seguinte:

0 Linha21 Linha22 Linha23 Linha24 Linha2FIM! Linha20 Linha1

Tópicos Avançados 27

1 Linha12 Linha13 Linha14 Linha1FIM! Linha1

Neste caso, se o programador deseja que a execução de threadsse processe de forma alternada, independentemente da implemen-tação da máquina virtual, então é necessário que ele insira códigopara a liberação voluntária da CPU. Isso pode ser feito com ométodo yield() ou com o método sleep().

2.5.2 Relacionamento entre os níveis de prio-ridades de�nidas na linguagem Java e osníveis de prioridades de�nidas nos Siste-mas Operacionais

Como já dissemos a linguagem Java prevê dez níveis de priorida-des que podem ser atribuídas aos threads. Na verdade são onzeprioridades, mas a prioridade de nível 0 é reservada para threadsinternos. As prioridades atribuídas aos threads são estáticas, ouseja não se alteram ao longo da vida do thread, a não ser que pormeio de chamadas a métodos de�nidos para esse propósito. Aclasse thread possui variáveis públicas �nais com valores de prio-ridade prede�nidos, como mostrado na tabela 2.4. No entanto, ossistemas operacionais podem possuir um número maior ou menorde níveis de prioridades. Vamos citar um exemplo: o MSWin-dows 9x/NT. Este sistema possui apenas sete níveis de priorida-des e estes sete níveis devem ser mapeados para os onze níveisde prioridades especi�cados em Java. Cada máquina virtual faráeste mapeamento de modo diferente, porém a implementação maiscomum é mostrada na tabela 2.5.

Note que, nesta implementação, níveis de prioridades diferen-tes em Java serão mapeados para um mesmo nível de prioridadeem MSWindows. Isto pode levar a resultados inesperados caso oprogramador projete uma aplicação esperando, por exemplo, queum thread de prioridade 4 irá interromper um thread de priori-dade 3. Para evitar este tipo de problema o programador pode

28 Java na prática

adotar dois tipos de abordagem:

1. utilizar, se for possível, apenas as prioridadesThread.MIN_PRIORITY, Thread.NORM_PRIORITY eThread.MAX_PRIORITY para atribuir prioridades aosthreads; ou

2. não se basear em níveis de prioridades para de�nir o escalo-namento de threads, utilizando, alternativamente, primitivasde sincronização que serão abordadas na próxima seção.

Prioridades Java Prioridades MSWindows0 THREAD_PRIORITY_IDLE1(Thread.MIN_PRIORITY) THREAD_PRIORITY_LOWEST2 THREAD_PRIORITY_LOWEST3 THREAD_PRIORITY_BELOW_NORMAL4 THREAD_PRIORITY_BELOW_NORMAL5(Thread.NORM_PRIORITY) THREAD_PRIORITY_NORMAL6 THREAD_PRIORITY_ABOVE_NORMAL7 THREAD_PRIORITY_ABOVE_NORMAL8 THREAD_PRIORITY_BELOW_HIGHEST9 THREAD_PRIORITY_BELOW_HIGHEST10(Thread.MAX_PRIORITY) THREAD_TIME_CRITICAL

Tabela 2.5 Mapeamento das prioridades de Java paraMSWindows.

2.6 Compartilhamento de Memória eSincronização

Como já foi dito, mais de um thread pode ser criado sobre ummesmo objeto. Neste caso, cuidados especiais devem ser tomados,uma vez que os threads compartilham as mesmas variáveis e pro-blemas podem surgir se um thread está atualizando uma variávelenquanto outro thread está lendo ou atualizando a mesma variá-vel. Este problema pode ocorrer mesmo em threads que executamsobre objetos distintos, já que os objetos podem possuir referên-cias para um mesmo objeto. O exemplo 2.10 mostra a execução de

Tópicos Avançados 29

dois threads sobre um mesmo objeto. O nome do thread é usadopara que o thread decida que ação tomar. O thread de nome �um�cria um número de 0 a 1000 gerado aleatoriamente e o coloca naposição inicial de um array de dez posições. As outras posiçõesdo array são preenchidas com os nove números inteiros seguintesao número inicial. O thread de nome �dois� imprime o conteúdodo vetor. A intenção inicial do projetista é obter na tela sequên-cias de dez números inteiros consecutivos iniciados aleatoriamente.No entanto, como os dois threads compartilham o mesmo objetoe não existe qualquer sincronismo entre eles, é pouco provável queo projetista obtenha o resultado esperado.

public class CalcDez implements Runnable{

private int ve t In t [ ] ;

public CalcDez ( ) { ve t In t=new int [ 1 0 ] ; }public void run ( ){

i f( Thread . currentThread ( ) . getName ( ) . equa l s ( "um" ) )

for ( ; ; ){

ve t In t [ 0 ] = ( int ) (Math . random ( ) ∗ 1 0 0 0 ) ;for ( int i =1; i <10; i++)

ve t In t [ i ]= ve t In t [0 ]+ i ;}else

for ( ; ; ){

System . out . p r i n t l n ( " S e r i e i n i c i a d a por"+vet In t [ 0 ] ) ;

for ( int i =1; i <10; i++)System . out . p r i n t l n ( ve t In t [ i ]+ " " ) ;

}

}public stat ic void main ( St r ing args [ ] ){

CalcDez ob = new CalcDez ( ) ;Thread t1 = new Thread (ob , "um" ) ;Thread t2 = new Thread (ob , " do i s " ) ;t1 . s t a r t ( ) ;t2 . s t a r t ( ) ;

30 Java na prática

}}

Exemplo 2.10 Dois threads executando sobre o mesmo objeto.

Se a máquina virtual não implementar um escalonamentoRound-Robin apenas um thread será executado, visto que os doisthreads possuem a mesma prioridade. Já no caso da máquina vir-tual implementar um escalonamento Round-Robin a alternânciada execução dos threads produzirá resultados imprevisíveis. Umtrecho de uma das saídas possíveis pode ser visto na �gura 2.4.Ele foi obtido em Pentium 100MHz executando a máquina virtualda Sun, versão 1.2, sob o sistema operacional MSWindows 95.

258259Serie iniciada por573574575576577578579580581582Serie iniciada por808182

Figura 2.4 Saída do exemplo 2.10.

Podemos notar as sequências estão misturadas, mostrando quecada thread interrompe o outro no meio da execução da tarefaespeci�cada. O mesmo problema pode ocorrer mesmo em threadsque executam sobre objetos diferentes, bastando que cada threadpossua uma referência para um mesmo objeto. O exemplo 2.11mostra a execução de dois threads sobre objetos distintos.

Tópicos Avançados 31

class Compartilhada{

private int ve t In t [ ] ;public Compartilhada ( ) { ve t In t=new int [ 1 0 ] ; }public void setVal ( ){

for ( ; ; ){

ve t In t [ 0 ] = ( int ) (Math . random ( ) ∗ 1 0 0 0 ) ;for ( int i =1; i <10; i++)

vet In t [ i ]= ve t In t [0 ]+ i ;}

}public int getVal ( int i ) { return ve t In t [ i ] ; }

}

public c lass CalcDez2 extends Thread{

private Compartilhada obj ;private int t ipo ;

public CalcDez2 ( Compartilhada aObj , int aTipo ){ obj = aObj ; t i po = aTipo ; }

public void run ( ){

for ( ; ; )i f ( t i po==1) obj . se tVal ( ) ;else{

System . out . p r i n t l n ( " S e r i e i n i c i a d a por"+obj . getVal ( 0 ) ) ;

for ( int i =1; i <10; i++)System . out . p r i n t l n ( obj . getVal ( i )+ " " ) ;

}}public stat ic void main ( St r ing args [ ] ){

Compartilhada obj = new Compartilhada ( ) ;CalcDez2 t1 = new CalcDez2 ( obj , 1 ) ;CalcDez2 t2 = new CalcDez2 ( obj , 2 ) ;t1 . s t a r t ( ) ;t2 . s t a r t ( ) ;

}}

Exemplo 2.11 Dois threads executando sobre objetos distintos.

É importante que o leitor não confunda o exemplo 2.11 como exemplo 2.10 achando que nos dois exemplos os dois threadsexecutam sobre o mesmo objeto, uma vez que a etapa da cria-ção dos threads é bem parecida. No entanto, no exemplo 2.11

32 Java na prática

foi declarada uma subclasse da classe Thread e não uma classeque implementa a interface Runnable. Apesar de parecer que noexemplo 2.11 ambos os threads executam sobre um mesmo objetoda classe Compartilhada que é passado como argumento, na ver-dade cada thread executará sobre sua própria instância da classeCalcDez2, sendo que o objeto da classe Compartilhada é referen-ciado pelos dois threads. O comportamento do código do exem-plo 2.11 é semelhante ao do exemplo 2.10, com a diferença queno primeiro a sequência de inteiros é encapsulada pelo objeto daclasse Compartilhada.

Este tipo de situação, onde o resultado de uma computaçãodepende da forma como os threads são escalonados, é chamadode condições de corrida (Race Conditions). É um problema a serevitado uma vez que o programa passa a ter um comportamentonão determinístico.

2.6.1 Atomicidade de Instruções e Sincroniza-ção do Acesso à Sessões Críticas

A condição de corrida ocorre porque o acesso a áreas de memó-ria compartilhadas é feito de forma não atômica, e de forma nãoexclusiva. Por forma não atômica queremos dizer que o acesso éfeito por meio de várias instruções e pode ser interrompido poroutro thread antes que todas as instruções, que fazem parte doacesso, sejam executadas. Por forma não exclusiva queremos di-zer que um thread pode consultar/atualizar um objeto durante aconsulta/atualização do mesmo objeto por outros threads. Pou-cas operações são atômicas em Java. Em geral, as atribuiçõessimples, com exceção dos tipos long e double, são atômicas, deforma que o programador não precisa se preocupar em ser inter-rompido no meio de uma operação de atribuição. No entanto, nocaso de operações mais complexas sobre variáveis compartilhadasé preciso que o programador garanta o acesso exclusivo a essasvariáveis. Os trechos de código onde são feitos os acessos às variá-veis compartilhadas são chamados de Seções Críticas ou RegiõesCríticas.

Uma vez determinada uma região crítica como garantir oacesso exclusivo? A linguagem Java permite que o programador

Tópicos Avançados 33

garanta o acesso exclusivo utilizando o conceito de monitor . Oconceito de monitor foi proposto por C. A. R. Hoare em 1974 epode ser encarado como um objeto que garante a exclusão mútuana execução dos procedimentos a ele associados. Ou seja, apenasum procedimento associado ao monitor pode ser executado em umdeterminado momento. Por exemplo, suponha que dois procedi-mentos A e B estão associados a um monitor. Se no momentoda invocação do procedimento A o procedimento B estiver sendoexecutado, então o processo ou thread que invocou o procedimentoA �ca suspenso até o término da execução do procedimento B.Ao término do procedimento B o processo que invocou o procedi-mento A é �acordado� e sua execução retomada.

Figura 2.5 Uma possível sequência na disputa de dois threadspela autorização de um monitor.

O uso de monitores em Java é uma variação do proposto por

34 Java na prática

Hoare. Na linguagem Java todo objeto possui um monitor asso-ciado. Para facilitar o entendimento podemos encarar o monitorcomo detentor de um �passe�. Todo thread pode pedir �empres-tado� o passe ao monitor de um objeto antes de realizar algumacomputação. Como o monitor possui apenas um passe, apenasum thread pode adquirir o passe em um determinado instante.O passe tem que ser devolvido para o monitor para possibilitaro empréstimo do passe a outro thread. A �gura 2.5 ilustra essaanalogia.

Nos resta saber como solicitar o passe ao monitor. Isto é feitopor meio da palavra chave synchronized. Existem duas formasde se usar a palavra chave synchronized: na declaração de mé-todos e no início de blocos. O exemplo 2.12 mostra duas versõesda classe FilaCirc que implementa uma �la circular de valoresinteiros: uma com métodos synchronized e outra com blocossynchronized. Um objeto desta classe pode ser compartilhadopor dois ou mais threads para implementar o exemplo clássico deconcorrência do tipo produtor/consumidor.

A palavra chave synchronized na frente dos métodos de ins-tância signi�ca que o método será executado se puder adquirir omonitor do objeto a quem pertence o método3. Caso contrário, othread que invocou o método será suspenso até que possa adquiriro monitor. Esta forma de sincronização é abordada no exem-plo 2.12 a. Portanto, se algum thread chamar algum método deum objeto da classe FilaCirc nenhum outro thread que compar-tilha o mesmo objeto poderá executar um método do objeto atéque o método chamado pelo primeiro thread termine. Caso outrothread invoque um método do mesmo objeto �cará bloqueado até

3Não usaremos mais a analogia com a aquisição do passe do monitor. Elafoi usada apenas para facilitar o entendimento do leitor. Quando se tratade monitores os termos mais usados são: �adquirir o monitor� e �liberar omonitor�.

Tópicos Avançados 35

que possa adquirir o monitor.

a) Versão com métodos synchronized

class FilaCirc{private final int TAM = 10;private int vetInt[];private int inicio, total;

public FilaCirc(){

vetInt=new int[TAM];inicio=0;total =0;

}public synchronized

void addElement(int v)throws Exception

{if (total == TAM) throw new

Exception("Fila cheia!");vetInt[(inicio+total)%TAM] = v;total++;

}public synchronized

int getElement()throws Exception

{if (total == 0 ) throw newException("Fila vazia!");

int temp = vetInt[inicio];inicio = (++inicio)%TAM;total--;return temp;

}}

b) Versão com blocos synchronized

class FilaCirc{private final int TAM = 10;private int vetInt[];private int inicio, total;

public FilaCirc(){

vetInt=new int[TAM];inicio=0;total =0;

}public void addElement(int v)

throws Exception{

synchronized(this) {if (total == TAM) throw new

Exception("Fila cheia!");vetInt[(inicio+total)%TAM] = v;total++;

}}public int getElement()

throws Exception{

synchronized(this) {if (total == 0 ) throw new

Exception("Fila vazia!");int temp = vetInt[inicio];inicio = (++inicio)%TAM;total--;

}return temp;

}}

Exemplo 2.12 Duas versões de uma classe que implementauma �la circular de inteiros.

O leitor pode estar se perguntando sobre a necessidade de sin-cronizar os métodos da classe FilaCirc uma vez que ocorremapenas atribuições simples a elementos individuais de um vetor eas atribuições de inteiros são atômicas. De fato o problema ocorrenão na atribuição dos elementos e sim na indexação do array. Porexemplo, a instrução

inicio = (++inicio)%TAM;

36 Java na prática

do método getElement() não é atômica. Suponha que os méto-dos da classe FilaCirc não sejam sincronizados e que as variáveisinicio e total possuam os valores 9 e 1 respectivamente. Su-ponha também que thread invocou o método getElement() e foiinterrompido na linha de código mostrada acima após o incre-mento da variável inicio mas antes da conclusão da linha decódigo. Nesse caso o valor de inicio é 10. Se neste instanteoutro thread executar o método getElement() do mesmo objetoocorrerá uma exceção IndexOutOfBoundsException ao atingir alinha de código

int temp = vetInt[inicio];

se alterarmos a linha de código para

inicio = (inicio+1)%TAM;

evitaremos a exceção, mas não evitaremos o problema de retornarmais de uma vez o mesmo elemento. Por exemplo, se um threadfor interrompido no mesmo local do caso anterior, outro threadpode obter o mesmo elemento, uma vez que os valores de inicioe total não foram alterados. Na verdade, o número de situa-ções problemáticas, mesmo para esse exemplo pequeno, é enormee perderíamos muito tempo se tentássemos descrevê-las em suatotalidade.

Em alguns casos pode ser indesejável sincronizar todo um mé-todo, ou pode-se desejar adquirir o monitor de outro objeto, di-ferente daquele a quem pertence o método. Isto pode ser feitousando a palavra chave synchronized na frente de blocos. Estaforma de sincronização é mostrada no exemplo 2.12 b. Nestemodo de usar a palavra-chave synchronized é necessário indicaro objeto do qual se tentará adquirir o monitor. Caso o monitorseja adquirido, o bloco é executado, caso contrário o thread é sus-penso até que possa adquirir o monitor. O monitor é liberado no�nal do bloco.

No exemplo 2.12 b, o monitor usado na sincronização é odo próprio objeto do método, indicado pela palavra chave this.Qualquer outro objeto referenciável no contexto poderia ser usado.

Tópicos Avançados 37

O que importa é que os grupos de threads, que possuam áreas decódigo que necessitam de exclusão mútua, usem o mesmo objeto.

No exemplo 2.12 não existe vantagem da forma de implemen-tação a) sobre a forma de implementação b) ou vice-versa. Issoocorre, principalmente, quando os métodos são muito pequenosou quando não realizam computações muito complexas. No en-tanto, se o método for muito longo ou levar muito tempo para serexecutado, sincronizar todo o método pode �travar� em demasia aexecução da aplicação. Nesses casos, a sincronização somente dasseções críticas é mais indicada. Outra vantagem da segunda formade sincronização é a liberdade no uso de monitores de qualquerobjeto referenciável. Isto permite a implementação sincronizaçõesmais complexas como veremos mais adiante. O exemplo 2.13 mos-tra como pode ser usado um objeto da classe FilaCirc.

public class TestaF i l aC i r c extends Thread{

private Fi l aC i r c obj ;private int t i po ;

public TestaF i l aC i r c ( F i l aC i r c aObj , int aTipo ){ obj = aObj ; t i po = aTipo ; }

public void run ( ){

for ( ; ; )try{

i f ( t i po==1){

int i = ( int ) (Math . random ( ) ∗ 1 0 0 0 ) ;System . out . p r i n t l n ( "Elemento gerado : "+i ) ;obj . addElement ( i ) ;

}else System . out . p r i n t l n ( "Elemento obt ido : "+

obj . getElement ( ) ) ;} catch ( Exception e ){System . out . p r i n t l n ( e . getMessage ( ) ) ; }

}public stat ic void main ( St r ing args [ ] ){

F i l aC i r c obj = new Fi l aC i r c ( ) ;Tes taF i l aC i r c t1 = new TestaF i l aC i r c ( obj , 1 ) ;Tes taF i l aC i r c t2 = new TestaF i l aC i r c ( obj , 2 ) ;

38 Java na prática

t1 . s t a r t ( ) ;t2 . s t a r t ( ) ;

}}

Exemplo 2.13 Uso da �la circular de inteiros.

Um trecho possível da saída obtida na execução do programado exemplo 2.13 seria o seguinte:

...Elemento obtido:154Elemento gerado:725Fila vazia!Elemento gerado:801Elemento obtido:725Elemento gerado:204Elemento obtido:801...

É importante observar que o monitor em Java por si só nãoimplementa a exclusão mútua. Ele é apenas um recurso que podeser usado pelo programador para implementar o acesso exclusivoà variáveis compartilhadas. Cabe ao programador a responsabili-dade pelo uso adequado deste recurso. Por exemplo, se o progra-mador esquecer de sincronizar um bloco ou método que necessitede exclusão mútua, de nada adiantará ter sincronizado os outrosmétodos ou blocos. O thread que executar o trecho não sincroni-zado não tentará adquirir o monitor e, portanto, de nada adiantaoutro thread te-lo o adquirido.

Outro ponto importante é usar a palavra chave synchronizedcom muito cuidado. A sincronização custa muito caro em se tra-tando de ciclos de CPU. A chamada de um método sincronizado épor volta de 10 vezes mais lenta do que a chamada de um métodonão sincronizado. Por essa razão use sempre a seguinte regra: nãosincronize o que não for preciso.

Tópicos Avançados 39

2.6.2 Comunicação entre Threads: wait() e no-tify()

O exemplo 2.12 não é um modelo de uma boa implementação deprograma. O thread que adiciona elementos à �la tenta adicionarum elemento a cada volta do laço de iteração, mesmo que a �laesteja cheia. Por outro lado, o thread que retira os elementos da�la tenta obter um elemento a cada volta do laço de iteração,mesmo que a �la esteja vazia. Isto é um desperdício de tempo deprocessador e pode tornar o programa bastante ine�ciente.

Alguém poderia pensar em uma solução onde o thread testariase a condição desejada para o processamento ocorre. Caso a condi-ção não ocorra o thread poderia executar o método sleep() para�car suspenso por algum tempo para depois testar novamente acondição. O thread procederia desta forma até que a condiçãofosse satisfeita. Este tipo de procedimento economizaria algunsciclos de CPU, evitando a tentativa incessante de se executar oprocedimento mesmo quando não há condições. O nome destaforma de ação, onde o procedimento, a cada intervalo de tempopré-determinado testa se uma condição é satisfeita, é chamado deespera ocupada (pooling ou busy wait).

No entanto, existem alguns problemas com este tipo de aborda-gem. Primeiramente, apesar da economia de ciclos de CPU aindaexiste a possibilidade de ine�ciência, principalmente se o temponão for bem ajustado. Se o tempo for muito curto ocorrerá váriostestes inúteis. Se for muito longo, o thread �cará suspenso alémdo tempo necessário. Porém, mais grave que isto é que o métodosleep() não faz o thread liberar o monitor. Portanto, se o trechode código for uma região sincronizada, como é o caso do exem-plo 2.12, de nada adiantará o thread ser suspenso. O thread que écapaz de realizar a computação que satisfaz a condição esperadapelo primeiro thread �cará impedido de entrar na região crítica,ocorrendo assim um deadlock : o thread que detém o monitor es-pera que a condição seja satisfeita e o thread que pode satisfazer acondição não pode prosseguir porque não pode adquirir o monitor.

O que precisamos é um tipo de comunicação entre threads quesinalize que certas condições foram satisfeitas. Além disso, é pre-ciso que, ao esperar por determinada condição, o thread libere

40 Java na prática

o monitor. Esta forma de interação entre threads é obtida emJava com o uso dos métodos de instância wait(), notify() enotifyAll(). Como vimos anteriormente, esses métodos perten-cem à classe Object e não à classe Thread. Isto ocorre porqueesses métodos atuam sobre os monitores, que são objetos relacio-nados a cada instância de uma classe Java e não sobre os threads.

Ao invocar o método wait() de um objeto, o thread é suspensoe inserido na �la do monitor do objeto, permanecendo na �la atéreceber uma noti�cação. Cada monitor possui sua própria �la.Ao invocar o método notify() de um objeto, um thread que estána �la do monitor do objeto é noti�cado. Ao invocar o métodonotifyAll() de um objeto, todos os threads que estão na �la domonitor do objeto são noti�cados.

a)class X{...public synchronized

int mx(){

...// Espera uma condiçãowhile(!cond) wait();// Prossegue com a// condição satisfeita...

}...

}

b)class Y{

X ob;...public int my(){...synchronized (ob){// Notifica algum// threadob.notify();

...}...

}

c)class Z{X ob;...public int mz(){

...synchronized (ob){// Notifica todos// os threads que// esperam na fila// do monitor de obob.notifyAll();...

}...

}

Exemplo 2.14 Exemplos de chamadas dos métodos wait(),notify() e notifyAll().

A única exigência é que esses métodos sejam invocados em umthread que detenha a posse do monitor do objeto associado aométodo. Essa exigência faz sentido uma vez que eles sinalizama threads que esperam na �la desses monitores. Devido a essaexigência, a invocação desses métodos ocorre em métodos ou blo-cos sincronizados. O exemplo 2.14 mostra as formas mais comunsde chamadas desses métodos. Note que o thread deve possuir o

Tópicos Avançados 41

monitor do objeto ao qual pertence o método. Por isso, nos exem-plos 2.14 b e 2.14 c, o objeto sincronizado no bloco é o mesmo queinvoca os métodos notify() e notifyAll().

Outra observação importante é que o thread que invoca o mé-todo wait() o faz dentro de um laço sobre a condição de espera.Isto ocorre porque apesar de ter sido noti�cado isto não asseguraque a condição está satisfeita. O thread pode ter sido noti�cadopor outra razão ou, entre a noti�cação e a retomada da execuçãodo thread, a condição pode ter sido novamente alterada.

Uma vez noti�cado o thread não retoma imediatamente a exe-cução. É preciso primeiro retomar a posse do monitor que nomomento da noti�cação pertence ao thread que noti�cou. Mesmoapós a liberação do monitor nada garante que o thread noti�cadoganhe a posse do monitor. Outros threads podem ter solicitado aposse do monitor e terem preferência na sua obtenção.

O exemplo 2.14 mostra apenas um esquema para uso dos mé-todos para noti�cação. O exemplo 2.15 é uma versão do exem-plo 2.12 a que usa os métodos de noti�cação para evitar problemascomo a espera ocupada. O exemplo 2.13 pode ser usado sem mo-di�cações para testar essa versão.

class Fi l aC i r c{

private f ina l int TAM = 10;private int ve t In t [ ] ;private int i n i c i o , t o t a l ;

public Fi l aC i r c ( ){

ve t In t=new int [TAM] ;i n i c i o =0;t o t a l =0;

}public synchronized void addElement ( int v )

throws Exception{

while ( t o t a l == TAM) wait ( ) ;v e t In t [ ( i n i c i o+t o t a l )%TAM] = v ;t o t a l++;no t i f y ( ) ;

}public synchronized int getElement ( )

42 Java na prática

throws Exception{

while ( t o t a l == 0 ) wait ( ) ;int temp = vet In t [ i n i c i o ] ;i n i c i o = (++ i n i c i o )%TAM;to ta l −−;n o t i f y ( ) ;return temp ;

}}

Exemplo 2.15 Classe que implementa uma �la circular deinteiros com noti�cação.

Figura 2.6 Uma possível sequência na execução de três threads.

A necessidade de se testar a condição em um comando derepetição pode ser observada na �gura 2.6 que mostra a evolu-ção da execução de três threads sobre objetos que compartilhamuma instância da classe FilaCirc. O thread 3 executa o métodoaddElement(), no entanto, em virtude da condição total==TAM,é obrigado a invocar o método wait() e esperar uma noti�ca-ção. O próximo thread a assumir a CPU é o thread 1 que executao método getElement(), que estabelece a condição total<TAMe executa um notify(). No entanto, o próximo thread a assu-mir a CPU é o thread 2 e não o thread 3. O thread 2 executa o

Tópicos Avançados 43

método addElement(), o qual estabelece novamente a condiçãototal==TAM. Quando o thread 3 assume novamente a CPU, umavez que foi noti�cado, testa a condição e invoca novamente o mé-todo wait() para esperar a condição favorável à execução. Casonão testasse a condição em um comando de repetição o thread 3tentaria inserir um elemento em uma �la cheia.

O método notify() não indica que evento ocorreu. No caso doexemplo 2.15 existem dois tipos de eventos (a �la não está cheiae a �la não está vazia), no entanto, podemos observar que nãoexiste a possibilidade de um thread ser noti�cado em decorrênciade um evento diferente do que está aguardando.

Porém, existem alguns casos mais complexos onde podemexistir vários threads aguardando em um mesmo monitor masesperando por evento diferentes. Neste caso podemos usar onotifyAll() para noti�car todos os threads que esperam em umúnico monitor que um evento ocorreu. Cada thread, a medidaque fosse escalado, testaria se ocorreu condição para a execução,em caso positivo prosseguiria na execução e em caso contrário,voltaria a aguardar no monitor.

O exemplo 2.16 mostra o código de um gerenciador de mensa-gens. Ele é responsável por receber mensagens destinadas à váriosthreads. As mensagens de cada thread são colocadas em uma �laimplementada por um objeto da classe Vector. Cada �la é porsua vez colocada em uma tabela hash onde a chave é um nome as-sociado ao thread ao qual as mensagens se destinam. As �las sãocriadas na primeira tentativa de acesso, tanto na leitura quantono armazenamento. Não existe bloqueio devido à �la cheia, umavez que as �las são implementadas por objetos da classe Vectorque crescem conforme a necessidade. Portanto, o único eventoque necessita ser noti�cado é a chegada de alguma mensagem.Como todos os threads aguardam sobre o mesmo monitor é usadoo método notifyAll() para noti�car todos os threads.

import java . u t i l . ∗ ;

class GerenteMen {private Hashtable tamMen ;

public GerenteMen ( ) { tamMen=new Hashtable ( ) ; }

44 Java na prática

// Método para adicionar uma mensagem// à f i l a de um des t ina tá r i opublic synchronized void addMen( St r ing dest ,

S t r ing men){

i f ( dest==null | | men==null ) return ;Vector l i s taMen = ( Vector ) tamMen . get ( dest ) ;i f ( l i s taMen==null ) l i s taMen = new Vector ( ) ;l i s taMen . addElement (men ) ;tamMen . put ( dest , l i s taMen ) ;n o t i f yA l l ( ) ;

}

// Método para obtenção da mensagempublic synchronized St r ing getMen ( St r ing dest )

throws Exception{

i f ( dest==null ) return null ;Vector l i s taMen = ( Vector ) tamMen . get ( dest ) ;

// Se não e x i s t e a f i l a para esse thread// cr ia uma vaziai f ( l i s taMen==null ){

l i s taMen = new Vector ( ) ;tamMen . put ( dest , l i s taMen ) ;

}// A f i l a es tá vazia , portanto thread deve// esperar a chegada de mensagenswhile ( l i s taMen . s i z e ()==0) wait ( ) ;S t r ing temp = ( St r ing ) l i s taMen . f i r s tE l ement ( ) ;

// A mensagem é removida da f i l al i s taMen . removeElementAt ( 0 ) ;return temp ;

}}

Exemplo 2.16 Gerenciador de mensagens.

O exemplo 2.17 mostra como pode ser usado o gerente de �lasdo exemplo 2.16. Devido o uso da classe ThreadGroup assim comovários de seus métodos, resolvemos numerar as linhas de código doexemplo 2.17 para melhor podermos explicar o seu funcionamento.

1 class Receptor extends Thread2 {3 private GerenteMen ger ;4 public Receptor (ThreadGroup tg , S t r ing nome ,5 GerenteMen aGer )

Tópicos Avançados 45

6 {7 super ( tg , nome ) ;8 ger = aGer ;9 }

10 public void run ( )11 {12 St r ing nome = Thread . currentThread ( ) . getName ( ) ;13 for ( ; ; )14 try15 {16 St r ing men = ger . getMen (nome ) ;17 i f (men . equa l s ( " fim" ) ) return ;18 System . out . p r i n t l n (nome+">Mensagem receb ida : "+19 men ) ;20 } catch ( Exception e )21 {22 System . out . p r i n t l n ( e . getMessage ( ) ) ;23 }24 }25 }2627 class Gerador extends Thread28 {29 private GerenteMen ger ;30 public Gerador (ThreadGroup tg , S t r ing nome ,31 GerenteMen aGer )32 {33 super ( tg , nome ) ;34 ger = aGer ;35 }36 public void run ( )37 {38 St r ing nome = Thread . currentThread ( ) . getName ( ) ;39 ThreadGroup tg =40 Thread . currentThread ( ) . getThreadGroup ( ) ;41 Thread [ ] t l=null ;42 for ( int i =0; i <100; i++)43 {44 i f ( t l==null | | t l . l ength != tg . act iveCount ( ) )45 t l = new Thread [ tg . act iveCount ( ) ] ;46 tg . enumerate ( t l ) ;47 int n = ( int ) (Math . random() ∗ 1000)% t l . l ength ;48 i f ( t l [ n ] != Thread . currentThread ( ) )49 {50 System . out . p r i n t l n (nome+51 ">Mensagem enviada para "+52 t l [ n ] . getName()+" : mensagem "+i ) ;53 ger . addMen( t l [ n ] . getName ( ) , "mensagem "+i ) ;54 }55 }56 t l = new Thread [ tg . act iveCount ( ) ] ;57 tg . enumerate ( t l ) ;58 for ( int i =0; i<t l . l ength ; i++)59 i f ( t l [ i ] != Thread . currentThread ( ) )60 ger . addMen( t l [ i ] . getName ( ) , " fim" ) ;61 }

46 Java na prática

62 }6364 public c lass TestaGerenteMen65 {66 public stat ic void main ( St r ing args [ ] )67 throws Exception68 {69 GerenteMen ger = new GerenteMen ( ) ;70 ThreadGroup tg = new ThreadGroup ( " tg " ) ;71 Receptor r1 = new Receptor ( tg , "r_um" , ger ) ;72 Receptor r2 = new Receptor ( tg , " r_dois " , ger ) ;73 Gerador g = new Gerador ( tg , "g" , ger ) ;74 r1 . s t a r t ( ) ;75 r2 . s t a r t ( ) ;76 g . s t a r t ( ) ;77 }78 }

Exemplo 2.17 Uso do gerenciador de �las.

Um objeto da classe ThreadGroup agrupa um conjunto de thre-ads. Um ThreadGroup pode possuir, como membros, outros obje-tos da classe ThreadGroup formando assim uma árvore onde todosos grupos, exceto o primeiro possuem um grupo pai. O objetivode se agrupar os threads em conjuntos é facilitar a sua manipu-lação. No caso do exemplo 2.17 usaremos esse agrupamento paraacessar cada thread.

As linhas 1 a 25 de�nem a classe que será usada para criaçãode objetos receptores de mensagens. Na linha 3 é declarada avariável que irá referenciar um objeto do tipo GerenteMen. Aslinhas 4 a 9 contém o código do único construtor da classe. Elerecebe uma referência para o grupo de threads ao qual deve seassociar, o nome que deve ser atribuído ao thread e a referênciaao gerente de �las. Na linha 7 os primeiros dois parâmetros sãopassados ao construtor da superclasse. Na linha 8 a referência aogerente de �las é atribuída à variável da instância. As linhas 10 a24 contêm o código do método run() que é o método de entradado thread. Na linha 12 é invocado o método

Thread.currentThread().getName();

para se obter o nome do thread corrente. O nome do thread éusado para referenciar a �la de mensagens do thread. Entre as

Tópicos Avançados 47

linhas 13 e 23 é executado um laço in�nito onde o thread recebee imprime as mensagens recebidas. Na linha 17 o thread testa sea mensagem recebida é igual a ��m�. Neste caso o thread encerrasua execução.

As linhas 27 a 62 de�nem a classe que será usada para criaçãodo objeto gerador de mensagens. Este exemplo foi projetado paralidar com apenas um thread gerador de mensagem. Modi�caçõesdevem ser realizadas para tratar de aplicações com mais de umthread gerador de mensagens. Na linha 29 é declarada a variávelque irá referenciar um objeto do tipo GerenteMen. As linhas 30a 35 contêm o código do único construtor da classe. Ele recebeuma referência para o grupo de threads ao qual deve se associar,o nome que deve ser atribuído ao thread e a referência ao gerentede �las. Na linha 33 os primeiros dois parâmetros são passados aoconstrutor da superclasse. Na linha 34 a referência ao gerente de�las é atribuída à variável da instância. As linhas 36 a 61 contêmo código do método run() que é o método de entrada do thread.Na linha 38 é obtido o nome do thread corrente que será usado naimpressão de mensagens. Na linha 40 o método

Thread.currentThread().getThreadGroup();

obtém uma referência para o grupo de threads ao qual pertenceo thread corrente. Na linha 41 é declarada uma variável que iráreferenciar um vetor contendo referências a todos os threads ativosdo grupo. Entre as linhas 42 e 55 é executado um laço com 100iterações que produz e armazena as mensagens. Na linha 44 érealizado um teste para a veri�car da necessidade de criar o vetorque irá conter as referências para os threads ativos. Ele deve sercriado a primeira vez e em todas as vezes que a capacidade do vetorfor diferente do número de threads ativos do grupo. O tamanhodo vetor é determinado pelo método de instância activeCount()da classe ThreadGroup. A linha 46 contém o código

tg.enumerate(tl);

que insere as referências de todos os threads no vetor tl. O co-mando

48 Java na prática

int n = (int)(Math.random()*1000)%tl.length;

da linha 47 calcula um número que será usado para acessar o threaddentro do vetor de referências. O teste da linha 48 impede queseja enviada uma mensagem para o próprio gerador, descartandoas mensagens. As linhas 50 a 53 tratam da impressão e envio damensagem construída. Já fora da iteração, as linhas 56 a 60 tratamdo envio da mensagem ��m� para todos os threads receptores, oque fará com que encerrem sua execução.

As linhas 64 a 78 de�nem a classe que será usada como pontode entrada da aplicação. Ela é responsável pela criação dos objetose disparos dos threads. No exemplo, além do thread gerador, ape-nas dois threads receptores são criados. É interessante notar quenão é preciso indicar para o thread gerador as referências para osthreads receptores. Elas são obtidas dinamicamente por meio dogrupo de threads. Um possível trecho da saída obtida na execuçãodo programa do exemplo 2.17 seria o seguinte:...g>Mensagem enviada para r_dois:mensagem 88r_um>Mensagem recebida:mensagem 87g>Mensagem enviada para r_dois:mensagem 90r_dois>Mensagem recebida:mensagem 88g>Mensagem enviada para r_um:mensagem 91r_dois>Mensagem recebida:mensagem 90g>Mensagem enviada para r_um:mensagem 93r_um>Mensagem recebida:mensagem 91g>Mensagem enviada para r_um:mensagem 95r_um>Mensagem recebida:mensagem 93g>Mensagem enviada para r_um:mensagem 96r_um>Mensagem recebida:mensagem 95g>Mensagem enviada para r_um:mensagem 97r_um>Mensagem recebida:mensagem 96g>Mensagem enviada para r_dois:mensagem 99r_um>Mensagem recebida:mensagem 97r_dois>Mensagem recebida:mensagem 99

Otimizando a Programação MultithreadExiste um problema óbvio com a abordagem do exemplo 2.16 : amensagem é dirigida a apenas um thread mas todos serão noti�-

Tópicos Avançados 49

cados, sobrecarregando o sistema, uma vez que todos os threadsprecisaram testar se a mensagem é destinada a eles. Para contor-nar esse problema é necessário vislumbrar uma forma de noti�carapenas o thread destinatário. Isto pode ser obtido se cada threadesperar em um monitor de um objeto diferente. Não importa otipo do objeto desde que seja referenciável pelo thread receptor epelo thread que irá armazenar a mensagem. Um candidato natu-ral é a �la de mensagem de cada thread. Existe uma �la para cadathread e o thread que armazena a mensagem tem acesso a todas as�las por meio da tabela hash. O exemplo 2.18 mostra uma versãodo exemplo 2.16 que utiliza esta técnica para criar uma aplicaçãomultithread mais otimizada.

import java . u t i l . ∗ ;

class GerenteMen {private Hashtable tamMen ;

public GerenteMen ( ) { tamMen=new Hashtable ( ) ; }

// Método para adicionar uma mensagem à f i l a de// um des t ina tá r i opublic void addMen( St r ing dest , S t r ing men){

i f ( dest==null | | men==null ) return ;Vector l i s taMen = ( Vector ) tamMen . get ( dest ) ;i f ( l i s taMen==null ) l i s taMen = new Vector ( ) ;synchronized ( l i s taMen ){

l i s taMen . addElement (men ) ;tamMen . put ( dest , l i s taMen ) ;l i s taMen . no t i f y ( ) ;

}}

// Método para obtenção da mensagempublic St r ing getMen ( St r ing dest )

throws Exception{

i f ( dest==null ) return null ;Vector l i s taMen = ( Vector ) tamMen . get ( dest ) ;

// Se não e x i s t e a f i l a para esse thread// cr ia uma vaziai f ( l i s taMen==null ){

l i s taMen = new Vector ( ) ;tamMen . put ( dest , l i s taMen ) ;

}// A f i l a es tá vazia , portanto thread

50 Java na prática

// deve esperarwhile ( l i s taMen . s i z e ()==0)

synchronized ( l i s taMen ) { l i s taMen . wait ( ) ; }St r ing temp = ( St r ing ) l i s taMen . f i r s tE l ement ( ) ;

// A mensagem é removida da f i l al i s taMen . removeElementAt ( 0 ) ;return temp ;

}}

Exemplo 2.18 Gerenciador de mensagens otimizado.

Note que os métodos wait() e notify() invocados pertencemà �la relacionada com cada thread. O exemplo 2.17 pode ser usadosem modi�cações para testar essa versão.

Criando outros mecanismos de sincronizaçãoExistem várias propostas de primitivas de sincronização. Den-tre as mais comuns podemos citar os semáforos, mutex, variáveiscondicionais, monitores e encontros (rendevouz ). Cada uma des-sas primitivas é mais adequada a um determinado propósito. Aimplementação de monitores na linguagem Java, juntamente comos métodos wait() e notify(), que formam um tipo de variá-veis condicionais, podem ser combinados para implementar mui-tas dessas outras primitivas, de modo a atender objetivos espe-cí�cos. Para exempli�car essa possibilidade mostraremos comoimplementar um semáforo usando as primitivas de sincronizaçãoda linguagem Java.

Um semáforo é uma variável inteira sobre a qual pode-se rea-lizar as seguintes operações:

Operação Descriçãoinicializar Um valor inteiro maior ou igual a zero é atri-

buído ao semáforo.

P

Se o semáforo é maior que zero, o semáforoé decrementado. Caso contrário, o thread ésuspenso até que o semáforo contenha um valormaior que zero.

VIncrementa o semáforo e acorda os threads queestiverem bloqueados na �la de espera do se-máforo.

Tópicos Avançados 51

Tabela 2.6 Operações sobre um semáforo.

Semáforo é um mecanismo de sincronização muito utilizadoquando existe a necessidade de comunicação entre dois ou maisprocessos, como no caso de sistemas do tipo produtor/consumi-dor. Por exemplo, suponha dois processos onde um coloca men-sagens em um bu�er e outro retira as mensagens. Os processospodem usar dois semáforos para sincronizar o acesso ao bu�er demensagens: um para controlar a entrada na região crítica e outropara contar o número de mensagens. A �gura 2.7 mostra os esque-mas dos processos. A implementação dos semáforos em Java podeser visto no exemplo 2.19 e o uso dos semáforos em uma situaçãocomo a ilustrada pela �gura 2.7 pode ser visto no exemplo 2.20.

Produtor Consumidors=1; n=0;

início loop início loop

Produz mensagem P(n) //Checa se existem mens.

P(s) //Checa se pode entrar na P(s) //Checa se pode entrar na

//região crítica //região crítica

Coloca mensagem no bu�er Retira mensagemV(n) //incrementa no. de mens. V(s) //sai da região crítica

V(s) //sai da região crítica Consome Mensagemfim loop fim loop

Figura 2.7 Comunicação entre processos usando semáforos.

public class Semaforo{

private int cont ;

public Semaforo ( ){ cont =0;}public Semaforo ( int i ){ cont = i ; }

public synchronized void P( )throws Inter ruptedExcept ion

{while ( cont <=0) this . wait ( ) ;cont−−;

}

52 Java na prática

public synchronized void V(){

cont++;n o t i f yA l l ( ) ;

}}

Exemplo 2.19 Implementação de um Semáforo.

import java . u t i l . Vector ;

class Consumidor extends Thread{

private Vector bu f f ;private Semaforo s , n ;

public Consumidor ( Vector aBuff , Semaforo as ,Semaforo an )

{super ( ) ;bu f f = aBuff ; s = as ; n = an ;

}public void run ( ){

for ( ; ; )try{

n . p ( ) ; // Ver i f i ca se e x i s t e mensagenss . p ( ) ; // Ver i f i ca se pode entrar na

// região c r í t i c aSt r ing men = ( St r ing ) bu f f . f i r s tE l ement ( ) ;bu f f . removeElementAt ( 0 ) ;s . v ( ) ;i f (men . equa l s ( " fim" ) ) return ;System . out . p r i n t l n ( "Mensagem receb ida : "+

men ) ;} catch ( Exception e ){System . out . p r i n t l n ( e . getMessage ( ) ) ; }

}}

class Produtor extends Thread{

private Vector bu f f ;private Semaforo s , n ;public Produtor ( Vector aBuff , Semaforo as ,

Semaforo an ){

super ( ) ;

Tópicos Avançados 53

bu f f = aBuff ; s = as ; n = an ;}public void run ( ){

for ( int i =0; i <11; i++){

try{

s . p ( ) ; // Ver i f i ca se pode entrar na// região c r í t i c a

i f ( i <10){

bu f f . addElement ( ""+i ) ;System . out . p r i n t l n (

"Mensagem enviada : "+ i ) ;}else bu f f . addElement ( " fim" ) ;n . v ( ) ; // incrementa o número de mensagenss . v ( ) ; // abandona a região c r í t i c aThread . y i e l d ( ) ;

} catch ( Exception e ){System . out . p r i n t l n ( e . getMessage ( ) ) ; }

}}

}

public c lass TestaSemaforo{

public stat ic void main ( St r ing args [ ] )throws Exception

{Vector bu f f = new Vector ( ) ;Semaforo s = new Semaforo ( 1 ) ;Semaforo n = new Semaforo ( 0 ) ;Produtor t1 = new Produtor ( buf f , s , n ) ;Consumidor t2 = new Consumidor ( buf f , s , n ) ;t1 . s t a r t ( ) ;t2 . s t a r t ( ) ;

}}

Exemplo 2.20 Uso de semáforos por dois threads

54 Java na prática

Capítulo 3

Programação em rede

Diferentemente da maioria das linguagens de programação atuais,Java foi projetada na era da Internet, e por isso mesmo ferramen-tas para comunicação dentro da Grande Rede foram incorporadasà linguagem desde a sua concepção. Classes para manipulação deURLs (Uniform Resource Locator) e dos protocolos que consti-tuem a Internet fazem parte do núcleo básico da linguagem. Istofacilita muito a tarefa de desenvolver aplicações para a Internetou outras redes que fazem uso do mesmo conjunto de protocolos.Esta é uma das principais forças da linguagem Java. Para enten-dermos como desenvolver aplicações em rede com Java é impor-tante a compreensão de alguns conceitos básicos sobre protocolosde comunicação.

3.1 Conceitos Sobre Protocolos Usadosna Internet

Um protocolo de comunicação é um conjunto de formatos e re-gras usadas para transmitir informação. Computadores distintosdevem obedecer estas regras e formatos de modo que se possamcomunicar. Podemos encarar o protocolo como a de�nição de umalinguagem comum, de modo a possibilitar a comunicação entre di-ferentes entidades.

55

56 Java na prática

Visando diminuir a complexidade de implementação e uso doprotocolo, ele é divido e organizado em forma de camadas de pro-tocolos, onde a camada relativamente inferior a outra na pilhaestabelece as regras para a camada superior sobre a utilização deseus serviços. As camadas inferiores fornecem serviços mais bási-cos de transmissão de dados, enquanto que as camadas superioresoferecem serviços de mais alto nível. Esta forma de organizaçãohierárquica de protocolos é também chamada de pilha de proto-colos.

A pilha de protocolos sobre a qual a Internet se organiza éa pilha de protocolos TCP/IP. Por simplicidade chamaremos apilha de protocolos TCP/IP apenas como protocolo TCP/IP ouTCP/IP. A �gura 3.1 mostra como se organizam alguns dos pro-tocolos que fazem parte do TCP/IP.

Figura 3.1 Alguns protocolos da pilha TCP/IP.

A camada física é a responsável pelo transporte efetivo dosdados sobre o meio físico. A camada de rede é responsável pelainterface lógica entre os computadores. A camada de transporteprovê transferência de dados no nível de serviço, e a camada deaplicação provê comunicação no nível de processos ou aplicações.Exemplos de protocolos no nível de aplicação são: FTP, usado

Tópicos Avançados 57

para transferência de arquivos; HTTP, usado para transmissãode páginas Web; TELNET provê capacidade de log-on remoto;SMTP provê serviços básicos de correio eletrônico; SNMP usadopara gerência da rede; e MIME que é uma extensão do SMTPpara lidar com mensagens com conteúdos diversos.

No processo de transmissão de dados sobre uma rede TCP/IPos dados são divididos em grupos chamados de pacotes. Cadacamada adiciona alguns dados a mais no início de cada pacote parapermitir que o mesmo chegue ao destino. Os dados adicionadossão chamados de headers.

Figura 3.2 Headers adicionados a cada camada de protocolo.

Na camada de transporte existem dois protocolos que fazemuso do protocolo IP: o protocolo TCP/IP e o UDP.

3.1.1 TCPO protocolo TCP é um protocolo orientado a conexão que provêum �uxo con�ável de dados entre dois computadores. Por pro-tocolo orientado a conexão queremos dizer que é estabelecido umcanal de comunicação ponto-a-ponto onde os dados podem trafe-gar em ambas as direções. O TCP garante que os dados enviadosem uma ponta cheguem ao destino, na mesma ordem que foramenviados. Caso contrário, um erro é reportado. Protocolos como

58 Java na prática

HTTP, FTP e TELNET exigem um canal de comunicação con-�ável e a ordem de recebimento dos dados é fundamental para osucesso dessas aplicações.

3.1.2 UDP

No entanto, nem todas as aplicações necessitam destas caracte-rísticas do protocolo TCP e o processamento adicional exigidopara garantir a con�abilidade e a ordenação dos dados podeminviabilizá-las. Para esses casos existe o protocolo de transporteUDP. UDP é um protocolo para envio de pacotes independentesde dados, chamados de datagramas, de um computador a outro,sem garantias sobre a chegada dos pacotes. O protocolo UDP nãoé orientado a conexão.

3.1.3 Identi�cação de Hosts (Número IP)

Cada computador conectado a uma rede TCP/IP é chamado deHost e é identi�cado por um único número de 32 bits, denominadode número IP. O número IP é representado por quatro grupos de8 bits, limitando desta forma o valor numérico de cada grupo aovalor máximo de 255. Um exemplo de número IP é 200.65.18.70.Uma vez que é muito difícil lembrar e atribuir signi�cado a núme-ros existe uma forma alternativa de identi�car os computadoresda rede por meio de nomes. Um ou mais computadores da redefazem o papel de resolvedores de nomes (servidores DNS- DomainName Server), mantendo bases de dados que associam o nomes doHosts à seus números IP. Desta forma é possível um computadorcomunicar com outro computador por meio do nome lógico e nãopor meio do número IP. A �gura 3.3 ilustra a comunicação entredois computadores. A Internet é representada como uma nuvemdevido a complexidade da rede.

Tópicos Avançados 59

Figura 3.3 Representação da comunicação entre doiscomputadores.

O representação dos nomes dos computadores na Internet éfeita por substrings separadas por `.' e obedecem uma regra denomeação que de�ne que o primeiro substring representa o nomeda máquina, e os restantes representam o domínio onde está inse-rida a máquina. Um domínio é um agrupamento de computadoresque pertencem a uma instituição, órgão, empresa, ou uma orga-nização qualquer. Assim, no exemplo da �gura 3.3 o computa-dor meucomp pertence ao domínio com.br. No Brasil a FAPESP(Fundação de Amparo à Pesquisa do Estado de São Paulo) é res-ponsável pela gerência dos nomes dos domínios na Internet.

3.1.4 Identi�cação de Processos (Portas)A comunicação entre dois processos em uma rede TCP/IP é as-simétrica, no sentido que um processo faz o papel de servidor,oferecendo um serviço e outro faz o papel de cliente do serviço.Em um único Host vários processos podem estar fazendo o papelde servidor, oferecendo serviços através de um único meio físico.Portanto, é preciso uma forma de identi�car os servidores em ummesmo Host. Isto é feito por meio da associação de um númerointeiro, chamado de porta, ao processo servidor. Essa associaçãoé feita pelo processo assim que é carregado, por meio de uma cha-mada ao sistema. O número da porta pode variar de 1 a 65535,no entanto os números de 1 a 1023 são reservados para serviçosconhecidos, como por exemplo o FTP e o HTTP. O programadornão deve usar estas portas a não ser que esteja implementandoalgum desses serviços. Nos ambientes Unix as portas que vão de6000 a 6999 são usadas pelo gerenciador de Interfaces X Windows

60 Java na prática

e 2000 a 2999 por outros serviços, como o NFS. Nestes ambientes,estas faixas de números de portas também devem ser evitadas. Atabela 3.1 mostra o número da porta de alguns dos serviços maisconhecidos.

Protocolo PortaHTTP 80echo 7FTP 20, 21SMTP 25Finger 79Daytime 13pop3 110

Tabela 3.1 Número das portas dos principais serviços.

Uma vez associado a uma porta o serviço pode ser acessado poruma aplicação cliente, bastando para isso que, ao se comunicar,ela indique o nome do Host e o número da porta.

3.2 Programação em Rede com JavaO pacote java.net contém as classes e interfaces usadas paraprogramação de sistemas em rede com Java. As classes podemser enquadradas em três categorias:

1. Classes para comunicação básica em rede. Tratamda comunicação em baixo nível entre aplicações. Outrosprotocolos podem ser implementados usando como base estacomunicação básica.

2. Classes para comunicação dentro da Web. Estas clas-ses provêem facilidades para acessar conteúdos por meio deURLs.

3. Classes para tratamento dos formatos estendidosda Web. Utilizadas para tratar novos protocolos e tiposMIME.

Tópicos Avançados 61

3.2.1 Comunicação Básica Entre AplicaçõesClasse DescriçãoSocket Provê um socket cliente para comunicação orientada à

conexão via protocolo TCP.ServerSocket Provê um socket servidor para comunicação orientada

à conexão via protocolo TCP.DatagramSocket Provê um socket UDP para comunicação não orientada

à conexão.DatagramPacket Representa um datagrama que pode ser enviado

usando DatagramSocket.InetAddress Representa os dados de um Host (Nome e endereço

IP).

Tabela 3.2 Classes para comunicação básica.

As classes Socket, ServerSocket, DatagramSocket,DatagramPacket e InetAddress, fornecem os métodos ne-cessários para a comunicação básica entre dois processos. Atabela 3.2 descreve sucintamente cada uma das classes.

As classes Socket e ServerSocket são utilizadas paracomunicação orientada a conexão, enquanto que as classesDatagramSocket e DatagramPacket são utilizadas para comuni-cação não orientada a conexão.

3.2.2 Comunicação orientada a conexão (cli-ente)

Para comunicar via protocolo TCP é preciso que a aplicação cli-ente crie um objeto Socket. É preciso passar o nome ou númeroIP do Host e o número da porta onde o servidor está esperandoas solicitações de serviço. A classe Socket possui os métodosgetInputStream() e getOutputStream(), que são usados paraobter Streams associados ao Socket. Deste modo, a transmissãode dados via Socket é idêntica à leitura e escrita em arquivos viastreams. O exemplo 3.1 mostra o código de um cliente que acessaum servidor de Daytime. O serviço de Daytime é disponibilizadonas plataformas UNIX e é acessado via porta 13. Sua função éenviar, aos processos clientes, uma linha de texto contendo a datae a hora corrente .

62 Java na prática

import java . i o . ∗ ;import java . net . ∗ ;

public class ClienteData{public stat ic void main ( St r ing [ ] a rgs )

throws IOException{Socket socke t = null ;Buf feredReader in = null ;

try{

socket = new Socket ( args [ 0 ] , 1 3 ) ;in = new BufferedReader (

new InputStreamReader (socke t . getInputStream ( ) ) ) ;

}catch ( UnknownHostException e ){System . e r r . p r i n t l n ( " host i n e x i s t e n t e : "+args [ 0 ] ) ;System . e x i t ( 1 ) ;

}catch ( IOException e ){System . e r r . p r i n t l n ( "Erro I /O: "+e . getMessage ( ) ) ;System . e x i t ( 1 ) ;

}

System . out . p r i n t l n ( "Data : " + in . readLine ( ) ) ;

in . c l o s e ( ) ;socke t . c l o s e ( ) ;

}}

Exemplo 3.1 Cliente para o serviço de Daytime.

No programa do exemplo 3.1 o usuário precisa passar o nomedo Host servidor pela linha de comando. Você pode testar esteprograma mesmo que seu computador não esteja conectado emuma rede, desde que o protocolo TCP/IP esteja instalado. Nestecaso você deve passar como parâmetro o nome do seu computa-dor ou, alternativamente, o nome localhost, ou, ainda, o númeroIP 127.0.0.1. O nome localhost e o número IP 127.0.0.1 sem-

Tópicos Avançados 63

pre identi�cam o computador local. Note que, após a criação doobjeto Socket, o método getInputStream() é chamado e o objetoretornado é envolvido por um objeto BufferedReader de modo acomunicar dados via rede da mesma forma que é realizada umaoperação de E/S. Ao se terminar a operação é preciso fechar tantoa instância BufferedReader quanto o objeto Socket. A instânciada classe de E/S sempre deve ser fechada primeiro.

Os Hosts que utilizam as variações do sistema operacionalUNIX possuem o serviço de Daytime, no entanto, outros sistemasoperacionais podem não implementar este serviço. O programa-dor pode resolver este problema implementando ele mesmo umservidor de Daytime. A próxima seção mostrará como isto podeser feito.

3.2.3 Comunicação orientada à conexão (servi-dor)

Para criar um processo servidor é preciso associá-lo à uma porta.Isto é feito ao se criar uma instância da classe ServerSocket. Seestamos criando um novo serviço é preciso associar a uma portacom valor maior que 1023. Se estamos implementando um serviçojá estabelecido é preciso obedecer as especi�cações de�nidas parao serviço. O exemplo 3.2 mostra o código de um servidor doserviço Daytime. Como não queremos substituir o serviço padrãode Daytime utilizaremos o número de porta 5013 no lugar donúmero 13. Para testar este servidor com o programa cliente doexemplo 3.1 é preciso alterar o número da porta no código docliente.

import java . u t i l . ∗ ;import java . i o . ∗ ;import java . net . ∗ ;

public class ServerData {public stat ic void main ( St r ing [ ] a rgs )

throws IOException{

ServerSocket s s o cke t = null ;Socket socke t = null ;Buf feredWriter out = null ;

64 Java na prática

try{

s s o cke t = new ServerSocket ( 5 0 1 3 , 5 ) ;for ( ; ; ){

socke t = s socke t . accept ( ) ;out = new Buf feredWriter (

new OutputStreamWriter (socket . getOutputStream ( ) ) ) ;

out . wr i t e ( (new Date ( ) ) . t oS t r i ng ()+"\n" ) ;}

} catch ( IOException e ){

System . out . p r i n t l n ( e . getMessage ( ) ) ;}f ina l ly {

out . c l o s e ( ) ;socke t . c l o s e ( ) ;

}}

}

Exemplo 3.2 Servidor de Daytime.

Ao criar uma instância da classe ServerSocket o programadorpode indicar o tamanho da �la de solicitações de conexão. As co-nexões são colocadas na �la até que o servidor possa atendê-las. Sechegar alguma conexão e não houver espaço na �la a conexão serárecusada. No nosso exemplo passamos como parâmetro o valor5. Após criar o objeto ServerSocket o servidor deve indicar queestá disposto a receber conexões. Isto é feito por meio da execuçãodo método accept() do objeto ServerSocket. Ao executar estemétodo o processo passa para o estado bloqueado até que algumaconexão seja solicitada. Quando a conexão é solicitada o métodoaccept() retorna um objeto Socket que será usado para obter osstreams onde será efetuada a comunicação. O stream obtido doobjeto Socket é encapsulado em um objeto BufferedWriter queserá encarregado de enviar a cadeia de caracteres contendo a datae a hora para o cliente. O Servidor implementa um laço in�nito,recebendo e tratando solicitações de serviços.

Tópicos Avançados 65

O servidor implementado trata uma solicitação de serviço porvez. Isto pode ser problemático quando existem vários clientessolicitando serviços ao mesmo tempo e o servidor leva um longotempo tratando cada solicitação. Nesta situação o cliente pode�car muito tempo a espera de atendimento. Isto pode ser re-mediado por meio da implementação de servidores Multithreaded.Apesar do serviço implementado pelo exemplo 3.2 não exigir umservidor Multithreaded, uma vez que o cliente é atendido rapida-mente, o servidor Daytime foi alterado para exempli�car a imple-mentação de um servidor Multithreaded. O exemplo 3.3 mostra ocódigo da versão Multithreaded do servidor.

import java . u t i l . ∗ ;import java . net . ∗ ;import java . i o . ∗ ;

public class ServerDataM{

public stat ic void main ( St r ing args [ ] )throws IOException

{ServerSocket s s o cke t=null ;

try{

s s o cke t = new ServerSocket ( 5013 , 5 ) ;

while ( true ){

Socket socke t = s socke t . accept ( ) ;(new SubServer ( socket ) ) . s t a r t ( ) ;

}}catch ( Exception e ){System . e r r . p r i n t l n ( e . getMessage ( ) ) ; }f ina l ly { s so cke t . c l o s e ( ) ; }

}}

class SubServer extends Thread{

Socket socke t ;

public SubServer ( Socket aSocket ){

66 Java na prática

socket = aSocket ;}

public void run ( ){

try{

Buf feredWriter out = new Buf feredWriter (new OutputStreamWriter (

socket . getOutputStream ( ) ) ) ;out . wr i t e ( (new Date ( ) ) . t oS t r i ng ()+"\n" ) ;out . f l u s h ( ) ;out . c l o s e ( ) ;socke t . c l o s e ( ) ;

}catch ( Exception e ) { System . e r r . p r i n t l n ( e ) ; }

}}

Exemplo 3.3 Servidor de Daytime Multithreaded.

No exemplo 3.3, ao receber uma conexão o servidor cria umThread para atender o cliente e �ca disponível para receber novassolicitações. Esse exemplo pode ser usado como esqueleto paradesenvolvimento de servidores mais complexos, porém, neste casoé necessário limitar o número de Threads que podem ser criados.

3.2.4 Comunicação Sem Conexão (UDP)Como já dissemos na seção anterior, nem sempre é necessário umcanal de comunicação con�ável entre duas aplicações. Para estescasos existe o protocolo UDP, que provê uma forma de comu-nicação onde a aplicação envia pacotes de dados, chamados dedatagramas, para outra aplicação, sem garantias se e quando amensagem vai chegar, nem se o conteúdo será preservado.

As portas do protocolo UDP obedecem a mesma distribuiçãodas TCP porém são distintas uma da outra, de modo que o progra-mador pode associar uma porta TCP de um determinado númeroà uma aplicação em um host e o mesmo número de porta UDP aoutra aplicação no mesmo host.

Tópicos Avançados 67

As classes DatagramPacket e DatagramSocket contém os mé-todos necessários para realizar este tipo de comunicação. Parailustrar o uso destas classes modi�caremos os exemplos 3.1 e 3.2para implementarmos uma aplicação Cliente/Servidor Daytimeque usa o protocolo UDP. O exemplo 3.4 mostra o código fontedo cliente e o exemplo 3.5 mostra o código do servidor.

import java . i o . ∗ ;import java . net . ∗ ;

public class DataClienteUDP{

public stat ic void main ( St r ing args [ ] )throws Exception

{i f ( args . l ength != 1 ){

System . e r r . p r i n t l n ("Uso : java DataClienteUDP host " ) ;

System . e x i t ( 1 ) ;}byte [ ] bu f f = new byte [ 6 4 ] ;DatagramSocket ds = new DatagramSocket ( ) ;DatagramPacket dp =

new DatagramPacket ( buf f , bu f f . length ,InetAddress . getByName( args [ 0 ] ) , 5 0 1 3 ) ;

ds . r e c e i v e (dp ) ;S t r ing s = new St r ing (dp . getData ( ) ) ;System . out . p r i n t l n ( "Data e hora r eceb ida de"+

dp . getAddress ()+" : "+s ) ;}

}

Exemplo 3.4 Cliente Daytime UDP.

Analisando o código da aplicação cliente podemos notar que énecessário criar um objeto da classe DatagramPacket para repre-sentar os Datagrama onde os dados são armazenados. No nossoexemplo colocamos também os dados do host e a porta, porémestes dados poderiam ser omitidos na construção do datagrama eserem passados somente no envio/recepção do pacote ou na cons-trução do DatagramSocket. O Datagrama deverá armazenar os

68 Java na prática

dados enviados pelo servidor e foi dimensionado para conter 64 by-tes. O método receive() do objeto DatagramSocket aguarda orecebimento do pacote, tendo como argumento o objeto da classeDatagramPacket. Após o recebimento do pacote os dados sãoconvertidos para String e exibidos na saída padrão.

import java . i o . ∗ ;import java . net . ∗ ;import java . u t i l . ∗ ;

public c lass DataServerUDP{

public stat ic void main ( St r ing args [ ] )throws Exception

{DatagramSocket ds ;DatagramPacket dp ;InetAddress addr =

InetAddress . getByName( " 255 . 255 . 255 . 0 " ) ;ds = new DatagramSocket ( ) ;

byte [ ] bu f f ;for ( ; ; ){

Thread . s l e ep ( 1000 ) ;S t r ing s = (new Date ( ) ) . t oS t r i ng ( ) ;bu f f = s . getBytes ( ) ;dp = new DatagramPacket (

buf f , bu f f . length , addr , 5 0 1 3 ) ;ds . send (dp ) ;

}}

}

Exemplo 3.5 Servidor Daytime UDP.

O servidor, diferentemente do servidor TCP, precisa saber aquem deve enviar os pacotes, uma vez que não é estabelecidauma conexão. Podíamos simplesmente passar o nome do hostpela linha de comando, mas resolvemos adotar uma estratégiade Broadcasting. Nesta abordagem os datagramas são enviadosa vários computadores e não apenas um. Isto é feito passando-se como argumento para o método InetAddress.getByName() oendereço de Broadcast da rede.

O tipo de endereço de Broadcast depende da classe de endere-çamento IP da rede. O endereço usado no exemplo 3.5 funciona

Tópicos Avançados 69

para a classe de endereçamento C. Para descobrir que tipo declasse pertence a rede onde está seu computador olhe o primeirobyte do endereço IP de sua máquina e veri�que junto a tabela 3.3.

Primeiro byte do endereço IP Classe Endereço de Broadcast0 a 126 A 255.0.0.0128 a 191 B 255.255.0.0192 a 223 C 255.255.255.0

Tabela 3.3 Endereços de Broadcast.

O envio dos dados é feito pelo método send() do objetoDatagramSocket, tendo como argumento um objeto da classeDatagramPacket. Note que não é necessário um �Socket servi-dor� uma vez que o servidor envia os pacotes independentementede existir clientes solicitando-os.

3.2.5 Comunicação por meio de URLUma URL (Uniform Resource Locator) é uma referência (um en-dereço) a um recurso na Internet. A URL é dividida em partes,sendo que apenas a primeira parte é obrigatória. A maioria dasURLs é dividida em três partes:

Figura 3.4 Divisões da URL.

A parte do protocolo de�ne o protocolo que deve ser usadopara acessar o recurso. Os protocolos mais comuns são FTP,HTTP e �le, este último indicando que o recurso se encontra nosistema de arquivos local. O protocolo é seguido do caractere �:�.

70 Java na prática

A parte com informação sobre o host fornece a informaçãonecessária para acessar o host, ou seja, onde está localizado orecurso. A informação sobre o host é precedida por duas barras(�//�), no caso de aplicação na Internet e apenas por uma barra(�/�), caso contrário. A informação sobre o host também podeser dividida em três partes: a) o nome do domínio do host; b)o nome e senha do usuário para login; e c) o número da portacaso necessário, após o nome do host, precedida pelo caractere �:�.Exemplos:

http://java.sun.com:80/doc/tutorial.htmlhttp://infax.com.br."claudio"."1234"/base/vendas

A última parte de uma URL representa o caminho até o recursono sistema de arquivos do host e é chamada de URI (UniformResource Identi�er). Esta seção é separada da seção anterior poruma barra simples (�/�).

3.2.6 Manipulando URLs em JavaA linguagem Java fornece as seguintes classes para manipulaçãode URLs:

Classe DescriçãoURL Representa um URL.URLConnection Classe abstrata que representa uma conexão entre uma

aplicação e um URL. Instâncias desta classe podem serusadas para ler e escrever no recurso referenciado pelaURL.

URLEncoder Usada para lidar com o formato MIME.HttpURLConnection Classe abstrata usada para dar suporte a conexões

HTTP.

Tabela 3.4 Classes para manipulação de URLs.

Um objeto URL é criado passando como parâmetro para oconstrutor o URL na forma de uma cadeia de caracteres:

URL dpi = new URL("http://www.dpi.ufv.br");

Tópicos Avançados 71

Um objeto URL pode ser construído passando como parâmetrooutro URL para servir de endereço base. Por exemplo

URL profs = new URL (dpi,"professores.html");

Isto tem o mesmo efeito que gerar uma instância da classe URLcom primeiro construtor, passando como parâmetro o endereço:

http://www.dpi.ufv.br/professores.html

Os construtores geram a exceção MalformedURLException, seo URL é inválido. Portanto, o programador deve providenciar ocódigo para a captura e tratamento desta exceção.

import java . net . ∗ ;import java . i o . ∗ ;

public class LeURL{public stat ic void main ( St r ing [ ] a rgs )

throws Exception{i f ( args . l ength < 1){System . e r r . p r i n t l n ( "uso : java LeURL <URL>" ) ;System . e x i t ( 1 ) ;

}URL ur l = new URL( args [ 0 ] ) ;BufferedReader in = new BufferedReader (new InputStreamReader ( u r l . openStream ( ) ) ) ;

S t r ing l i nha ;

while ( ( l i nha = in . readLine ( ) ) != null )System . out . p r i n t l n ( l i nha ) ;

in . c l o s e ( ) ;}

}

Exemplo 3.6 Leitor de URL.

72 Java na prática

De posse de um objeto URL é possível obter um objetoInputStream para ler os dados endereçados pela URL, utilizandoo método openStream() do objeto URL. O exemplo 3.6 mostra ocódigo de um programa que pode ser usado para listar na saídapadrão o conteúdo de um URL passado pela linha de comando.

3.2.7 Comunicando por meio de URLConnec-tion

O programador pode utilizar o método openConnection() doobjeto URL para obter uma conexão entre a aplicação e o recursoreferenciado pela URL. O método openConnection()retorna umobjeto URLConnection, que permite que a aplicação escreva e leiaatravés da conexão. Alguns URLs, como os associados à scriptsCGI1 (Common-Gateway Interface), JSP, PHP ou ASP permitemque aplicação cliente escreva informação na URL.

<%Str ing va l o r = reques t . getParameter ( " c e l c i u s " ) ;i f ( va l o r != null ){

double f = Double . parseDouble ( va l o r )∗9/5 +32;out . p r i n t l n ( f ) ;

}%>

Exemplo 3.7 Página JSP que realiza a conversão.

A resposta à requisição pode ser interceptada pelo programaJava de modo que possa ser exibida para o usuário. Para ilus-trar esta forma de comunicação mostraremos um programa quesubmete um número, indicando uma temperatura em graus cel-sius, a uma URL para que seja transformado para graus fahre-nheit e enviado de volta. A URL aponta para uma página JSP

1Common-Gateway Interface (CGI) é um mecanismo para gerar páginasWeb dinamicamente. Os dados são obtidos de formulários HTML e submeti-dos a um programa binário no servidor que gera a resposta na forma de umapágina Web. O programa pode ser escrito em uma variedade de linguagens,como Perl e C.

Tópicos Avançados 73

(exemplo 3.7) que realiza a conversão. Para o teste foi usado umservidor HTTP local e a página foi colocada na seguinte URLhttp://127.0.0.1:8080/teste/conversao.jsp

O exemplo 3.8 contém o programa que envia a temperaturaem graus celsius para o URL recebe de volta a temperatura emgraus fahrenheit.

import java . i o . ∗ ;import java . net . ∗ ;

public c lass Converte{

public stat ic void main ( St r ing [ ] a rgs )throws Exception

{

i f ( args . l ength != 2){

System . e r r . p r i n t l n ("Uso : java Converte temp ur l " ) ;

System . e x i t ( 1 ) ;}

St r ing s t r i n g =URLEncoder . encode ( args [ 0 ] , "UTF−8" ) ;

URL ur l = new URL( args [ 1 ] ) ;

URLConnection c = ur l . openConnection ( ) ;c . setDoOutput ( true ) ;

Pr intWriter out =new PrintWriter ( c . getOutputStream ( ) ) ;

out . p r i n t l n ( " c e l c i u s=" + s t r i n g ) ;out . c l o s e ( ) ;

BufferedReader in = new BufferedReader (new InputStreamReader (

c . getInputStream ( ) ) ) ;S t r ing re to rno ;

while ( ( r e to rno = in . readLine ( ) ) != null )System . out . p r i n t l n ( r e to rno ) ;

in . c l o s e ( ) ;}

}

Exemplo 3.8 Programa que escreve em um URL.

74 Java na prática

É necessário codi�car a cadeia de caracteres enviada por meiodo método estático encode() da classe URLEncoder, uma vez quealgumas transformações são necessárias, como por exemplo, osespaços em branco são substituídos pelo caractere �+�, os campossão separados pelo caractere �&� e valor do campo é separado donome do campo pelo caracter �=�.

Em seguida o programa cria um objeto URL relacionado como endereço onde se encontra o script, abre uma conexão e de�neque esta será usada para entrada e saída.

URL url = new URL(args[1]);

URLConnection c = url.openConnection();c.setDoOutput(true);

Neste momento o programa está preparado para trabalhar como URL como se fosse um stream. O stream para escrever noURL é obtido do objeto URLConnection por meio do métodogetOutputStream() e o stream para ler do URL é obtido doobjeto URLConnection por meio do método getInputStream().Primeiro o programa submete a cadeia de caracteres a ser inver-tida precedida pelo nome do campo e pelo caractere �=�:

out.println("string="+ string);

Após isso o stream de saída é fechado e o stream de entradaé aberto. A cadeia de caracteres com a temperatura convertida élida e exibida no dispositivo de saída padrão.

Capítulo 4

Acesso a Banco deDados

Nos dias de hoje, uma linguagem sem recursos para acesso a siste-mas de Banco de Dados está fadada ao fracasso. Pensando nissoa Sun incluiu, como parte do núcleo de bibliotecas de classes dalinguagem Java, uma API (Application Program Interface) como objetivo de preencher esta função, chamada de JDBC (segundoa Sun JDBC é apenas um acronismo, no entanto, muitas pes-soas acreditam que é uma sigla para Java Database Connectivity).JDBC é uma API baseada no X/Open SQL Call Level Interface,tendo sido desenvolvida originalmente como um pacote separado,porém a partir do JDK1.1 passou a fazer parte do núcleo básico depacotes. Utilizando a API JDBC é possível conectar um programaJava com servidores de Banco de Dados e executar comandos SQL(Structure Query Language). Sendo uma API independente doSistema Gerenciador de Banco de Dados, não é necessário escre-ver uma aplicação para acessar um Banco de Dados Oracle, outrapara uma Base de Dados Sybase, outra para o DB2, e assim pordiante.

A idéia de se usar uma camada intermediária entre o Bancode Dados e a aplicação, com o objetivo de isolá-la das particula-ridades do SGBD (Sistema gerenciador de Banco de dados), não

75

76 Java na prática

é nova. O exemplo mais popular deste enfoque é a API ODBC(Open DataBase Connectivity), proposta pela Microsoft. O lei-tor pode estar se perguntando porque a Sun resolveu propor maisuma API em vez de adotar o ODBC. Existem vários motivos, po-rém o principal é que a API ODBC simplesmente não é adequadapara a linguagem Java. Isto ocorre porque ODBC foi desenvol-vida para ser usada na linguagem C e é baseada fortemente nouso de ponteiros, estrutura que não existe em Java. Além disso,a API ODBC não foi desenvolvida tendo em vista as facilidadesdo interfaciamento com uma linguagem orientada a objetos.

4.1 Modelos de Acesso a ServidoresO modelo mais simples de aplicação Cliente/Servidor é o chamadomodelo de duas camadas, onde a aplicação acessa diretamenteo Banco de Dados. A �gura 4.1 mostra o esquema para umaaplicação que acessa um Banco de Dados usando o modelo deduas camadas.

Figura 4.1 Modelo de acesso a Banco de Dados em duascamadas.

No entanto, hoje em dia, é mais comum o desenvolvimento desistemas com três ou mais camadas. A vantagem de se usar um nú-mero maior de camadas é a possibilidade de um maior isolamentodas funções, permitindo assim uma melhor manutenabilidade e�exibilidade do sistema. A �gura 4.2 mostra o esquema para uma

Tópicos Avançados 77

aplicação que acessa um Banco de Dados usando o modelo de trêscamadas.

Figura 4.2 Modelo de acesso a Banco de Dados em trêscamadas.

4.2 Tipos de Drivers JDBCOs drivers JDBC devem suportar o nível de entrada do padrãoANSI SQL-2. Na API JDBC 2.0 foi incluído suporte para tiposde dados do padrão SQL3, tais como BLOB, CLOBCLOB etipos estruturados. No momento, os drivers JDBC existentes seencaixam em um dos quatro tipos abaixo:

Tipo 1 (Ponte JDBC-ODBC com driver ODBC) : o dri-ver JDBC acessa o banco de dados via drivers ODBC. Comoo ODBC é um código binário e, em alguns casos, compõe ocódigo do cliente, é necessário instalá-lo em cada máquinacliente que usa o driver. Essa é uma solução adequada so-mente para testes ou para pequenas aplicações, uma vez quea tradução para o protocolo ODBC in�uencia o desempenho.

78 Java na prática

Tipo 2 (Driver Java parcial e Api Nativa) : neste caso aschamadas JDBC são convertidas para as chamadas na APInativa do SGBD. Como o driver possui uma parte em có-digo binário é necessário instalar algum código na máquinacliente, como é feito nos drivers do tipo 1.

Tipo 3 (Driver puro Java e protocolo de rede) : nestecaso as chamadas JDBC são convertidas para um protocolode rede independente do SGBD que é depois traduzido paraas chamadas na API nativa do SGBD por um servidor.Esta é uma arquitetura em três camadas, onde o servidormiddleware é capaz de conectar seus clientes Java puroscom vários SGBDs. Esta solução permite o desenvolvimentode clientes 100% Java, tendo como consequência a nãonecessidade de instalação de qualquer código na máquinacliente.

Tipo 4 (Driver Java Puro e protocolo nativo) : neste casoas chamadas JDBC são convertidas para as chamadas naAPI nativa do SGBD pelo driver, que foi escrito totalmenteem Java. Este driver é independente de plataforma, umavez que é totalmente escrito em Java.

Tópicos Avançados 79

4.2.1 Obtendo os Drivers JDBCInformações sobre como obter drivers JDBC podem ser obtidas nosite http://www.javasoft.com/products/jdbc. Outra alterna-tiva é acessar as páginas dos fabricantes de SGBD, para veri�carse existe driver disponível.

4.3 Preparando o Banco de DadosOs exemplos deste livro usam um driver JDBC do tipo 4 para paraconectar com o SGBD HSQLDB. O HSQLDB (www.hsqldb.org)é um SGBD de código aberto desenvolvido em Java. Ele obedeceos padrões SQL e JDBC, e possui as seguintes características:

• Tamanho pequeno (100KB).

• Funciona como servidor, standalone e in-memory.

• Suporta transação.

• Suporta integridade referencial.

• Suporta procedimentos Armazenados em Java.

• Possibilita a atribuição de direitos de acessos.

Para instalá-lo execute as seguintes etapas:

1. Descompacte o arquivo hsqldb_1_7_1.zip em um diretó-rio qualquer. Por exemplo : c:\ (MS-Windows) ou /usr(Linux).

2. Adicione as classes do HSQLDB ao CLASSPATH:SET CLASSPATH=%CLASSPATH%;c:\hsqldb\lib\hsqldb.jar (MS-Windows)

ouexport CLASSPATH=$CLASSPATH:/usr/hsqldb/lib/hsqldb.jar (Linux)

Para iniciar a execução do HSQLDB em modo servidor, abrauma shell, mude para o diretório onde você deseja que o banco dedados seja gravado e digite:

80 Java na prática

java org.hsqldb.Server -database meudb

onde meudb é o nome do banco de dados. Para executar o geren-ciador grá�co para execução de comandos digite:

java org.hsqldb.util.DatabaseManager

A �gura 4.3 mostra a tela inicial do gerenciador do HSQLDB.Nessa tela o usuário deve selecionar a opção HSQL DatabaseEngine Server e pressionar o botão OK (o usuário é �sa� e a se-nha é ��). A �gura 4.4 mostra a tela principal do gerenciador doHSQLDB.

Figura 4.3 Tela inicial do gerenciador do HSQLDB.

Tópicos Avançados 81

Figura 4.4 Tela principal do gerenciador do HSQLDB.

Após isso é necessário criar uma base de dados no SGBD. Nosexemplos deste livro será usada uma base contendo dados sobrelivros, alunos e empréstimos de livros aos alunos. Não trataremosneste livro dos conceitos relacionados com banco de dados rela-cionais nem sobre a linguagem de consulta SQL. Existem váriostextos sobre o assunto onde o leitor pode buscar essas informações,tais como Elmasri e Navathe ou Silberschatz, Forth e Sudarshan(1997).

As tabelas 4.1 a 4.3 mostram as tabelas que formam a base dedados usada nos exemplos. O banco de dados é formado por trêstabelas. Uma para armazenar os dados dos alunos, outra parareceber os dados dos livros e uma terceira para conter os dadosdos empréstimos.

matricula nome1 Railer Costa Freire2 Alexandre Altoé Pigatti3 André M. A. Landro4 Ana Maria Freitas5 Claudia Maria Oliveira6 Alexandra Moreira

Tabela 4.1 Tabela alunos.

82 Java na prática

codlivro titulo volume1 Curso Pratico de Java 12 Curso Pratico de Java 23 Introdução a Compiladores 14 Fundamentos de Banco de Dados 15 Redes de Computadores Fácil 16 Redes de Computadores Fácil 27 Lógica matemática 18 Engenharia de Software para Leigos 19 Aprenda Computação Gráfica em duas horas 110 Aprenda Inteligência Artificial em 5 anos 1

Tabela 4.2 Tabela livros.

codlivro matricula dataemp datadev1 1 01/01/99 10/01/997 3 03/01/99 13/01/999 6 12/01/99 22/01/991 3 20/01/99 30/01/994 2 03/02/99 13/02/9910 2 12/02/99 22/02/99

Tabela 4.3 Tabela emprestimos.

Em um Banco de Dados Relacional cada tabela representaum conjunto de entidades ou relacionamentos entre entidades, ecada linha da tabela representa uma entidade particular ou umrelacionamento entre entidades. Assim, cada linha das tabelasalunos e livros representa um aluno e um livro respectivamente.Já na tabela emprestimos cada linha representa o relacionamentopor empréstimo de um livro a um aluno. Para estabelecer este tipode relacionamento em um Banco de Dados Relacional é precisocolocar na tabela que representa o relacionamento os atributoschaves de cada entidade que participa da relação. Atributos chavessão os atributos que identi�cam cada entidade. No caso dos alunosé seu número de matrícula, uma vez que pode existir dois alunoscom o mesmo nome. Já no caso de um livro o seu atributo chaveé o código do livro. Um Banco de Dados Relacional pode ser

Tópicos Avançados 83

representado pelo diagrama de classes da UML, como mostradopela �gura 4.5, onde cada tabela é vista como uma classe.

Figura 4.5 Diagrama de Classes do Banco de Dados.

CREATE TABLE ALUNOS (MATRICULA INT PRIMARY KEY,NOMEVARCHAR( 5 0 ) NOT NULL) ;

CREATE TABLE LIVROS (CODLIVRO INT PRIMARY KEY,TITULO VARCHAR( 5 0 ) NOT NULL,VOLUME INT NOT NULL) ;

CREATE TABLE EMPRESTIMOS (CODLIVRO INT NOT NULL,MATRICULA INT NOT NULL,DATAEMP DATE NOT NULL,DATADEV DATE,CONSTRAINT PK_EMP PRIMARYKEY

(CODLIVRO, MATRICULA, DATAEMP) ,CONSTRAINT FK_EMP1

FOREIGN KEY (CODLIVRO)REFERENCES LIVROS (CODLIVRO ) ,

CONSTRAINT FK_EMP2FOREIGN KEY (MATRICULA)REFERENCES ALUNOS (MATRICULA ) ) ;

Exemplo 4.1 Comandos em DDL para criação das tabelas emum SGBD relacional.

Para criação das tabelas em banco de dados relacional deve-seusar comandos DDL (Data De�nition Language). O exemplo 4.1mostra os comandos em DDL para a criação das tabelas do exem-plo.

84 Java na prática

4.4 Exemplo InicialO pacote java.sql fornece as classes e interfaces necessárias paraa conexão com uma base de dados e a posterior manipulação dosdados. As etapas para se criar uma aplicação cliente de um SGBDem Java são as seguintes:

1. Carregar o driver JDBC.

2. Estabelecer a conexão.

3. Criar um objeto Statement.

4. Executar o comando SQL por meio de um método do objetoStatement.

5. Receber o resultado, se for o caso.

Para comentar cada uma destas etapas utilizaremos o exem-plo 4.2 que mostra o código de uma aplicação que lista no dispo-sitivo de saída padrão o nome de todos os alunos.

import java . s q l . ∗ ;import java . net .URL;

class jdbc{

public stat ic void main ( St r ing a [ ] ){

try{

Class . forName ( " org . hsqldb . jdbcDr iver " ) ;Connection con =

DriverManager . getConnect ion (" jdbc : hsqldb : hsq l : // l o c a l h o s t " , " sa " , "" ) ;

Statement stmt = con . createStatement ( ) ;Resu l tSet r s = stmt . executeQuery

( "SELECT NOME FROM alunos " ) ;System . out . p r i n t l n ( "Nome" ) ;while ( r s . next ( ) )

System . out . p r i n t l n ( r s . g e tS t r i ng ( "nome" ) ) ;stmt . c l o s e ( ) ;con . c l o s e ( ) ;

} catch ( Exception e )

Tópicos Avançados 85

{System . out . p r i n t l n ( e . getMessage ( ) ) ;e . pr intStackTrace ( ) ; }

}}

Exemplo 4.2 Código para listar o nome dos alunos.

4.4.1 Carregando o DriverA primeira etapa é carregar o driver JDBC. Para isso é usado ométodo estático forName() da classe Class. Em caso de erro estemétodo lança a exceção ClassNotFoundException. O métodocria uma instância do driver e o registra junto ao DriverManager.No exemplo 4.2 é carregado o driver JDBC que vem junto com oHSQLDB.

4.4.2 Estabelecendo a conexãoA segunda etapa é realizada por meio do método estáticogetConnection() da classe DriverManager. Este método, na suaforma mais simples, recebe como parâmetro um URL que faz refe-rência a base de dados e retorna um objeto da classe Connection,que representa a conexão com a base de dados. URLs (UniformResource Locator) são endereços de recursos na Web. No entanto,existem algumas particularidades no que refere a URLs que fazemreferência à Banco de Dados. O formato padrão deste tipo deURL é o seguinte:

jdbc:<subprotocolo>:<identificador>

onde:

1. jdbc representa o protocolo;

2. <subprotocolo> se refere ao driver ou ao mecanismo de co-nexão com o Banco de Dados, que pode ser suportado porum ou mais drivers. No exemplo 4.2 o subprotocolo é repre-sentado pela palavra hsqldb.

86 Java na prática

3. <identi�cador> é a parte onde se identi�ca o Banco de Da-dos. A forma de identi�cação varia de acordo com o subpro-tocolo. Como o HSQLDB gerencia apenas uma base dadosem uma determinada máquina, então é preciso apenas indi-car a máquina onde está localizado o gerenciador. Isto é feitopor meio da palavra chave hsql: seguida da identi�cação damáquina. Como o gerenciador está executando na máquinalocal, então a identi�cação da máquina é localhost.

Tanto o uso local como remoto pode requerer a identi�cação dousuário, assim como uma senha. Estes dados podem ser passadoscomo parâmetro no método getConnection(). No exemplo 4.2 onome do usuário é �sa� e a senha é ��. Alguns exemplos de URLsestão descritos na tabela 4.4.

Arquivo Descriçãojdbc:odbc:biblioteca Referencia fonte de da-

dos biblioteca via ponteJDBC-ODBC.

jdbc:odbc:bd1;CacheSize=20 Referencia fonte de dados bd1via ponte JDBC-ODBC. É de�-nido o tamanho do cache.

jdbc:odbc:contas;UID=ana;PWD=sght Referencia fonte de dadoscontas via ponte JDBC-ODBC.É passado também o nome dousuário e a senha.

jdbc:oracle:thin:@sap.ufv.br:1521:agenda Referencia fonte de dadosagenda no host remotosap.dpi.ufv.br via subpro-tocolo oracle. É passadotambém o número da portausada no acesso.

jdbc:postgresql://localhost/dpi Referencia fonte de dados dpino host local via subprotocolopostgresql.

Tabela 4.4 Exemplos de URLs JDBC.

4.4.3 Criando e Executando ComandosÉ necessário criar um ou mais objetos da classe Statement, quepossui os métodos necessários para manipular a base de dados.

Tópicos Avançados 87

Este objeto é criado por meio do método createStatement() doobjeto da classe Connection.

Statement stmt = con.createStatement();

Podemos então usar o objeto Statement para executar co-mandos de manipulação do Banco de Dados. No exemplo 4.2 oobjetivo é recuperar os nomes dos alunos. Este objetivo é atingidopor meio da execução do comando SQL

SELECT nome FROM alunos

passado como parâmetro para o método executeQuery() doobjeto Statement. Este método retorna um objeto que imple-menta a interface ResultSet, e que fornece os meios de acesso aoresultado da consulta. Muitas vezes, como no exemplo, o resul-tado de uma consulta é uma tabela com várias linhas. O progra-mador pode utilizar o objeto ResultSet para acessar cada linhada tabela resultante em sequência. Para isso o objeto mantém umapontador para a linha corrente, chamado de cursor. Inicialmenteo cursor é posicionado antes da primeira linha e é movimentadopara próxima linha por meio de chamadas ao método next() doobjeto ResultSet.

O método executeQuery() é usado apenas para consul-tas. Além desse método, a classe Statement possui o mé-todoexecute() que retorna múltiplos ResultSets e o métodoexecuteUpdate(), para atualização (comandos INSERT, DE-LETE e UPDATE da linguagem SQL), criação de tabelas (co-mandos CREATE TABLE) e remoção (DROP TABLE). O valorde retorno do método executeUpdate() é um valor inteiro in-dicando o número de linhas afetadas ou zero no caso do DROPTABLE. Um exemplo de um comando para inserir um novo alunona tabela seria:stmt.executeUpdate("INSERT INTO alunos VALUES(7,'Ana mia')");

4.5 Recuperando ValoresO objeto ResultSet possui os métodos necessários para recupe-rar os valores de cada coluna da tabela, bastando passar o nome

88 Java na prática

da coluna como parâmetro. No exemplo 4.2 é utilizado métodogetString() para recuperar o valor da coluna �nome�. Os mé-todos para recuperação possuem o formato geral getXXX(), ondeXXX é o nome de um tipo. A tabela 4.5 mostra qual o métodomais indicado para cada tipo SQL.

Tabela 4.5 Tabelas com os métodos indicados para recuperaçãode valores.

É possível recuperar o valor da coluna passando como parâ-metro o número da coluna no lugar de seu nome. Neste caso arecuperação do nome do aluno no exemplo 4.2 �caria na seguinteforma:

rs.getString(1);

Tópicos Avançados 89

4.6 Trabalhando com MetadadosMetadados são informações sobre dados. No caso de bases dedados relacionais estas informações são os nomes das tabelas, osnomes e os tipos da colunas, os nomes dos índices, os nomes dasrestrições de integridade e outras informações que descrevem osdados. Em muitas situações é necessário ter acesso a essas infor-mações para poder implementar certas aplicações. Por exemplo,uma aplicações pode não saber de antemão o número e nem osnomes das colunas que serão recuperadas em uma consulta. Semessas informações é impossível percorrer as colunas para recuperaros valores. No entanto, acessando os metadados do resultado daconsulta é possível contornar esse problema.

Para recuperar essas informações o pacote java.sql forneceos objetos ResultSetMetaData e DatabaseMetaData. O primeiroobjeto é obtido a partir do método de instância getMetaData()do objeto ResultSet e é utilizado para obter os metados de umresultado de uma consulta, tais como nome e número de colunasretornadas. Já o segundo objeto é obtido a partir do método deinstância getMetaData() do objeto Connection e é utilizado paraobter os metados da base de dados, tais como nível de isolamento,suporte a comandos SQL, suporte a transações, etc.

O exemplo 4.3 mostra o código de um programa que recebena linha de comando uma consulta SQL e imprime o resultadoda consulta. Como o programa não sabe o nome e o númerode colunas que resultarão da consulta é necessário fazer uso dosmetadados para produzir uma saída adequada.

import java . s q l . ∗ ;

public c lass TestaMeta{

void accessDB ( St r ing consu l ta ){

try{

Class . forName ( " org . hsqldb . jdbcDr iver " ) ;Connection connect ion ;connect ion=DriverManager . getConnect ion (

" jdbc : hsqldb : hsq l : // l o c a l h o s t " , " sa " , "" ) ;

Statement statement = connect ion . createStatement ( ) ;

90 Java na prática

boolean hasResu l t s = statement . execute ( consu l ta ) ;i f ( hasResu l t s ){

Resu l tSet r e s u l t = statement . ge tResu l tSe t ( ) ;i f ( r e s u l t !=null ) d i sp l ayRe su l t s ( r e s u l t ) ;

}connect ion . c l o s e ( ) ;

}catch ( Exception ex ){

ex . pr intStackTrace ( ) ;}

}

void d i sp l ayRe su l t s ( Resu l tSet r ) throws SQLException{

ResultSetMetaData rmeta = r . getMetaData ( ) ;

int nCol=rmeta . getColumnCount ( ) ;

S t r ing nomeCol [ ]=new St r ing [ nCol ] ;for ( int i =1; i<=nCol;++ i )

nomeCol [ i −1] = rmeta . getColumnName( i ) ;

while ( r . next ( ) ){

for ( int i =1; i<=nCol;++ i ){

System . out . p r i n t l n (nomeCol [ i−1]+"="+r . g e tS t r i ng ( i ) ) ;

i f ( i==nCol )System . out . p r i n t ( "\n" ) ;

}}

}

public stat ic void main ( St r ing args [ ] ){new TestaMeta ( ) . accessDB ( args [ 0 ] ) ;

}}

Exemplo 4.3 Uso de metados em consulta.

Ao invocar o programa deve-se passar o comando SQL entreaspas, como mostrado abaixo:

java -cp ".;/hsqldb/lib/hsqldb.jar"TestaMeta "SELECT * FROMLIVROS"

Tópicos Avançados 91

4.7 Trabalhando com datasSQL2 de�ne vários tipos relacionados com datas. A tabela 4.6mostra os tipos de dados para datas segundo Elmasri e Navathe.

tipo Descrição e ExemploDATE Possui dez posições e o formato padrão é

'AAAA-MM-DD' , onde AAAA representa o ano, MMrepresenta o mês e DD representa o dia. Exem-plo: '2003-09-07' .

TIME Possui oito posições e o formato padrão é'HH:MM:SS' , onde HH representa a hora, MMrepresenta os minutos e SS representa os se-gundos. Exemplo: '10:30:12' .

TIME(i) Neste tipo de dados a precisão de segundos éincrementada de i posições para representarfraçoes de segundos.

TIME WITH TIME ZONE Neste tipo de dados é incluída seis posiçõespara representar o deslocamento em relaçãoo meridiano de Greenwich, que pode ir de+13:00 a -12:59.

TIMESTAMP Inclui os campos DATE e TIME e mais seisposições para frações de segundos.

Tabela 4.6 Tipos de dados relacionados com datas em SQL2.

O exemplo 4.4 mostra um método que recebe como parâmetrosuma conexão com o banco de dados, a matrícula de um aluno, ocódigo de um livro e uma data e atualiza a tabela de emprestimos,colocando a data atual no campo DATADEV no registro que temcomo chave primária a matrícula de um aluno, o código do livroe data de emprestimo iguais aos recebidos como parãmetros.

. . .void devolve ( Connection con , int mat , int cod l i v ro ,

S t r ing dataemp ){

java . u t i l . Date data = new java . u t i l . Date ( ) ;SimpleDateFormat f = new SimpleDateFormat ( "yyyy−MM−dd" ) ;S t r ing datadev = f . format ( data ) ;try{

Statement statement = con . createStatement ( ) ;

statement . executeUpdate (

92 Java na prática

"UPDATE EMPRESTIMOS SET DATADEV = '"+datadev+" ' WHERE MATRICULA="+mat+" AND CODLIVRO="+cod l i v r o+" AND DATAEMP='"+dataemp+" ' " ) ;

}catch ( Exception ex ){

ex . pr intStackTrace ( ) ;}

}. . .

Exemplo 4.4 Método para devolução de um livro.

4.8 Transações e Nível de Isolamento4.8.1 TransaçãoUma Transação é um conjunto de operações realizadas, sobre umbanco de dados, tratadas atomicamente, em outras palavras, outodas operações são realizadas e o seu resultado registrado perma-nentemente na base de dados ou nenhuma operação é realizada.Por default, o banco de dados trata cada operação como umatransação, realizando implicitamente uma operação de commit ao�m de cada uma delas. A operação de commit registra perma-nentemente o resultado da transação na tabela.

No entanto, existem situações onde é necessário tratar comouma transação um conjunto de operações, e não apenas uma ope-ração. Por exemplo, suponha que em um Banco de Dados de umaagência bancária exista uma tabela com informações sobre contascorrente e outra com informações sobre contas de poupança. Su-ponha também que um cliente deseje transferir o dinheiro da contacorrente para a conta de poupança. Essa transação é constituídapelas seguintes operações:

1. Caso exista saldo su�ciente, subtração do montante datransferência do saldo da conta corrente.

2. Adição do montante da transferência ao saldo da conta depoupança.

Tópicos Avançados 93

As operações acima precisam ocorrer totalmente ou o efeitode nenhuma delas deve ser registrado na base de dados. Casocontrário podemos ter uma situação onde o dinheiro sai da contacorrente mas não entra na conta da poupança. Este estado, ondeas informações do banco de dados não re�ete a realidade, é cha-mado de estado inconsistente.

De modo a obter esse controle sobre as transações é necessáriodesabilitar o modo de auto-commit. Isto é feito por meio métodosetAutoCommit() do objeto Connection.

con.setAutoCommit(false);

A partir do momento em que é executado o comando acima, oprogramador é responsável pela indicação do �nal da transação,por meio da execução do método commit() do objeto Connection.

con.commit();

Se alguma exceção for levantada durante a execução de qual-quer operação da transação, o programador pode usar o métodorollback() para desfazer as operações já realizadas após o úl-timo commit(). O exemplo 4.5 mostra um trecho de código queprocura explorar o uso do commit() e rollback().

. . .con . setAutoCommit ( fa l se ) ;try{

Statement stmt = con . createStatement ( ) ;stmt . executeUpdate (

"UPDATE EMPRESTIMOS SET DATADEV='"+data+" ' WHERE CODLIVRO="+cod l i v r o+" AND MATRICULA="+mat ) ;

stmt . executeUpdate ("UPDATE RESERVA SET LIBERADO='s ' WHERE CODLIVRO="+cod l i v r o ) ;

con . commit ( ) ;stmt . c l o s e ( ) ;

}catch ( Exception e ){

con . r o l l b a ck ( ) ;}f ina l ly{

try

94 Java na prática

{con . setAutoCommit ( true ) ;

}catch ( SQLException s q l e ){

System . out . p r i n l n ( s q l . getMessage ( ) ) ;}

}. . .

Exemplo 4.5 Uso dos métodos commit() e rollback().

4.8.2 Níveis de isolamentoAlém da atomicidade outra propriedade desejável em uma tran-sação é o isolamento. A propriedade de isolamento implica queuma transação não é afetada pelas operações realizadas por outrastransações que estão sendo realizadas concorrentemente.

O isolamento completo entre transações prejudica muito a exe-cução concorrente de transações e pode ser desnecessário em deter-minados tipos de aplicações. Por isso os SGBDs permitem que oprogramador de�na o nível de isolamento entre as transações. Deacordo com o relaxamento do isolamento certos problemas devidoa interferência entre as transações podem ocorrer e o programadordeve estar ciente disso.

O número de níveis de isolamento, sua nomenclatura e carac-terísticas dependem do SGBD utilizado. Descreveremos os níveisde isolamento de�nidos no pacote java.sql. Para exempli�car osproblemas que podem ocorrer devido a interferência entre tran-sações utilizaremos um banco de dados exemplo com a seguintetabela:

NumCC Saldo10189-9 200,0020645-7 300,00

Tabela 4.7 Tabela contas.

Read uncommitted : é o nível menos restritivo. Podem ocor-rer leituras de registros não committed (Dirty reads). Usado

Tópicos Avançados 95

onde não existe concorrência ou não existem alteraçõesem registros ou quando essas alterações não são relevan-tes. Exemplo de problema: Uma transação deve transferirR$50,00 da conta 10189-9 para a conta 20645-7 e uma se-gunda transação deve somar R$70,00 à conta 10189-9. A�gura a seguir mostra o estado inicial e o estado �nal dese-jado da tabela:

Estado antes dastransações

NumCC Saldo10189-9 200,0020645-7 300,00

⇒ Estado desejado apósas transações

NumCC Saldo10189-9 220,0020645-7 350,00

Cada transação é divida em operações de leitura e escrita.Suponha que o intercalamento das operações seja feito comomostrado abaixo:

Tempo Transação 1 Transação 21 leitura do saldo 10189

2 Escrita do Saldo-50,00

3 leitura do saldo 10189

4 leitura do saldo 20645

5 (falha na transação,

realizado rollback)

6 Escrita do Saldo+70,00

Como a transação 1 falhou o valor lido pela transação 2 éum valor que não foi tornado permanente na tabela. Isto fazcom que a transação 2 opere sobre um resultado desfeito. Atabela resultante, mostrada a seguir, estará em um estadoinconsistente.

96 Java na prática

NumCC Saldo10189-9 220,0020645-7 300,00

Read committed : Somente registros committed podem ser li-dos. Evita o problema de Dirty reads, no entanto duas lei-turas de um mesmo item em uma mesma transação podempossuir valores diferentes, uma vez que o valor pode ser mu-dado por outra transação entre duas leituras.

Repeatable Read : Somente registros committed podem ser li-dos, além disso impede a alteração de um item lido pelatransação. Evita o problema de Dirty reads e o problema donon-repeatable read .

Serializable : é o nível mais restritivo. Impede Dirty reads enon-repeatable reads. Além disso impede o problema dephantom reads onde um conjunto de registros satisfazendo acondição WHERE é lido enquanto outra transação insere novosregistros que satisfazem a condição.

Para se de�nir o nível de isolamento na linguagem Java usa-seum objeto DatabaseMetaData que é obtido por meio do métodogetMetaData() do objeto Connection. Primeiro é preciso saberse o SGBD suporta o nível de isolamento desejado para depoisde�nir o nível. O exemplo 4.6 mostra uma sequência típica co-mandos.

DatabaseMetaData meta=con . getMetaData ( ) ;

i f (meta . suppo r t sTran sa c t i on I s o l a t i onLeve l (con .TRANSACTION_READ_COMMITTED) ) {con . s e tT r an s a c t i o n I s o l a t i o n (con .TRANSACTION_READ_COMMITTED) ;

}else return ;

Exemplo 4.6 Estabelecimento do nível de isolamento.

Tópicos Avançados 97

A tabela 4.8 mostra as constantes relacionadas com os níveisde isolamento da linguagem Java. A constante TRANSACTION_NONEindica que não existe suporte para transações:

ConstanteTRANSACTION_NONETRANSACTION_READ_UNCOMMITTEDTRANSACTION_READ_COMMITTEDTRANSACTION_REPEATABLE_READTRANSACTION_SERIALIZABLE

Tabela 4.8 Tabela com as constantes dos níveis de isolamento.

4.9 Prepared StatementsCada vez que se executa um comando SQL, passado por meio deum String, ele é analisado pelo processador de SQL do SGBDque irá, no caso do String estar sintaticamente correta, gerarum código binário que será executado para atender à solicitação.Todo esse processo é caro e sua execução repetidas vezes terá umimpacto signi�cativo sobre o desempenho da aplicação e do SGBDcomo um todo.

Existem duas abordagens para se tentar solucionar esse pro-blema: Comandos preparados (prepared statements) e procedi-mentos armazenados (stored procedures). Discutiremos primeira-mente os prepared statements.

Prepared Statement é indicado nos casos onde um comandoserá executado várias vezes em uma aplicação. Neste caso é me-lhor compilar o comando uma única vez e toda vez que for neces-sário executá-lo basta enviar o comando compilado. Além disso, ocomando pré-compilado pode ser parametrizado, tornando-o maisgenérico e, portanto, apto a expressar um maior número de con-sultas.

Para criar um Prepared Statement é necessário obter um objetoPreparedStatement por meio do método prepareStatement()do objeto Connection, passando como argumento um comandoSQL.

98 Java na prática

PreparedStatement pstmt= con . prepareStatement ("INSERT INTO alunos ( matr icula , nome ) VALUES( ? , ? ) " ) ;

O comando anterior insere uma nova linha na tabela alunoscom os valores das colunas matricula e nome passados por pa-râmetro. O caractere `?' representa o parâmetro. Este tipo decomando só possui valor tendo parâmetros, caso contrário teriapouca chance de ser reutilizado. Para executar o comando de-vemos especi�car o valor dos parâmetros e executar o comando,como mostrado no exemplo seguinte:

. . .pstmt . c l earParameter s ( ) ;pstmt . s e t I n t ( 1 , 8 ) ;pstmt . s e t S t r i n g (2 , "Clara Maria" ) ;pstmt . executeUpdate ( ) ;. . .

Antes de especi�car os parâmetros é necessário limpar qual-quer outro parâmetro previamente especi�cado. Para especi�caros parâmetros é utilizado um conjunto de métodos com o nome noformato setXXX(), onde XXX é o tipo sendo passado. O primeiroparâmetro do método setXXX() é o índice da ocorrência do ca-ractere `?' que será substituído pelo valor. O segundo parâmetroé o valor que será transmitido.

4.10 Procedimentos Armazenados (Sto-red Procedures)

A maioria dos SGBDs possuem algum tipo de linguagem de pro-gramação interna, como por exemplo a PL/SQL do Oracle oumesmo Java e C/C++. Estas linguagens permitem que os desen-volvedores insiram parte do código da aplicação diretamente nobanco de dados e invoquem este código a partir da aplicação. Estaabordagem possui as seguintes vantagens:

• Reuso de código - o código precisa ser escrito apenas uma

Tópicos Avançados 99

vez e usado em várias aplicações, comunicando com váriaslinguagens.

• Independência entre a aplicação e o esquema do BD - se o es-quema mudar, provavelmente será necessário mudar apenasos procedimentos armazenados.

• Desempenho - os procedimentos são previamente compila-dos, eliminando esta etapa.

• Segurança - as aplicações possuem privilégio apenas paraexecução de procedimentos armazenados, evitando assimacessos não autorizados.

A sintaxe dos procedimentos armazenados depende do SGBDem questão. Utilizaremos um exemplo em PL/SQL. No exem-plo 4.7 o procedimento retorna o nome do aluno a partir de suamatrícula.

CREATEOR REPLACE PROCEDURE sp_obtem_nome( id IN INTEGER, Nome_aluno out VARCHAR2) IS

BEGINSELECT nome INTO Nome_alunoFROM alunosWHERE matr i cu la = id ;

END;/

Exemplo 4.7 Procedimento PL/SQL.

Para invocar o procedimento anterior de dentro de uma apli-cação Java é necessário obter um objeto CallableStatement pormeio do método prepareCall() do objeto Connection, passandocomo argumento a chamada ao procedimento armazenado. Oexemplo 4.8 mostra a chamada ao procedimento de�nido no exem-plo 4.7.

Cal lab leStatement cstmt =con . prepareCa l l ( " { CALL sp_obtem_nome (? , ? ) } " ) ;

cstmt . reg i s terOutParameter ( 2 , Types .VARCHAR) ;

100 Java na prática

cstmt . s e t I n t ( 1 , 3 ) ;cstmt . execute ( ) ;System . out . p r i n l n ( "O nome do aluno numero 3 : "

+cstmt . g e tS t r i ng ( 2 ) ;

Exemplo 4.8 Chamada do procedimento PL/SQL.

4.11 Agenda Eletrônica versão JDBCA seguir é mostrado o código dos módulos que compõem a versãoda agenda eletrônica que usa um gerenciador de banco de dadospara armazenar os dados. O gerenciador de banco de dados utili-zado é o HSQLDB, mas o programa pode ser facilmente adaptadopara outros gerenciadores, bastando para isso alterar as linhas re-lacionadas com a carga do driver e com a obtenção da conexão noarquivo Agenda.java. A �gura 4.6 mostra a tela principal destaversão da agenda eletrônica.

Figura 4.6 Tela principal da agenda eletrônica.

O exemplo 4.9 mostra a classe Agenda que é a principal doprograma. Ao iniciar, a classe cria uma instância da classeAgendaIntUsuario que implementa a interface com o usuário.

Tópicos Avançados 101

Além disso, no início de seu processamento, a classe Agenda es-tabelece uma conexão com o gerenciador de banco de dados quepermanece ativa até que o programa termine. Esta não é a me-lhor forma de se manter conexões com o banco de dados, a não serque o sistema funcione com poucos usuários. O mais adequado éque as conexões sejam estabelecidas apenas durante os acessos aobanco de dados e que sejam, preferencialmente, gerenciadas emum pool de conexões1.

Note que o método exibeTodos() usa no comando SQL a ex-pressão LIKE `...%'. Esta expressão é usada quando se desejaobter cadeias de caracteres com um trecho idêntico ao da expres-são. O caractere `%' funciona como um curinga que �casa� comqualquer sequência de palavras. Portanto, a expressão utilizadano exemplo determina que todos os nomes que iniciam com asequência digitada devem ser recuperados.

import java . s q l . ∗ ;

public c lass Agenda{

Connection con=null ;AgendaIntUsuario a i = null ;

// Construtorpublic Agenda ( ){}

public void i n i c i a ( ){

a i = new AgendaIntUsuario ( this ) ;a i . show ( ) ;try{

Class . forName ( " org . hsqldb . jdbcDr iver " ) ;con=DriverManager . getConnect ion (

" jdbc : hsqldb : hsq l : // l o c a l h o s t " , " sa " , "" ) ;} catch ( Exception e ){

a i . s e tS ta tu s ( e . getMessage ( ) ) ;}

}

public void closeAgenda ( )

1pool de conexões é uma camada de software que �ca entre o programae o SGBD e que é responsável pelo manutenção e reuso compartilhado dasconexões com o SGBD, de forma a promover um uso otimizado deste recurso.

102 Java na prática

{i f ( con != null )try{con . c l o s e ( ) ; } catch ( Exception e ) {} ;

}

public void i n s e r i r ( S t r ing n , S t r ing t ){

i f ( con == null ) return ;

try{

Statement stmt = con . createStatement ( ) ;S t r ing s = "INSERT INTO pessoas (nome , t e l e f o n e ) "+

" va lues ( ' "+n+" ' , ' "+t+" ' ) " ;stmt . executeUpdate ( s ) ;

stmt . c l o s e ( ) ;}catch ( Exception e ) { a i . s e tS ta tu s ( e . getMessage ( ) ) ; }

}

public void con su l t a r ( S t r ing n){

i f ( con == null ) return ;

try{

Statement stmt = con . createStatement ( ) ;Resu l tSet r s = stmt . executeQuery (

"SELECT Tele fone FROM pessoas WHERE Nome='"+n+" ' " ) ;

i f ( r s . next ( ) ){

a i . setNome ( r s . g e tS t r i ng ( "Nome" ) ) ;a i . setNome ( r s . g e tS t r i ng ( "Te le fone " ) ) ;

}stmt . c l o s e ( ) ;

} catch ( Exception e ){ a i . s e tS ta tu s ( e . getMessage ( ) ) ; }}

public void remover ( S t r ing n){

i f ( con == null ) return ;

try{

Statement stmt = con . createStatement ( ) ;i f ( stmt . execute (

"DELETE FROM pessoas WHERE Nome='"+n+" ' " ) )a i . s e tS ta tu s ( " removido" ) ;

elsea i . s e tS ta tu s ( "não encontrado " ) ;

stmt . c l o s e ( ) ;} catch ( Exception e ){ a i . s e tS ta tu s ( e . getMessage ( ) ) ; }

}

public void exibeTodos ( S t r ing n)

Tópicos Avançados 103

{i f ( con == null ) return ;

try{

Statement stmt = con . createStatement ( ) ;Resu l tSet r s = stmt . executeQuery (

"SELECT Nome , Te le fone FROM pessoas " +" WHERE Nome LIKE ' " + n + "%'" ) ;

a i . s e tSa ida ( "" ) ;while ( r s . next ( ) ){

a i . p r i n t l n ( "Nome : "+r s . g e tS t r i ng ( "Nome" ) ) ;a i . p r i n t l n ( "Tel . : "+r s . g e tS t r i ng ( "Te le fone " ) ) ;

}stmt . c l o s e ( ) ;

} catch ( Exception e ){ a i . s e tS ta tu s ( e . getMessage ( ) ) ; }}

public stat ic void main ( St r ing args [ ] ){new Agenda ( ) . i n i c i a ( ) ;

}

}

Exemplo 4.9 Arquivo Agenda.java.

import javax . swing . ∗ ;

public c lass AgendaIntUsuario extends JFrame {

Agenda ag =null ;

public AgendaIntUsuario (Agenda a ){

ag =a ;initComponents ( ) ;

}

private void initComponents ( ){

jPanel1 = new javax . swing . JPanel ( ) ;jLabe l1 = new javax . swing . JLabel ( ) ;jTextFieldNome = new javax . swing . JTextFie ld ( ) ;jLabe l2 = new javax . swing . JLabel ( ) ;jTextFie ldTe l = new javax . swing . JTextFie ld ( ) ;jPane l2 = new javax . swing . JPanel ( ) ;j S c ro l lPane1 = new javax . swing . JScro l lPane ( ) ;jTextAreaSaida = new javax . swing . JTextArea ( ) ;jPane l3 = new javax . swing . JPanel ( ) ;

104 Java na prática

jLabe lS ta tus = new javax . swing . JLabel ( ) ;jPane l4 = new javax . swing . JPanel ( ) ;jButtonCons = new javax . swing . JButton ( ) ;jButtonIns = new javax . swing . JButton ( ) ;jButtonRem = new javax . swing . JButton ( ) ;jButtonLi s t = new javax . swing . JButton ( ) ;

addWindowListener (new java . awt . event .WindowAdapter ( ){

public void windowClosing (java . awt . event .WindowEvent evt )

{exitForm ( evt ) ;

}} ) ;

jPane l1 . setLayout (new java . awt . GridLayout ( 2 , 2 ) ) ;

jLabe l1 . setText ( "Nome : " ) ;jPane l1 . add ( jLabe l1 ) ;

jPane l1 . add ( jTextFieldNome ) ;

jLabe l2 . setText ( "Te le fone : " ) ;jPane l1 . add ( jLabe l2 ) ;

jPane l1 . add ( jTextFie ldTe l ) ;

getContentPane ( ) . add (jPanel1 , java . awt . BorderLayout .NORTH) ;

jTextAreaSaida . setColumns ( 6 0 ) ;jTextAreaSaida . s e tEd i t ab l e ( fa l se ) ;jTextAreaSaida . setFont (new java . awt . Font ( "Cour ier " , 0 , 1 0 ) ) ;

jTextAreaSaida . setRows ( 1 2 ) ;j S c ro l lPane1 . setViewportView ( jTextAreaSaida ) ;

jPane l2 . add ( jSc ro l lPane1 ) ;

getContentPane ( ) . add ( jPanel2 ,java . awt . BorderLayout .CENTER) ;

jPane l3 . setLayout (new java . awt . BorderLayout ( ) ) ;

jLabe lS ta tus . setBackground (new java . awt . Color ( 0 , 2 5 5 , 2 5 5 ) ) ;

jLabe lS ta tus . setForeground (new java . awt . Color ( 2 5 5 , 0 , 1 0 2 ) ) ;

jLabe lS ta tus . se tHor izonta lAl ignment (SwingConstants .LEFT) ;

jLabe lS ta tus . s e tVer t i ca lA l i gnment (SwingConstants .TOP) ;

jLabe lS ta tus . setBorder (new javax . swing . border . Tit l edBorder ( " Status " ) ) ;

jPane l3 . add ( jLabe lStatus ,

Tópicos Avançados 105

java . awt . BorderLayout .CENTER) ;

getContentPane ( ) . add ( jPanel3 ,java . awt . BorderLayout .SOUTH) ;

jPanel4 . setLayout (new java . awt . GridLayout ( 4 , 0 ) ) ;

jButtonCons . setText ( "Consultar " ) ;jButtonCons . addAct ionListener (

new java . awt . event . Act i onL i s t ene r ( ) {public void act ionPerformed (

java . awt . event . ActionEvent evt ) {jButtonConsActionPerformed ( evt ) ;

}} ) ;

jPane l4 . add ( jButtonCons ) ;

jButtonIns . setText ( " I n s e r i r " ) ;jButtonIns . addAct ionListener (

new java . awt . event . Act i onL i s t ene r ( ) {public void act ionPerformed (

java . awt . event . ActionEvent evt ) {jButtonInsActionPerformed ( evt ) ;

}} ) ;

jPane l4 . add ( jButtonIns ) ;

jButtonRem . setText ( "Remover" ) ;jButtonRem . addAct ionListener (

new java . awt . event . Act i onL i s t ene r ( ) {public void act ionPerformed (

java . awt . event . ActionEvent evt ) {jButtonRemActionPerformed ( evt ) ;

}} ) ;

jPane l4 . add ( jButtonRem ) ;

jButtonLi s t . setText ( " L i s t a r " ) ;jButtonLi s t . addAct ionListener (

new java . awt . event . Act i onL i s t ene r ( ) {public void act ionPerformed (

java . awt . event . ActionEvent evt ) {jButtonListAct ionPerformed ( evt ) ;

}} ) ;

jPane l4 . add ( jButtonLi s t ) ;

getContentPane ( ) . add ( jPanel4 ,java . awt . BorderLayout .EAST) ;

pack ( ) ;}

106 Java na prática

private void jButtonInsActionPerformed (java . awt . event . ActionEvent evt ) {

ag . i n s e r i r ( getNome ( ) , getTel ( ) ) ;}

private void jButtonConsActionPerformed (java . awt . event . ActionEvent evt ) {

ag . c on su l t a r ( getNome ( ) ) ;}

private void jButtonRemActionPerformed (java . awt . event . ActionEvent evt ){

ag . remover ( getNome ( ) ) ;}

private void jButtonListAct ionPerformed (java . awt . event . ActionEvent evt ){

ag . exibeTodos ( getNome ( ) ) ;}

private void exitForm ( java . awt . event .WindowEvent evt ){

ag . closeAgenda ( ) ;System . e x i t ( 0 ) ;

}

public stat ic void main ( St r ing args [ ] ) {new AgendaIntUsuario ( null ) . show ( ) ;

}

public void s e tS ta tu s ( java . lang . S t r ing s ) {jLabe lS ta tus . setText ( s ) ;

}

public void s e tSa ida ( java . lang . S t r ing s ) {jTextAreaSaida . setText ( s ) ;

}

public void pr in t ( java . lang . S t r ing s ) {jTextAreaSaida . append ( s ) ;

}

public void p r i n t l n ( java . lang . S t r ing s ) {jTextAreaSaida . append ( ' \n '+s ) ;

}

public St r ing getNome ( ) {return jTextFieldNome . getText ( ) ;

}

public St r ing getTel ( ) {return jTextF ie ldTe l . getText ( ) ;

}

public void setNome ( St r ing s ) {

Tópicos Avançados 107

jTextFieldNome . setText ( s ) ;}

public void s e tTe l ( S t r ing s ) {jTextFie ldTe l . setText ( s ) ;

}

private javax . swing . JButton jButtonRem ;private javax . swing . JLabel jLabe lS ta tus ;private javax . swing . JTextFie ld jTextFieldNome ;private javax . swing . JButton jButtonLi s t ;private javax . swing . JPanel jPane l4 ;private javax . swing . JPanel jPane l3 ;private javax . swing . JPanel jPane l2 ;private javax . swing . JPanel jPane l1 ;private javax . swing . JScro l lPane jSc ro l lPane1 ;private javax . swing . JTextFie ld jTextFie ldTe l ;private javax . swing . JTextArea jTextAreaSaida ;private javax . swing . JButton jButtonIns ;private javax . swing . JButton jButtonCons ;private javax . swing . JLabel jLabe l2 ;private javax . swing . JLabel jLabe l1 ;

}

Exemplo 4.10 Arquivo AgendaIntUsuario.java.

4.12 Como con�gurar a ponte JDBC-ODBC

Apesar de estarmos utilizando um banco de dados que possui umdriver JDBC do tipo 4, é útil saber como con�gurar um driverdo tipo 1 (ponte JDBC-ODBC), para aqueles casos onde o SGBDnão possui um driver JDBC ou para testes rápidos. Por isso resol-vemos mostrar como utilizar a ponte JDBC-ODBC na plataformaMS-Windows. No entanto, é importante mencionar que a ponteJDBC-ODBC não é adequada para sistemas em produção, devidoao seu menor desempenho. Para utilizar a ponte JDBC-ODBCé necessário, primeiro, con�gurar o ODBC para acessar a basede dados. Por exemplo, suponha que a base de dados foi criadapor meio do MS-Access e foi gravada em um arquivo de nomeagenda.mdb. Na plataforma MS-Windows 98, a con�guração doODBC seria feita da seguinte forma:

108 Java na prática

Figura 4.7 Seleção da fonte de dados.

1. Execute o programa de con�guração do ODBC por meio doícone �ODBC de 32bits� do painel de controle.

2. Clique na pasta �NFD do sistema� e em seguida no botãode �adicionar...� (�gura 4.7). O NFD do sistema é esco-lhido no lugar da opção �NFD do usuário� porque permiteo compartilhamento da base de dados.

3. Selecione o driver do banco de dados e pressione o botão deconcluir. Por exemplo, se você estiver usando o MS-Accessselecione a opção �Driver para o Microsoft Access (*.mdb)�.

Tópicos Avançados 109

Figura 4.8 Seleção do driver ODBC no Windows.

4. Ao surgir a tela para a seleção do Banco de Dados, utilizeo botão �Selecionar...� para localizar o arquivo onde está abase de dados. Preencha a janela de texto �Nome da fontede dados� com o nome que será usado para referenciar abase.

Figura 4.9 Seleção da base de dados.

5. Feche o programa de con�guração.

110 Java na prática

Após isso a base de dados pode ser acessada via ponte JDBC-ODBC por meio do driver sun.jdbc.odbc.JdbcOdbcDriver e daURL jdbc:odbc:agenda, onde agenda é o nome dado à fonte dedados na con�guração do ODBC.

Capítulo 5

RMI

A RMI (Remote Method Invocation) é uma tecnologia que co-loca a programação com rede em um nível mais alto. RMI tornapossível que objetos distribuídos em uma rede se comuniquem deforma transparente para o programador utilizando chamadas deprocedimentos remotos.

O principal objetivo da tecnologia RMI é permitir que progra-madores desenvolvam programas distribuídos utilizando a mesmasintaxe e semântica de programas Java convencionais. Antes daintrodução da RMI no mundo Java, no JDK 1.1, para fazer comque dois objetos em máquinas diferentes se comunicassem, o pro-gramador deveria de�nir um protocolo de comunicação e escrevercódigo utilizando soquete para implementar este protocolo. ComRMI a maior parte do trabalho quem realiza é a máquina virtualJava.

Existem outras tecnologias, como CORBA, que também temcomo objetivo fazer com que objetos distribuídos em uma rede secomuniquem. Java tem suporte a CORBA, mas para projetos emum ambiente Java puro, a RMI é consideravelmente mais simplesque a CORBA.

111

112 Java na prática

5.1 Arquitetura RMIA arquitetura do Sistema RMI se baseia na distinção entre osconceitos de interface e implementação fornecidos pela plataformaJava. Por isso podemos ter aplicações clientes que conhecem ape-nas a interface de um objeto, e consegue utilizá-lo, mesmo estandosua implementação rodando em outra máquina virtual, possivel-mente até em outra máquina de uma rede.

Na �gura 5.1 vemos a separação possível entre um objeto cli-ente e um objeto servidor. A de�nição de um serviço é feita uti-lizando uma interface Java e a implementação é codi�cada comouma classe. 1

Figura 5.1 Arquitetura RMI.

5.2 Criando nossa agenda distribuídaSeguindo a mesma abordagem, esta seção mostra e explica osconceitos e o código de nossa agenda distribuída.

5.2.1 Passo a Passo1. Escrever e compilar a interface que descreve como serão as

chamadas do cliente ao servidor;1Uma interface Java não carrega consigo nenhum código. Temos então um

grande desacoplamento entre o objeto cliente e o servidor.

Tópicos Avançados 113

2. Escrever e compilar a classe que implementa a interface dopasso 1 (objeto servidor);

3. Gerar o stub do objeto distribuído;

4. Desenvolver o código que disponibiliza o objeto;

5. Escrever e compilar o código para o cliente RMI;

5.2.2 Implementando interface do objeto re-moto

O primeiro passo quando se deseja criar um objeto Remoto comRMI é implementar uma interface para este objeto. Essa inter-face deve herdar a interface Remote. É através dessa interfaceRemote, que não tem métodos, que a máquina virtual Java sabequal objeto pode ser disponibilizado para acesso remoto. Abaixotemos o exemplo da interface do nosso objeto:

public interface Agenda extendsjava . rmi . Remote{

public void i n s e r i r ( Pessoa p)throws java . rmi . RemoteException ;

public Pessoa getPessoa ( S t r ing nome)throws java . rmi . RemoteException ;

public java . u t i l . Enumeration getPessoas ( )throws java . rmi . RemoteException ;

}

Exemplo 5.1 Interface remota de Agenda.

A interface Remote deve ser herdada pela nossa interfaceAgenda.

Com objetos distribuídos sempre estaremos sujeitos a falha derede e problemas com algumas máquinas. Por isso todos os méto-dos devem poder levantar a exceção java.rmi.RemoteException.Para compilar a interface Agenda utilizamos o comando:

javac Agenda.java

114 Java na prática

5.2.3 Escrevendo objeto remotoEm seguida, deve ser criada a classe AgendaImp que implementaa interface Agenda. O exemplo 5.2 mostra a listagem da classeAgendaImpl que utiliza uma Hashtable.

import java . rmi . ∗ ;2 import java . u t i l . ∗ ;public class AgendaImpl

4 extends java . rmi . s e r v e r . UnicastRemoteObjectimplements Agenda{

6 Hashtable pes soas ;public AgendaImpl ( ) throws RemoteException{

8 super ( ) ;pe s soas = new Hashtable ( ) ;

10 }public void i n s e r i r ( Pessoa p)

12 throws RemoteException{pes soas . put (p . getNome ( ) , p ) ;

14 }public Pessoa getPessoa ( S t r ing nome)

16 throws RemoteException{return ( Pessoa ) pes soas . get (nome ) ;

18 }public Enumeration getPessoas ( )

20 throws RemoteException{return pes soas . e lements ( ) ;

22 }}

Exemplo 5.2 Implementa classe de Agenda Remota.

Uma classe de implementação deve usarUnicastRemoteObject para se ligar ao sistema RMI, comona linha 4 do exemplo 5.2. A classe AgendaImpl usa aUnicastRemoteObject através de herança, mas se uma classenão puder herdar UnicastRemoteObject , lembrando que Javanão possui herança múltipla, ela pode usar o seu métodoexportObject() para se ligar ao RMI.

Tópicos Avançados 115

Quando uma classe herda UnicastRemoteObject, seu constru-tor deve poder levantar a exceção java.rmi.RemoteException eeste construtor deve chamar o método super() que inicializa aclasse para usar RMI, como na linha 8.

Para compilar a AgendaImpl, usamos:

javac AgendaImpl.java

5.2.4 Gerando StubO próximo passo é a geração do stub de AgendaImpl. Nesta tarefao compilador rmic é utilizado. Este compilador cria uma classestub que é responsável por passar as chamadas de métodos remotospara o sistema RMI. Esse por sua vez se conecta ao objeto servidorpela rede e chama seus métodos.

Para gerar o stub, digitamos o comando:

rmic -v1.2 AgendaImpl

A opcao -v1.2 diz ao rmic para criar somente o stub para oRMI do Java 2. Na versão Java 1.1 o comando rmic criava umstub e um esqueleto, este esqueleto não é mais criado no Java 2.

Este comando gera um arquivo de stub para o RMI do Java 2com o nome AgendaImpl_Stub.class.

5.2.5 Desenvolvendo o código que disponibilizao objeto

Um dos problemas que se enfrenta quando temos vários objetosdistribuídos em uma rede trocando requisições e dados, é saberonde está determinado objeto. Este é uma das questões que re-solvemos com servidores de nomes ou diretórios.

O sistema RMI pode utilizar vários servidores diferentes, entreeles o JNDI (Java Naming and Directory Interface). O sistemaRMI nos disponibiliza um servidor de nomes simples chamadoRMI registry .

Chamamos de vinculação o processo de associar um objeto auma URL no sistema RMI. Todos os clientes que precisarem usareste objeto irão localizá-lo através desta URL.

116 Java na prática

import java . rmi . Naming ;2public c lass AgendaServer {

4 public AgendaServer ( ) {try {

6 Agenda a = new AgendaImpl ( ) ;Naming . reb ind ( "rmi :// l o c a l h o s t :1099/Agenda" , a ) ;

8 } catch ( Exception e ) {e . pr intStackTrace ( ) ;

10 }}

12public stat ic void main ( St r ing args [ ] ) {

14 new AgendaServer ( ) ;}

16 }

Exemplo 5.3 Servidor do objeto Agenda.

A linha 6 do exemplo 5.3 cria um objeto AgendaImpl, que seráo objeto disponibilizado para uso remoto.

A classe Naming fornece o método para armazenar a referênciade um objeto remoto Agenda no sistema de registro do RMI (linha7).

O método rebind() cria uma associação entre a URL"rmi://localhost:1099/Agenda" e o objeto AgendaImpl criadona linha anterior.

5.2.6 Escrevendo o clienteAbaixo temos o código de um cliente para o objeto Agenda quedisponibilizamos.

import java . rmi . ∗ ;2 public c lass AgendaCliente {

public stat ic void main ( St r ing [ ] a rgs ) {4 try {

Agenda a = (Agenda )6 Naming . lookup ( "rmi :// l o c a l h o s t :1099/Agenda" ) ;

Pessoa p1 = new Pessoa ( "João S i l v a " ,8 "999−99999" , "Rua Avenida" ) ;

Pessoa p2 = new Pessoa ( "Manuel" ,10 "888−8888" , "Avenida Rua" ) ;

a . i n s e r i r ( p1 ) ;12 a . i n s e r i r ( p2 ) ;

Tópicos Avançados 117

Pessoa b = a . getPessoa ( "Manuel" ) ;14 System . out . p r i n t l n (b . getNome () + " " +

b . ge tTe l e f one ( ) ) ;16 }catch ( Exception e ){

e . pr intStackTrace ( ) ;18 }

}20 }

Exemplo 5.4 Exemplo de cliente para o objeto Agenda.

A linha 6 do exemplo 5.4 utiliza o método lookup() para obteruma referência para o objeto remoto Agenda. Obtida esta refe-rência todo o resto do programa a utiliza como em uma programaconvencional.

O processo de transformar uma chamada de um método re-moto para um formato que possa ser transmitido pela rede é cha-mado de arranjo. O inverso, transformar o formato transmitidopela rede em classes Java e chamado desarranjo.

Um objeto para participar de um arranjo ou desarranjo deveser Serializable. Um objeto do tipo Pessoa é o parâmetro dométodo inserir da interface Agenda, por isso classe Pessoa deveser do tipo Serializable.

5.3 Testando tudoNesta seção as formas de testar o programa construído no Win-dows e no Linux serão mostradas.

5.3.1 No WindowsAbra três janelas do DOS e digite na primeira:

rmiregistry

Isso inicializa o servidor de nomes do RMI. Na segunda janelado DOS digite:

java AgendaServerrmiregistry

118 Java na prática

Agora temos o objeto servidor inicializado e vinculado ao ser-vidor de nomes. Na terceira janela do DOS digite o teste:

java AgendaClientermiregistry

que produzirá a seguinte saida:

Manuel 888-8888

5.3.2 No LinuxAbra uma shell e digite:

rmiregistry &

Isso inicializa o servidor de nomes do RMI. Em seguida digite:

java AgendaServer &

Digite agora o teste:

java AgendaCliente

que produzirá a seguinte saida:

Manuel 888-8888

Capítulo 6

CORBA

A tecnologia RMI provê, para a plataforma Java, uma forma e�-ciente e intuitiva de trabalhar com objetos distribuídos. Contudo,alguns problemas surgem quando os sistemas vão se tornando maiscomplexos. Características como interoperabilidade com diferen-tes componentes, possivelmente escritos em várias linguagens eplataformas diferentes, passam a ser exigidas. Interfaciamentocom sistemas legados são também características que grandes sis-temas �reais� geralmente pedem. Neste cenário temos aplicaçõesescritas em COBOL, C, ADA e outras linguagens, rodando emSistemas Operacionais distintos como Windows, UNIX e VMS.Uma abordagem para tratar de forma homegênia esse problema éo uso de CORBA.

Neste Capítulo serão mostrados os conceitos básicos deCORBA e como criar e utilizar objetos em um ORB, escrevendo-os em Java. Na seção 6.2 um exemplo de objetos escritos em Javainteroperando com objetos escritos em C será mostrado.

6.1 O que é CORBA?CORBA (Common Object Request Broker Architecture) é um con-junto de padrões que de�nem o que é chamado de um sistema deobjetos distribuídos. Esses padrões são escritos pelo OMG (Object

119

120 Java na prática

Management Group). O OMG é composto por mais de 700 empre-sas com o propósito comum de utilizar esse conjunto de protocolospadrões. CORBA de�ne o protocolo de interação entre objetos.Uma das diretrizes desses padrões é que objetos podem ser escri-tos em diferentes linguagens de programação, rodar em diferentessistemas operacionais, hospedados em diferentes máquinas.

O meio de comunicação entre os objetos é o ORB (Object Re-quest Broker). O ORB pode transmitir mensagens entre objetos,sendo que estes objetos podem estar na mesma máquina ou emmáquinas diferentes na rede. No primeiro caso, o ORB deve oti-mizar a troca de mensagens.

O ORB é especi�co da implementação CORBA, mas ORBsde diferentes fornecedores devem poder se comunicar, transparen-temente, utilizando um protocolo comum. Isso signi�ca que nóspodemos usar um ORB para desenvolver os objetos, e mais tarde,por qualquer motivo, podemos usar outro ORB para interagir comnossos objetos.

Para possibilitar que os objetos CORBA sejam escritos em di-ferentes linguagens, máquinas e sistemas operacionais, é utilizadauma notação para de�nir quais características do objeto (méto-dos, atributos) são expostas. CORBA oferece também um padrãopara esta notação: a IDL (Interface De�nition Language).

ClienteImplementacao

do Objeto

IDL IDL

ORB

Figura 6.1 Modelo de computação.

O ORB permite aos clientes invocarem operações em objetosdistribuídos sem se preocuparem com:

Tópicos Avançados 121

• as localizações dos objetos - Objetos CORBA podem sercolocados junto com os clientes ou distribuídos em servidoresremotos sem afetar suas implementações ou usos;

• os Sistemas Operacionais - CORBA roda em várias pla-taformas, tais como: Windows, UNIX, Linux e sistemas paratempo real e embutidos como QNX e VxWorks;

• as linguagens de programação - Linguagens com mape-amentos padronizados pela OMG incluem: C, C++, Java,Lisp, Ada95, COBOL e Smalltalk. Outras linguagens, ape-sar de não padronizadas pela OMG, também suportamCORBA, entre elas: Perl, Object Pascal(Delphi), Python;

• os protocolos de comunicação - Os protocolos de co-municação e interconectores que podem ser suportadas peloCORBA incluem: TCP/IP, IPX/SPX, FDDI, ATM, Ether-net, Fast Ethernet, backplanes de sistemas embutidos e me-mória compartilhada; e

• os hardware envolvidos - CORBA esconde das aplicaçõesos detalhes de diferentes hardwares como layout de armaze-namento e tamanhos e faixas de tipos de dados.

ClienteImplementacao

do Objeto

IDL IDL

ORB

ClienteImplementacao

do Objeto

IDL IDL

ORB

Rede

Figura 6.2 Modelo de distribuição mostrando dois objetos emmáquinas diferentes se comunicando através dos ORBs.

Alguns componentes básicos da arquitetura do CORBA sãonecessários para se entender sua forma de funcionamento e algunsdetalhes da sua programação. São eles:

122 Java na prática

• Cliente - Realiza suas tarefas obtendo referências para ob-jetos e invocando suas operações. Os objetos podem estarna mesma máquina que o cliente ou estarem localizados emoutras máquinas. A chamada de um método remoto, parao programador, não difere da chamada de um método local.

• Objeto - No CORBA um objeto é uma instância de umainterface da linguagem IDL. O objeto é identi�cado unica-mente por uma referência de objeto. Um ObjectId associaum objeto com o servant que o implementa.

• Servant - Este componente implementa as operações de�-nidas pela IDL. Em linguagens como Java, que suportamorientação a objetos (OO), os servants são implementadosusando um ou mais objetos. Em linguagens não OO, osservants são implementados usando funções e structs. Umcliente nunca interage diretamente com um servant, e simatravés de um objeto.

• ORB core - Quando um cliente invoca uma operação emum objeto, o ORB Core é responsável por entregar a requi-sição para o objeto correto e retornar a resposta, se exis-tir alguma, para o cliente. Para objetos executando remo-tamente, um ORB CORBA deve se comunicar com outrovia alguma versão do General Inter-ORB Protocol (GIOP).Normalmente um ORB Core é implementado como uma bi-blioteca que é linkada no cliente e no servidor.

• GIOP - O General InterORB Protocol especi�ca todos osaspectos de interoperabilidade sobre o transporte das men-sagens na rede. O GIOP de�ne um pequeno conjunto demensagens que um ORB transmite para outro quando umcliente invoca um método de um objeto servidor. O GIOPnão de�ne como essas mensagens devem ser transportadas.

• IIOP - A especi�cação da GIOP sendo transportada pelapilha TCP/IP recebe o nome de IIOP(Internet Inter-ORBProtocol)

Tópicos Avançados 123

• Interface do ORB - Um ORB é uma abstração que podeser implementada de várias formas (um ou vários processos,um conjunto de bibliotecas etc). Para desacoplar as aplica-ções dos detalhes de implementação, a especi�cação CORBAde�ne a interface para um ORB. Essa interface fornece ope-rações padrões para:

� inicializar e parar o ORB;� converter referências para objetos em strings e vice-

versa;� criar listas de argumentos para requisições dinâmicas.

• Stubs e Skeletons da IDL - IDL stubs e skeletons servemcomo uma "cola"entre o cliente e o servant, respectivamente,com o ORB.Quando queremos fazer uma chamada a um objeto, deve-mos criar a mensagem correta, isso é feito pelo stub. Oskeleton é responsável por traduzir essa mensagem para achamada correta dos objetos. O programador é responsá-vel por escrever o stub e o skeleton, mas várias ferramentassão disponibilizadas para auxiliá-lo nesta tarefa. O stub agecomo um objeto de representação local ou �proxy� que habi-lita o trabalho com o objeto remoto como se ele fosse local.O skeleton faz o contrário.

• compilador IDL - Um compilador IDL automaticamentetransforma uma de�nição em OMG IDL no código dos stubse skeletons para a linguagem de implementação. O SDKJava fornece um compilador IDL.

• Adaptador de objetos - Um adaptador de objetos é res-ponsável por:

� gerar referências para objetos;� ativar e desativar servants;� demultiplexar requisições para servants;� colaborar com os skeletons IDL para invocar operações

dos servants.

124 Java na prática

A �gura 6.3 mostra um adaptador direcionando requisiçõespara os servants.

ORB POA ServantsRequisicao

Figura 6.3 Funcionamento do Adaptador.

• IOR - Uma Interoperable Object Reference têm sua estru-tura mostrada na �gura 6.4.

Protocolo e detalhes de enderecoe porta

Nome do Tipo(ID no repositorio)

Chave do objeto(Adaptador e nome do objeto

Figura 6.4 Referência a objetos.

Em CORBA as referências a objetos são opacas, i.e., não épermitido ao código da aplicação olhar para dentro da re-ferência ou assumir qualquer coisa baseado em seu interior.Essa característica deixa transparente para a aplicação de-talhes como protocolo, linguagem e forma de transporte.O binding de uma requisição é ilustrada na �gura 6.5. Arequisição segue os seguintes passos:

Tópicos Avançados 125

1. Uma requisição é enviada para a máquina onde estáo objeto baseado no campo contendo os detalhes deendereço e porta;

2. O adaptador referenciado na chave do objeto direcionaa mensagem para o servant correto;

3. O retorno da requisição, se existir, é enviado para ocliente.

IDL:MeuObj:1.0 serv:4443 CT5, Math

Obj1

Obj10

Math

CT5

CK1

Obj5

Cliente

Referencia do Objeto

Math

Obj1Obj5

Obj10

Servidor em serv:4443

Adaptadores

Servants

(1)

(2)

(3)

Figura 6.5 Caminho da requisição.

Esta é a base da arquitetura CORBA. O ORB é um frameworkpara objetos, mas CORBA também de�ne um conjunto rico deobjetos e pseudo-objetos (façades) para vários problemas comunsdo trabalho com objetos distribuídos:

• CORBA Services que permite que o programador tenha, porexemplo, naming services, security services (autenticação deobjetos, encriptação da comunicação, etc) e license services(que controla o uso dos objetos pelos usuários)

126 Java na prática

• CORBA facilities são construídos sobre os CORBA servicese é um conjunto de objetos especí�cos para determinadasáreas da indústria: médica, legal, etc.

Object Request Broker

Cliente ClienteAplicacao

Objetos da

CORBA Services

Figura 6.6 Serviço CORBA.

6.2 Exemplo CORBA em JavaCORBA tem, como discutido na seção anterior, uma série de qua-lidades, mas um dos seus defeitos é a grande quantidade de códigogerado para utilizá-la. Parte desse código é gerado automatica-mente pelos compiladores de IDL. Para evitar a inclusão de gran-des listagens nesse livro, trabalharemos um exemplo mais simples,mas nem por isso menos interessante.

Faremos, agora, um objeto CORBA que realiza operações ma-temática. No exemplo teremos somente uma operação, o fatorial.

6.2.1 Escrevendo a IDLA interface de nosso objeto deve ser de�nida através daOMG/IDL. Nosso objeto fará parte de uma biblioteca de obje-tos de nome MinhaLib.

module MinhaLib{interface Matematica{

long f a t o r i a l ( in long n ) ;

Tópicos Avançados 127

} ;} ;

Exemplo 6.1 Arquivo Fatorial.idl

O arquivo Fatorial.idl de�ne nosso objeto, contendo so-mente um método fatorial que retorna long e recebe como pa-râmetro de entrada um outro valor long.

6.2.2 Compilando a IDLPara compilar a IDL e gerar os stubs e skeletons utilizamos oprograma idlj que acompanha o JDK. Ao executar o comando:

idlj -fall Fatorial.idl

um diretório de nome MinhaLib é gerado e dentro desse diretórioestarão o stub, o skeleton e a classe base para a implementação denosso objeto.

6.2.3 Implementando nosso ObjetoDentro do diretório do módulo estará a classe base para nossaimplementação, essa classe é abstrata, no nosso caso seu nome é_MatematicaImplBase.

Na listagem 6.2 temos a implementação do método fatorialdeclarado na IDL.

package MinhaLib ;

public class MatematicaImpl extends _MatematicaImplBase{public int f a t o r i a l ( int n){

i f (n > 0) return n ∗ f a t o r i a l (n − 1) ;else return 1 ;

}}

Exemplo 6.2 Arquivo MatematicaImpl.java

128 Java na prática

6.2.4 Escrevendo o servidorAgora, com nosso objeto implementado, devemos escrever o có-digo que o disponibilize para o uso de seus clientes. A listagem 6.3mostra esse código. A seguinte sequência de passos é seguida:

1. o ORB é inicializado;

2. uma instância do objeto MatematicaImpl é criada;

3. o método connect disponibiliza o objeto no ORB;

4. a referência para o objeto é convertida em uma string egravada no arquivo Matematica.ior; e

5. a execução do thread que executa o método main é suspen-dida.

Nos nossos exemplos, para simpli�car o código e facilitar osteste, não utilizamos nenhum servidor de nomes. Colocamos nossareferência ao objeto CORBA gravada em um arquivo que o clientedeve ler.

import org . omg .CORBA.ORB;

import java . u t i l . P rope r t i e s ;import java . i o . ∗ ;

public class Fato r i a l S e r v e r {public stat ic void main ( St r ing args [ ] ) {

try {Prope r t i e s props = System . g e tP rope r t i e s ( ) ;

ORB orb = ORB. i n i t ( args , props ) ;

MinhaLib . MatematicaImpl se rvant =new MinhaLib . MatematicaImpl ( ) ;

orb . connect ( se rvant ) ;

S t r ing i o r = orb . object_to_str ing ( se rvant ) ;F i l eWr i t e r f i l e =new java . i o . F i l eWr i t e r ( "Matematica . i o r " ) ;

Pr intWriter p f i l e = new java . i o . Pr intWriter ( f i l e ) ;

Tópicos Avançados 129

p f i l e . p r i n t l n ( i o r ) ;p f i l e . f l u s h ( ) ;p f i l e . c l o s e ( ) ;

java . lang . Object sync = new java . lang . Object ( ) ;synchronized ( sync ) {

sync . wait ( ) ;}

} catch ( Exception ex ) {ex . pr intStackTrace ( ) ;

}}

}

Exemplo 6.3 Arquivo FatorialServer.java

6.2.5 Escrevendo o clienteO cliente deve inicializar o ORB, obter a refêrencia ao objeto,converté-la em um objeto Java e usar seu método fatorial.

import org . omg .CORBA.ORB;

import java . u t i l . P rope r t i e s ;import java . i o . F i leReader ;import java . i o . BufferedReader ;

public class Fa t o r i a lC l i e n t {public stat ic void main ( St r ing args [ ] ) {

try {Prope r t i e s props = System . g e tP rope r t i e s ( ) ;

ORB orb = ORB. i n i t ( args , props ) ;

Fi l eReader f i l e =new java . i o . F i leReader ( "Matematica . i o r " ) ;

BufferedReader input =new java . i o . BufferedReader ( f i l e ) ;

S t r ing i o r = input . readLine ( ) ;input . c l o s e ( ) ;

org . omg .CORBA. Object obj =

130 Java na prática

orb . s t r ing_to_object ( i o r ) ;i f ( obj == null )

throw new RuntimeException ( ) ;MinhaLib . Matematica srv =

MinhaLib . MatematicaHelper . narrow ( obj ) ;i f ( s rv == null )

throw new RuntimeException ( ) ;

long r e s = srv . f a t o r i a l ( 1 0 ) ;System . out . p r i n t l n ( "O f a t o r i a l de 1 0 e "

+ re s ) ;} catch ( Exception ex ) {

ex . pr intStackTrace ( ) ;}

}}

Exemplo 6.4 Arquivo FatorialClient.java

6.2.6 Rodando o exemploPara rodar o exemplo devemos abrir dois consoles e no primeirodigitar:

java FatorialServer

no outro console digite:

java FatorialClient

a saida será:

O fatorial de 10 e 3628800

6.3 Exemplo CORBA (Java + C)Nesta seção codi�caremos o mesmo exemplo da seção 6.2 utili-zando um mapeamento de IDL para a linguagem C e o ORBfree-source ORBit. Detalhes sobre o mapeamento da IDL paraC não faz parte do escopo deste livro, podendo ser encontradosdiretamente no site da OMG(www.omg.org).

Tópicos Avançados 131

O ORBit é um ORB compatível com a versão 2.4 da especi-�cação. Os principais mapeamentos do ORBit são para as lin-guagens C e Perl, mas mapeamentos para outras também estãodisponíveis C++, Lisp, Pascal, Python, Ruby e TCL). O ORBittem como diretriz de desenvolvimento a grande preocupação coma performance. Por isso o core ORB é escrito em C e foi esco-lhido como base para a comunicação entre objetos no ambientegrá�co GNOME. O ORBit roda sobre Linux, UNIX (BSD, So-laris, HP-UX) e Windows. Outra importante característica é odesenvolvimento realizado sobre a licensa GPL/LGPL.

O ORBit não utiliza por default a comunicação entre objetosatravés da rede, colocando todas as trocas de mensagens entre ob-jetos sendo realizada por mecanismos de IPC do sistema operacio-nal. Essa abordagem é justi�cada pela busca de alta performance.Como nosso ORB Java não conhece esse interconector IPC, que épróprio do ORBit, devemos habilitar o uso do IIOP sobre socketsde rede. Com esse propósito a listagem 6.5 deve ser colocada nodiretório home do usuário que colocará os objetos para rodar.

ORBIIOPUSock=1ORBIIOPIPv4=1ORBIIOPIPv6=0

Exemplo 6.5 Arquivo .orbitrc

No exemplo utilizaremos a mesma IDL utilizada no exemploJava.

6.3.1 Compilando a IDLO compilador de IDL do ORBit tem o nome orbit-idl e deve serusado da primeira vez com o parâmetro --skeleton-impl paragerar uma implementação vazia do objeto CORBA.

orbit-idl �skeleton-impl Fatorial.idl

Esse comando também criará os arquivos stubs e skeletons.Num segundo passo devemos alterar o arquivo de implementaçãogerado para dar a funcionalidade desejada para nosso objeto.

132 Java na prática

6.3.2 Implementando nosso ObjetoNo nosso exemplo o arquivo de implementação vazio ge-rado terá o nome Fatorial-skelimpl.c. Esse arquivoé extenso, e a parte que interessa é o corpo da funçãoimpl_MinhaLib_Matematica_fatorial. Para implementarmos afuncionalidade da fatorial declaramos a seguinte função acima deimpl_MinhaLib_Matematica_fatorial:

stat ic long f a t o r i a l ( long n){i f (n > 0) return n ∗ f a t o r i a l (n − 1) ;else return 1 ;

}

Exemplo 6.6 Função que realiza a operação de fatorial.

O Corpo de impl_MinhaLib_Matematica_fatorial é modi�-cado para:

stat ic CORBA_longimpl_MinhaLib_Matematica_fatorial (

impl_POA_MinhaLib_Matematica ∗ servant ,CORBA_long n , CORBA_Environment ∗ ev )

{CORBA_long r e t v a l ;r e t v a l = f a t o r i a l (n ) ;return r e t v a l ;

}

Exemplo 6.7 Função impl_MinhaLib_Matematica_fatorialmodi�cada.

Essas duas alterações implementam o método do nosso objeto.

6.3.3 Escrevendo o ServidorSemelhante a implementação do servidor em Java, escolhemosmostrar a referência do objeto CORBA (ior) criado na saída pa-drão.

Tópicos Avançados 133

Os passos para criação e disponibilização do objeto em C sãoos mesmos realizados em Java.

#include " Fato r i a l−ske l imp l . c"#include <s td i o . h>

intmain ( int argc , char ∗ argv [ ] ){

CORBA_ORB orb ;CORBA_Environment∗ ev ;PortableServer_ObjectId ∗ oid ;MinhaLib_Matematica Mat ;PortableServer_POA root_poa ;PortableServer_POAManager pm;CORBA_char∗ ob j r e f ;

ev = g_new0(CORBA_Environment , 1 ) ;

CORBA_exception_init ( ev ) ;

orb = CORBA_ORB_init(&argc , argv ," orb i t−l o c a l−orb" , ev ) ;

/∗ Manipula uma p o s s i v e l excecao ∗/

root_poa = ( PortableServer_POA )CORBA_ORB_resolve_initial_references ( orb ,

"RootPOA" , ev ) ;/∗ Manipula uma p o s s i v e l excecao ∗/

Mat = impl_MinhaLib_Matematica__create ( root_poa , ev ) ;/∗ Manipula uma p o s s i v e l excecao ∗/

ob j r e f = CORBA_ORB_object_to_string( orb , Mat , ev ) ;/∗ Manipula uma p o s s i v e l excecao ∗/

g_print ( "%s\n" , o b j r e f ) ;

pm = PortableServer_POA__get_the_POAManager (root_poa , ev ) ;

/∗ Manipula uma p o s s i v e l excecao ∗/

PortableServer_POAManager_activate (pm, ev ) ;/∗ Manipula uma p o s s i v e l excecao ∗/

134 Java na prática

CORBA_ORB_run( orb , ev ) ;return 0 ;

}

Exemplo 6.8 Código do servidor Fatorial-server.c.

6.3.4 Escrevendo o ClienteNosso cliente de teste obtém o ior do servidor através da linha decomando e chama o método fatorial para calcular o fatorial de10 e mostrá-lo na saída padrão.

#include <orb/ o rb i t . h>#include " Fa t o r i a l . h"#include <s td i o . h>

intmain ( int argc , char ∗ argv [ ] ) {

CORBA_Environment ev ;CORBA_ORB orb ;CORBA_Object s e r v e r ;gchar ∗ dummy_argv [ 2 ] ;g i n t dummy_argc ;int i ;

dummy_argc = 1 ;dummy_argv [ 0 ] = argv [ 0 ] ;dummy_argv [ 1 ] = 0 ;

CORBA_exception_init(&ev ) ;orb = CORBA_ORB_init(&dummy_argc ,

dummy_argv , " orb i t−l o c a l−orb" , & ev ) ;s e r v e r = CORBA_ORB_string_to_object (

orb , argv [ 1 ] , & ev ) ;p r i n t f ( "O f a t o r i a l de 1 0 e i g ua l a %d\n" ,MinhaLib_Matematica_fatorial ( s e rver , 10 , & ev ) ) ;

CORBA_Object_release ( se rver ,&ev ) ;e x i t ( 0 ) ;

}

Exemplo 6.9 Código do cliente Fatorial-client.c.

Tópicos Avançados 135

6.3.5 Compilando e Rodando o ExemploPara compilar todos os exemplo o arquivo Makefile abaixo podeser utilizado:

LIBS= ` orb i t−c on f i g −− l i b s s e r v e r c l i e n t ` −ggdbCFLAGS= ` orb i t−c on f i g −− c f l a g s s e r v e r c l i e n t ` \

` g l i b−c on f i g −− c f l a g s g l ib ` −ggdb

LISTA_SERVER= Fator i a l−common . o \Fato r i a l−s k e l s . o \Fato r i a l−s e r v e r . o

LISTA_CLIENTE= Fator i a l−common . o \Fato r i a l−s tubs . o\Fato r i a l−c l i e n t . o

a l l : ORBJava ORBC

ORBJava :i d l j − f a l l Fa t o r i a l . i d ljavac MinhaLib /∗ . javajavac ∗ . java

ORBC: c l i e n t s e r v e r

c l i e n t : cod igogcc −o c l i e n t $ (LIBS ) $ (LISTA_CLIENTE)

s e r v e r : cod igogcc −o s e r v e r $ (LIBS ) $ (LISTA_SERVER)

codigo : i d lgcc −c $ (CFLAGS) ∗ . c

i d l : Fa t o r i a l . i d lo rb i t−i d l Fa t o r i a l . i d l

c l ean :cd MinhaLib ; rm − f ∗ . c l a s s ∗~rm − f ∗ . o ∗~ s e r v e r c l i e n t ∗ . c l a s s ∗ . i o r

Exemplo 6.10 arquivo Make�leDevemos lembrar, novamente, que a primeira execução

do orbit-idl deve ser feita manualmente com o parâmetro

136 Java na prática

--skeleton-impl, como descrito na seção 6.3.1.Para executar o servidor escrito em C devemos utilizar o co-

mando:

./server > Matematica.ior

e para rodar o cliente, use o comando:

./client `cat Matematica.ior`

A interoperação entre objetos C e objetos Java pode ser obtidasubstituindo o cliente e/ou o servidor pelo programas Java.

Capítulo 7

Servlets e JSP

Servlets e JSP são duas tecnologias criadas pela Sun para desen-volvimento de aplicações na Web a partir de componentes Javaque executem no lado servidor. Essas duas tecnologias fazem parteda plataforma J2EE (Java 2 Enterprise Edition) que fornece umconjunto de tecnologias para o desenvolvimento de soluções esca-láveis e robustas para a Web. Neste livro abordaremos apenas astecnologias Servlets e JSP, sendo o su�ciente para o desenvolvi-mento de sites dinâmicos de razoável complexidade. Se a aplicaçãoexigir uma grande robustez e escalabilidade o leitor deve conside-rar o uso em conjunto de outras tecnologias da plataforma J2EE.

7.1 ServletsServlets são classes Java que são instanciadas e executadas emassociação com servidores Web, atendendo requisições realizadaspor meio do protocolo HTTP. Ao serem acionados, os objetos Ser-vlets podem enviar a resposta na forma de uma página HTML ouqualquer outro conteúdo MIME. Na verdade os Servlets podemtrabalhar com vários tipos de servidores e não só servidores Web,uma vez que a API dos Servlets não assume nada a respeito doambiente do servidor, sendo independente de protocolos e plata-formas. Em outras palavras Servlets é uma API para construção

137

138 Java na prática

de componentes do lado servidor com o objetivo de fornecer umpadrão para comunicação entre clientes e servidores. Os Servletssão tipicamente usados no desenvolvimento de sites dinâmicos.Sites dinâmicos são sites onde algumas de suas páginas são cons-truídas no momento do atendimento de uma requisição HTTP.Assim é possível criar páginas com conteúdo variável, de acordocom o usuário, tempo, ou informações armazenadas em um bancode dados.

Figura 7.1 Relacionamento entre Servlets, container e servidorWeb.

Servlets não possuem interface grá�ca e suas instâncias sãoexecutadas dentro de um ambiente Java denominado de Contai-ner. O container gerencia as instâncias dos Servlets e provê osserviços de rede necessários para as requisições e respostas. Ocontainer atua em associação com servidores Web recebendo asrequisições reencaminhada por eles. Tipicamente existe apenasuma instância de cada Servlet, no entanto, o container pode criarvários threads de modo a permitir que uma única instância Ser-vlet atenda mais de uma requisição simultaneamente. A �gura 7.1fornece uma visão do relacionamento destes componentes.

Servlets provêem uma solução interessante para o relaciona-mento cliente/servidor na Internet, tornando-se uma alternativapara a implantação de sistemas para a Web. Antes de entrarmosem detalhes na construção de Servlets, compararemos esta soluçãocom outras duas soluções possíveis para implantação de aplicaçõesna Internet.

Tópicos Avançados 139

7.1.1 Applets X ServletsApesar de ser uma solução robusta, existem problemas no usode Applets para validação de dados e envio para o servidor. Oprogramador precisa contar com o fato do usuário possuir umnavegador com suporte a Java e na versão apropriada. Você nãopode contar com isso na Internet, principalmente se você desejaestender a um grande número de usuário o acesso às suas páginas.Em se tratando de Servlets, no lado do cliente pode existir apenaspáginas HTML, evitando restrições de acesso às páginas. Emresumo, o uso de Applets não é recomendado para ambientes comdiferentes navegadores ou quando a semântica da aplicação possaser expressa por componentes HTML.

7.1.2 CGI X ServletsComo já comentado, scripts CGI (Common Gateway Interface),acionam programas no servidor. O uso de CGI sobrecarrega oservidor uma vez que cada requisição de serviço acarreta a execu-ção de um programa executável (que pode ser escrito em qualquerlinguagem que suporte o padrão CGI) no servidor, além disso,todo o processamento é realizado pelo CGI no servidor. Se hou-ver algum erro na entrada de dados o CGI tem que produzir umapágina HTML explicando o problema. Já os Servlets são carrega-dos apenas uma vez e como são executados de forma multithreadpodem atender mais de uma mesma solicitação simultaneamente.Versões posteriores de CGI contornam este tipo de problema, maspermanecem outros como a falta de portabilidade e a insegurançana execução de código escrito em uma linguagem como C/C++.

7.2 A API ServletA API Servlet é composta por um conjunto de interfaces e Classes.O componente mais básico da API é a interface Servlet. Elade�ne o comportamento básico de um Servlet. A �gura 7.2 mostraa interface Servlet.

140 Java na prática

public interface Se r v l e t {public void i n i t ( Se rv l e tCon f i g c on f i g )

throws Serv l e tExcept ion ;public Se rv l e tCon f i g g e tSe rv l e tCon f i g ( ) ;public void s e r v i c e ( Serv l e tReques t req ,

Serv le tResponse r e s )throws Serv le tExcept ion , IOException ;

public St r ing g e t S e r v l e t I n f o ( ) ;public void dest roy ( ) ;

}

Figura 7.2 Interface Servlet.

O método service() é responsável pelo tratamento de todasas requisições dos clientes. Já os métodos init() e destroy()são chamados quando o Servlet é carregado e descarregado docontainer, respectivamente. O método getServletConfig() re-torna um objeto ServletConfig que contém os parâmetros deinicialização do Servlet. O método getServletInfo() retornaum String contendo informações sobre o Servlet, tais como ver-são e autor.

Tendo como base a interface Servlet o restante da API Servletse organiza hierarquicamente como mostra a �gura 7.3.

Figura 7.3 Hierarquia de classes da API Servlet.

A classe GenericServlet implementa um servidor genérico egeralmente não é usada. A classe HttpServlet é a mais utilizada

Tópicos Avançados 141

e foi especialmente projetada para lidar com o protocolo HTTP.A �gura 7.4 mostra a de�nição da classe abstrata HttpServlet.

HttpServ l e t

public abstract class HttpServ l e textends Gener i cSe rv l e timplements java . i o . S e r i a l i z a b l e

Figura 7.4 De�nição da classe HttpServlet.

Note que por ser a HttpServlet uma classe abstrata, parase criar um Servlet que atenda requisições HTTP o programadordeve criar uma classe derivada da HttpServlet e sobrescrever pelomenos um dos métodos abaixo:

método DescriçãodoGet Trata as requisições HTTP GET.doPost Trata as requisições HTTP POST.doPut Trata as requisições HTTP PUT.doDelete Trata as requisições HTTP DELETE.

Tabela 7.1 Métodos da classe HttpServlet que devem sersobrescritos para tratar requisições HTTP.

Todos esses métodos são invocados pelo servidor por meio dométodo service(). O método doGet() trata as requisições GET.Este tipo de requisição pode ser colocada em um bookmark, possi-bilitando assim, o seu envio repetidas vezes. O método doPost()trata as requisições POST que permitem que o cliente envie dadosde tamanho ilimitado para o servidor Web uma única vez, sendoútil para enviar informações tais como o número do cartão de cré-dito. O método doPut() trata as requisições PUT. Este tipo derequisição permite que o cliente envie um arquivo para o servidorà semelhança de como é feito via FTP. O método doDelete()trata as requisições DELETE, permitindo que o cliente remova umdocumento ou uma página do servidor. O método service(), querecebe todas as requisições, em geral não é sobrescrito, sendo suatarefa direcionar a requisição para o método adequado.

142 Java na prática

7.2.1 Exemplo de ServletPara entendermos o que é um Servlet nada melhor que um exem-plo simples. O exemplo 7.1 gera uma página HTML em respostaa uma requisição GET. A página HTML gerada contém simples-mente a frase Ola mundo!!!. Este é um Servlet bem simples queilustra as funcionalidades básicas da classe.

import javax . s e r v l e t . ∗ ;import javax . s e r v l e t . http . ∗ ;

public class Ola extends HttpServ le t{

public St r ing g e t S e r v l e t I n f o ( ){ return "Ola versão 0 . 1 " ; }

public void doGet ( HttpServ letRequest req ,HttpServletResponse r e s )

throws IOException , Se rv l e tExcept ion{

r e s . setContentType ( " text /html" ) ;java . i o . Pr intWriter out = r e s . getWriter ( ) ;out . p r i n t l n ( "<html>" ) ;out . p r i n t l n ( "<head>" ) ;out . p r i n t l n ( "<t i t l e >Serv l e t </ t i t l e >" ) ;out . p r i n t l n ( "</head>" ) ;out . p r i n t l n ( "<body>Ola mundo ! ! ! " ) ;out . p r i n t l n ( "</body>" ) ;out . p r i n t l n ( "</html>" ) ;out . c l o s e ( ) ;

}}

Exemplo 7.1 Servlet Ola.

O método doGet() recebe dois objetos: um da classeHttpServletRequest e outro da classe HttpServletResponse. OHttpServletRequest é responsável pela comunicação do clientepara o servidor e o HttpServletResponse é responsável pela co-municação do servidor para o cliente. Sendo o exemplo 7.1 apenasum exemplo simples ele ignora o que foi enviado pelo cliente, tra-tando apenas de enviar uma página HTML como resposta. Para

Tópicos Avançados 143

isso é utilizado o objeto da classe HttpServletResponse. Pri-meiramente é usado o método setContentType() para de�nir otipo do conteúdo a ser enviado ao cliente. Como argumento deveser passado um dos tipos de�nidos no protocolo MIME (Multipur-pose Internet Mail Extensions). Tipos MIME de�nem o tipo deconteúdo que está sendo transmitido e são especi�cados na formatipo-principal/subtipo. Exemplos de tipos MIME são text/html,application/pdf e image/gif. O método setContentType()deve ser usado apenas uma vez e antes de se obter um objetodo tipo PrintWriter ou ServletOutputStream para a resposta.Após isso é usado o método getWriter() para se obter um objetodo tipo PrintWriter que é usado para escrever a resposta. Nestecaso os dados da resposta são baseados em caracteres. Se o pro-gramador desejar enviar a resposta em bytes deve usar o métodogetOutputStream() para obter um objeto OutputStream. A par-tir de então o programa passa a usar o objeto PrintWriter paraenviar a página HTML.

7.3 Compilando o ServletA API Servlet não está incorporada ao SDK, portanto, para com-pilar um Servlet é preciso adicionar um pacote ao classpath. Exis-tem várias formas de se fazer isso. A Sun fornece a especi�ca-ção da API e diversos produtores de software executam a im-plementação. Atualmente, a especi�cação da API Servlet estána versão 2.3. Uma das implementações da API que pode serbaixada gratuitamente pela Internet é a fornecida pelo projetoJakarta (http://jakarta.apache.org) denominada de Tomcat.A implementação da API Servlet feita pelo projeto Jakarta é aimplementação de referência indicada pela Sun. Ou seja, é a im-plementação que os outros fabricantes devem seguir para garantira conformidade com a especi�cação da API.

7.3.1 Instalando o TomcatAssim como para se executar um Applet era preciso de um nave-gador Web com Java habilitado, no caso de Servlets é preciso de

144 Java na prática

servidor Web que execute Java ou que passe as requisições feitasa Servlets para programas que executem os Servlets. O Tomcaté tanto a implementação da API Servlet como a implementaçãode um container, que pode trabalhar em associação com um ser-vidor Web como o Apache ou o IIS, ou pode também trabalharisoladamente, desempenhando também o papel de um servidorWeb. Nos exemplos aqui mostrados usaremos o Tomcat isolada-mente. Em um ambiente de produção esta con�guração não é amais adequada, uma vez que os servidores Web possuem um me-lhor desempenho no despacho de páginas estáticas. As instruçõespara con�gurar o Tomcat para trabalhar em conjunto com umservidor Web podem ser encontradas junto às instruções gerais doprograma. As �guras 7.5 e 7.6 ilustram essas duas situações.

Figura 7.5 Servidor Web habilitado para Servlet.

Figura 7.6 Servidor Web reencaminhando as requisições para oServlet container.

A versão estável do Tomcat é a 4.1 e, após baixá-la do site doprojeto Jakarta, o usuário deve executar as instruções de instala-ção. Por exemplo, no ambiente Windows o programa de instalaçãogera a seguinte árvore de diretórios:

Tópicos Avançados 145

C:\...\Tomcat 4.1||______bin|______common|______conf|______logs|______server|______shared|______temp|______src|______webapps|______work

No diretório bin encontram-se os programas para execu-ção e interrupção do container Tomcat. No diretório commonencontram-se as bibliotecas e classes de uso comum. No dire-tório conf encontram-se os arquivos de con�guração. No diretó-rio logs são registradas as mensagens geradas durante a execu-ção do sistema. No diretório server encontram-se as classes doTomcat e aplicações para administração do Tomcat. No diretórioshared encontram-se as classes compartilhadas entre as aplica-ções do Tomcat. O diretório temp é usado pela máquina virtualpara armazenamento temporário. No diretório src encontram-seos arquivos fontes do container e da implementação da API decon�guração. Finalmente, no diretório webapps encontram-se aspáginas e códigos das aplicações dos usuários. O diretório work éusado pelo Tomcat para armazenar as classes resultantes da com-pilação dos Servlets.

Antes de executar o Tomcat é necessário de�nir a variável deambiente CATALINA_HOME com o valor do diretório onde foi insta-lado o Tomcat. Por exemplo, supondo que no Linux o Tomcat foiinstalado no diretório /usr/jakarta-tomcat-4.1.24-LE-jdk14então os seguintes comandos devem ser colocados no arquivo deinicialização apropriado:

CATALINA_HOME=/usr/jakarta-tomcat-4.1.24-LE-jdk14export CATALINA_HOME

146 Java na prática

Após de�nir a variável de ambiente CATALINA_HOME pode-seexecutar o Tomcat por meio do seguinte comando1:

%CATALINA_HOME%\bin\startup (Windows)

$CATALINA_HOME/bin/startup.sh (Unix)

Para interromper a execução servidor basta executar o co-mando:

%CATALINA_HOME%\bin\shutdown (Windows)

$CATALINA_HOME/bin/shutdown.sh (Unix)

No ambiente MS-Windows o instalador do Tomcat adicionaatalhos para iniciar e �nalizar o Tomcat a partir do menu deprogramas.

Ao entrar em execução o servidor lê as con�gurações cons-tantes no arquivo server.xml e, por default, se conecta à porta8080. Para veri�car se o programa está funcionando corretamenteexecute um navegador como o Netscape ou o Internet Explorer edigite a seguinte URL:

http://127.0.0.1:8080/

ou

http://localhost:8080/

A �gura 7.7 mostra a tela principal do Tomcat.

1Caso ao iniciar o servidor no MS-Windos apareça a mensagem �semespaço de ambiente� clique com o botão direito do mouse no arquivo .bat eedite as propriedades de�nindo o ambiente inicial com 4096 bytes de memória.Feche o arquivo e execute novamente.

Tópicos Avançados 147

Figura 7.7 Tela inicial do Tomcat.

O número da porta default para recebimento das requisi-ções HTTP pode ser alterado por meio da edição do arquivoserver.xml do diretório conf como mostrado abaixo:

<!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8081 --><Connector className="org.apache.coyote.tomcat4.CoyoteConnector"

port="8080" minProcessors="5" maxProcessors="75"enableLookups="true" redirectPort="8443"acceptCount="10" debug="0" connectionTimeout="20000"useURIValidationHack="false" />

Figura 7.8 Trecho do arquivo server.xml relacionado com aporta de conexão.

No entanto, caso o Tomcat esteja operando em conjunto comum servidor HTML, o ideal é que ele não responda requisiçõesdiretamente.

148 Java na prática

7.4 Preparando para executar o Servlet7.4.1 Compilando o ServletAntes de executar o Servlet e preciso compilá-lo. Para compilá-loé preciso que as classes que implementam a API Servlet estejamno classpath. Isto é feito da seguinte forma no ambiente MS-Windows:set CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\common\lib\servlet.jar

e no ambiente Unix seriaexport CLASSPATH=$CLASSPATH:${TOMCAT_HOME}/common/lib/servlet.jar

Alternativamente, é possível indicar o classpath na própria li-nha de execução do compilador Java. Por exemplo, no ambienteMS-Windows �caria na seguinte forma:javac -cp %CLASSPATH%;%TOMCAT_HOME%\common\lib\servlet.jar Ola.java

7.4.2 Criando uma aplicação no TomcatAgora é preciso de�nir onde deve ser colocado o arquivo compi-lado. Para isso é preciso criar uma aplicação no Tomcat ou usaruma das aplicações já existentes. Vamos aprender como criar umaaplicação no Tomcat. Primeiramente é preciso criar a seguinte es-trutura de diretórios abaixo do diretório webapps do Tomcat:

webapps|_____ Nome da aplicação

|_____ WEB-INF|_____classes

O diretório de uma aplicação2 é denominado de contexto daaplicação.

Copie o arquivo compilado Ola.class para o subdiretório/webapps/nome aplicação/Web-inf/classes do Tomcat. É pre-ciso também editar um arquivo web.xml que deve ser colocado nodiretório WEB-INF da aplicação com o seguinte conteúdo:

2Na verdade é possível de�nir outro diretório para colocar as aplicações doTomcat. Para indicar outro diretório é preciso editar o arquivo server.xml eindicar o diretório por meio do tag Context path.

Tópicos Avançados 149

<?xml version="1.0" encoding="ISO-8859-1"?><!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>Teste</display-name><description> Primeiro teste </description>

<servlet><servlet-name>ola</servlet-name><servlet-class>Ola</servlet-class>

</servlet>

<servlet-mapping><servlet-name>ola</servlet-name><url-pattern>/ola</url-pattern>

</servlet-mapping></web-app>

Figura 7.9 Exemplo do arquivo web.xml.

O tag servlet é usado para descrever cada Servlet da aplica-ção. O subtag servlet-name é usado para associar um nome aoServlet e o subtag servlet-class associa o nome à classe que im-plementa o Servlet. O tag servlet-mapping é usado de�nir quaisURLs serão mapeadas para Servlets. O subtag servlet-nameé usado para de�nir que Servlet será mapeado e o subtagurl-pattern de�ne o padrão da URL. No caso da �gura 7.9 umaURL na forma http://<servidor>/<contexto>/ola será mape-ado para o Servlet. A elaboração do arquivo web.xml pode serbastante complexa. Os detalhes fornecidos aqui são su�cientesapenas para executar os exemplos. Maiores detalhes devem serobtidos na própria documentação que acompanha o Tomcat.

7.5 Executando o Servlet7.5.1 Invocando diretamente pelo NavegadorPodemos executar um Servlet diretamente digitando a URL doServlet no navegador. A URL em geral possui o seguinte formato:

http://<servidor>/<contexto>/<mapeamento>

150 Java na prática

Por exemplo, suponha que o nome da aplicação criada no Tom-cat seja teste e que o Tomcat esteja rodando no computador lo-cal. Então a URL para a invocação do Servlet do exemplo 7.1teria a seguinte forma:

http://máquina:porta/contexto da aplicação/nome servlet

Por exemplo,

http://localhost:8080/teste/ola

7.5.2 Invocando em uma página HTMLNo caso de uma página HTML basta colocar a URL na forma delink. Por exemplo,

<a href="http://localhost:8080/teste/ola>Servlet Ola</a>

Neste caso o Servlet Ola será solicitado quando o link associadoao texto �Servlet Ola� for acionado. Se o arquivo HTML pertencerao mesmo contexto do Servlet, então basta de�nir um link comomostrado a seguir:

<a href="ola">Servlet Ola</a>

Por exemplo, o Servlet do exemplo 7.1 pode ser acessado pormeio do seguinte arquivo HTML colocado no subdiretório teste:

<HTML><BODY><a href="ola">Servlet Ola</a></BODY></HTML>

Figura 7.10 Arquivo html para acessar o Servlet Ola.

7.5.3 Diferenças entre as requisições GET ePOST

Os dois métodos mais comuns, de�nidos pelo protocolo HTTP,de se enviar uma requisição a um servidor Web são os métodos

Tópicos Avançados 151

POST e GET. Apesar de aparentemente cumprirem a mesma fun-ção, existem diferenças importantes entre estes dois métodos. Ométodo GET tem por objetivo enviar uma requisição por um re-curso. As informações necessárias para a obtenção do recurso(como informações digitadas em formulários HTML) são adicio-nadas à URL e, por consequência, não são permitidos caracteresinválidos na formação de URLs, como espaços em branco e carac-teres especiais. Já na requisição POST os dados são enviados nocorpo da mensagem.

O método GET possui a vantagem de ser idempotente, ou seja,os servidores Web podem assumir que a requisição pode ser repe-tida, sendo possível adicionar a URL ao bookmark. Isto é muitoútil quando o usuário deseja manter a URL resultante de umapesquisa. Como desvantagem as informações passadas via GETnão podem ser muito longas, um vez que o número de caracterespermitidos é por volta de 2K.

Já as requisições POST, a princípio, podem ter tamanho ili-mitado. No entanto, elas não são idempotente, o que as tornamideais para formulários onde os usuários precisam digitar infor-mações con�denciais, como número de cartão de crédito. Destaforma o usuário é obrigado a digitar a informação toda vez quefor enviar a requisição, não sendo possível registrar a requisiçãoem um bookmark.

7.6 ConcorrênciaUma vez carregado o Servlet não é mais descarregado, a não serque o servidor Web tenha sua execução interrompida. De modogeral, cada requisição que deve ser direcionada a uma determi-nada instância de Servlet é tratada por um thread sobre a ins-tância de Servlet. Isto signi�ca que se existirem duas requisiçõessimultâneas que devem ser direcionadas para um mesmo objeto ocontainer criará dois threads sobre o mesmo objeto Servlet paratratar as requisições. A �gura 7.11 ilustra esta situação.

152 Java na prática

Figura 7.11 Relacionamento entre as instâncias dos Servlets eos threads.

Em conseqüência disto temos os benefícios de uma sobrecargamenor para servidor, uma vez que a criação de threads é menosonerosa do que a criação de processos, e uma aparente melhorano tempo de resposta.

Por outro lado, o fato dos Servlets operarem em modo multith-read aumenta a complexidade das aplicações e cuidados especiais,como visto no capítulo sobre concorrência, devem tomados paraevitar comportamentos erráticos. Por exemplo, suponha um Ser-vlet que receba um conjunto de números inteiros e retorne umapágina contendo a soma dos números. O exemplo 7.2 mostra ocódigo do Servlet. O leitor pode imaginar um código muito maise�ciente para computar a soma de números, mas o objetivo do có-digo do exemplo é ilustrar o problema da concorrência em Servlets.O exemplo contém também um trecho de código para recebimentode valores de formulários, o que será discutido mais adiante.

import java . u t i l . ∗ ;import javax . s e r v l e t . ∗ ;import javax . s e r v l e t . http . ∗ ;

public class Soma extends HttpServ le t {

Vector v = new Vector ( 5 ) ;protected void doPost ( HttpServ letRequest req ,

HttpServletResponse r e s )throws Serv le tExcept ion , java . i o . IOException {

Tópicos Avançados 153

v . c l e a r ( ) ;Enumeration e = req . getParameterNames ( ) ;

while ( e . hasMoreElements ( ) ) {S t r ing name = ( St r ing ) e . nextElement ( ) ;S t r ing value = req . getParameter (name ) ;i f ( va lue != null ) v . add ( value ) ;

}

r e s . setContentType ( " text /html" ) ;java . i o . Pr intWriter out = r e s . getWriter ( ) ;out . p r i n t l n ( "<html>" ) ;out . p r i n t l n (

"<head><t i t l e >Serv l e t </ t i t l e ></head>" ) ;out . p r i n t l n ( "<body>" ) ;out . p r i n t l n ( "<h1> A soma e ' " ) ;int soma =0;for ( int i =0 ; i < v . s i z e ( ) ; i ++) {

soma += Int eg e r . pa r s e In t ( ( S t r ing )v . get ( i ) ) ;}out . p r i n t l n ( soma ) ;out . p r i n t l n ( "<h1>" ) ;out . p r i n t l n ( "</body>" ) ;out . p r i n t l n ( "</html>" ) ;out . c l o s e ( ) ;

}}

Exemplo 7.2 Servlet com problemas de concorrência.

Note que o Servlet utiliza uma variável de instância para refe-renciar o Vector que armazena os valores. Se não forem usadasprimitivas de sincronização (como no código do exemplo) e se duasrequisições chegarem simultaneamente ao Servlet o resultado podeser inconsistente, uma vez que o Vector poderá conter parte dosvalores de uma requisição e parte dos valores de outra requisição.Neste caso em particular, para corrigir esse problema basta decla-rar a variável como local ao método doPost() ou usar primitivasde sincronização.

154 Java na prática

7.7 Obtendo Informações sobre a Requi-sição

O objeto HttpServletRequest passado para o Servlet contém vá-rias informações importantes relacionadas com a requisição, comopor exemplo o método empregado (POST ou GET), o protocoloutilizado, o endereço remoto, informações contidas no cabeçalhoe muitas outras. O Servlet do exemplo 7.3 retorna uma páginacontendo informações sobre a requisição e sobre o cabeçalho darequisição.

import java . i o . ∗ ;import java . u t i l . ∗ ;import javax . s e r v l e t . ∗ ;import javax . s e r v l e t . http . ∗ ;

public class RequestInfo extends HttpServ l e t{

public void doGet ( HttpServ letRequest req ,HttpServletResponse r e s )

throws IOException , Se rv l e tExcept ion{

r e s . setContentType ( " text /html" ) ;Pr intWriter out = r e s . getWriter ( ) ;out . p r i n t l n ( "<html><head>" ) ;out . p r i n t l n (

"<t i t l e >Exemplo de Requi s i cao de Info </ t i t l e >" ) ;out . p r i n t l n ( "</head><body>" ) ;out . p r i n t l n (

"<h3> Exemplo sobre Requi s i cao de In f o </h3>" ) ;out . p r i n t l n ( "Metodo : " + req . getMethod()+"<br>" ) ;out . p r i n t l n (

"Request URI : " + req . getRequestURI ()+"<br>" ) ;out . p r i n t l n (

" Protoco lo : " + req . ge tPro toco l ()+"<br>" ) ;out . p r i n t l n (

"PathInfo : " + req . getPathInfo ()+"<br>" ) ;out . p r i n t l n ( "Endereco remoto : "+req . getRemoteAddr()+

"<br><br>" ) ;Enumeration e = req . getHeaderNames ( ) ;while ( e . hasMoreElements ( ) ){

S t r ing name = ( St r ing ) e . nextElement ( ) ;S t r ing value = req . getHeader (name ) ;

Tópicos Avançados 155

out . p r i n t l n (name + " = " + value+"<br>" ) ;}out . p r i n t l n ( "</body></html>" ) ;

}

public void doPost ( HttpServ letRequest req ,HttpServletResponse r e s )

throws IOException , Se rv l e tExcept ion{

doGet ( req , r e s ) ;}

}

Exemplo 7.3 Servlet que retorna as informações sobre arequisição.

Note que o método doPost() chama o método doGet(), demodo que o Servlet pode receber os dois tipos de requisição. A�gura 7.12 mostra o resultado de uma execução do Servlet doexemplo 7.3.

Exemplo sobre Requisicao de InfoMetodo: GETRequest URI: /teste/reqProtocolo: HTTP/1.1PathInfo: nullEndereco remoto: 127.0.0.1

accept =image/gif,image/x-xbitmap, image/jpeg, image/pjpeg,application/vnd.ms-excel, application/msword,application/vnd.ms-powerpoint, */*

accept-language = pt-braccept-encoding = gzip, deflateuser-agent =Mozilla/4.0(compatible;MSIE 5.0;Windows 98;DigExt)host = localhost:8080connection = Keep-Alivecookie = JSESSIONID=B536655B2CD30258F11011808C3C6205

Figura 7.12 Saída da execução do Servlet que exibe asinformações sobre a requisição.

156 Java na prática

7.8 Lidando com FormuláriosSer capaz de lidar com as informações contidas em formuláriosHTML é fundamental para qualquer tecnologia de desenvolvi-mento de aplicações para Web. É por meio de formulários que osusuários fornecem dados, preenchem pedidos de compra e (aindamais importante) digitam o número do cartão de crédito. As in-formações digitadas no formulário chegam até o Servlet por meiodo objeto HttpServletRequest e são recuperadas por meio dométodo getParameter() deste objeto. Todo item de formulárioHTML possui um nome e esse nome é passado como argumentopara o método getParameter() que retorna, na forma de String,o valor do item de formulário.

O Servlet do exemplo 7.4 exibe o valor de dois itens de formu-lários do tipo text. Um denominado nome e o outro denominadode sobrenome. Em seguida o Servlet cria um formulário contendoos mesmos itens de formulário. Note que um formulário é criadopor meio do tag <form>. Como parâmetros opcionais deste tagtemos o método da requisição (method), é a URL para onde serásubmetida a requisição (action). No caso do exemplo, o métodoadotado é o POST e a requisição será submetida ao próprio ServletForm.

import java . i o . ∗ ;import java . u t i l . ∗ ;import javax . s e r v l e t . ∗ ;import javax . s e r v l e t . http . ∗ ;

public class Form extends HttpServ le t{

public void doGet ( HttpServ letRequest req ,HttpServletResponse r e s )

throws IOException , Se rv l e tExcept ion{

r e s . setContentType ( " text /html" ) ;

Pr intWriter out = r e s . getWriter ( ) ;out . p r i n t l n ( "<html>" ) ;out . p r i n t l n (

"<head><t i t l e >Trata formular io </ t i t l e ></head>" ) ;out . p r i n t l n ( "<body bgco lo r=\"white\">" ) ;

Tópicos Avançados 157

out . p r i n t l n ( "<h3>Trata formular io </h3>" ) ;S t r ing nome = req . getParameter ( "nome" ) ;S t r ing sobreNome = req . getParameter ( " sobrenome" ) ;i f ( nome != null | | sobreNome != null ){

out . p r i n t l n ( "Nome = " + nome + "<br>" ) ;out . p r i n t l n ( "Sobrenome = " + sobreNome ) ;

}out . p r i n t l n ( "<P>" ) ;out . p r i n t ( "<form act i on=\"Form\" method=POST>" ) ;out . p r i n t l n (

"Nome : < input type=text s i z e =20 name=nome><br>" ) ;out . p r i n t l n ( "Sobrenome : "+

"<input type=text s i z e =20 name=sobrenome>" ) ;out . p r i n t l n ( "<br><input type=submit>" ) ;out . p r i n t l n ( "</form>" ) ;out . p r i n t l n ( "</body></html>" ) ;

}

public void doPost ( HttpServ letRequest req ,HttpServletResponse r e s )

throws IOException , Se rv l e tExcept ion{

doGet ( req , r e s ) ;}

}

Exemplo 7.4 Servlet para lidar com um formulário simples.

7.9 Lidando com CookiesUm cookie nada mais é que um bloco de informação que é enviadodo servidor para o navegador no header página. A partir de então,dependendo do tempo de validade do cookie, o navegador reenviaessa informação para o servidor a cada nova requisição. Dependodo caso o cookie é também armazenado no disco da máquina cli-ente e quando o site é novamente visitado o cookie é enviado no-vamente para o servidor, fornecendo a informação desejada.

Os cookies foram a solução adotada pelos desenvolvedores doNetscape para implementar a identi�cação de clientes no proto-colo HTTP, que não é orientado à conexão. Esta solução, apesar

158 Java na prática

das controvérsias sobre a possibilidade de quebra de privacidade,passou ser amplamente adotada e hoje os cookies são parte inte-grante do padrão Internet, normalizados pela norma RFC 2109.

A necessidade da identi�cação do cliente de onde partiu a re-quisição e o monitoramento de sua interação com o site (denomi-nada de sessão) é importante para o desenvolvimento de sistemaspara a Web pelas seguintes razões:

• É necessário associar os itens selecionados para compra como usuário que deseja adquirí-los. Na maioria da vezes a sele-ção dos itens de uma compra é feita por meio da navegaçãode várias páginas do site e a todo instante é necessário dis-tinguir os usuários que estão realizando as requisições.

• É necessário acompanhar as interação do usuário com o sitepara observar seu comportamento e, a partir dessas infor-mações, realizar adaptações no site para atrair um maiornúmero de usuários ou realizar campanhas de marketing.

• É necessário saber que usuário está acessando o site para,de acordo com o seu per�l, fornecer uma visualização e umconjunto de funcionalidades adequadas às suas preferências.

Todas essas necessidades não podem ser atendidas com o usobásico do protocolo HTTP, uma vez que ele não é orientado àsessão ou conexão. Com os cookies é possível contornar essa de�-ciência, uma vez que podem ser usados para identi�car os clientese armazenar informações. Existem outras formas de contornar ade�ciência do protocolo de HTTP, como a codi�cação de URL e ouso de campos escondidos nas páginas HTML, mas o uso de coo-kies é a técnica mais utilizada, por ser mais simples e padronizada.No entanto, o usuário pode impedir que o navegador aceite coo-kies, o que torna o ato de navegar pela Web muito desagradável.Neste caso, é necessário utilizar as outras técnicas para controlede sessão.

A API Servlet permite a manipulação explícita de cookies.Para controle de sessão o programador pode manipular direta-mente os cookies, ou usar uma abstração de nível mais alto, im-plementada por meio da classe HttpSession. Se o cliente não

Tópicos Avançados 159

permitir o uso de cookies a API Servlet fornece métodos para acodi�cação de URL. O exemplo 7.5 mostra o uso de cookies paraarmazenar as informações digitadas em um formulário.

import java . i o . ∗ ;import javax . s e r v l e t . ∗ ;import javax . s e r v l e t . http . ∗ ;

public class CookieTeste extends HttpServ l e t{

public void doGet ( HttpServ letRequest req ,HttpServletResponse r e s )

throws IOException , Se rv l e tExcept ion{

r e s . setContentType ( " text /html" ) ;

Pr intWriter out = r e s . getWriter ( ) ;out . p r i n t l n ( "<html>" ) ;out . p r i n t l n ( "<body bgco lo r=\"white\">" ) ;out . p r i n t l n ( "<head><t i t l e >Teste de Cookies " ) ;out . p r i n t l n ( "</ t i t l e ></head><body>" ) ;

out . p r i n t l n ( "<h3>Teste de Cookies</h3>" ) ;

Cookie [ ] c ook i e s = req . getCookies ( ) ;i f ( c ook i e s . l ength > 0){

for ( int i = 0 ; i < cook i e s . l ength ; i++){

Cookie cook i e = cook i e s [ i ] ;out . p r i n t ( "Cookie Nome : " +

cook i e . getName () + "<br>" ) ;out . p r i n t l n ( " Cookie Valor : " +

cook i e . getValue () +"<br><br>" ) ;}

}

St r ing cName = req . getParameter ( " cookienome" ) ;S t r ing cValor = req . getParameter ( " cook i e va l o r " ) ;i f ( cName != null && cValor != null ){

Cookie cook i e = new Cookie (cName , cValor ) ;r e s . addCookie ( cook i e ) ;out . p r i n t l n ( "<P>" ) ;out . p r i n t l n ( "<br>" ) ;out . p r i n t ( "Nome : "+cName +"<br>" ) ;

160 Java na prática

out . p r i n t ( "Valor : "+cValor ) ;}

out . p r i n t l n ( "<P>" ) ;out . p r i n t (

"<form act i on=\"CookieTeste \" method=POST>" ) ;out . p r i n t l n ( "Nome : "+

"<input type=text l ength=20 name=cookienome>" ) ;out . p r i n t l n ( "<br>Valor : "+

"<input type=text l ength=20 name=cook i eva lo r>" ) ;out . p r i n t l n ( "<br><input type=submit></form>" ) ;out . p r i n t l n ( "</body>" ) ;out . p r i n t l n ( "</html>" ) ;

}

public void doPost ( HttpServ letRequest req ,HttpServletResponse r e s )

throws IOException , Se rv l e tExcept ion{

doGet ( req , r e s ) ;}

}

Exemplo 7.5 Servlet para lidar com Cookies.Para se criar um cookie é necessário criar um objeto Cookie,

passando para o construtor um nome e um valor, sendo ambosinstâncias de String. O cookie é enviado para o navegador pormeio do método addCookie() do objeto HttpServletResponse.Um vez que os cookies são enviados no header da página, o mé-todo addCookie() deve ser chamado antes do envio de qual-quer conteúdo para o navegador. Para recuperar os cookies en-viados pelo navegador usa-se o método getCookies() do objetoHttpServletRequest que retorna um array de Cookie. Os mé-todos getName() e getvalue() do objeto Cookie são utilizadospara recuperar o nome o valor da informação associada ao cookie.

Os objetos da classe Cookie possuem vários métodos para con-trole do uso de cookies. É possível de�nir o tempo de vida máximodo cookie, os domínios que devem receber o cookie (por default odomínio que deve receber o cookie é o que o criou), o diretórioda página que deve receber o cookie, se o cookie deve ser envi-ado somente sob um protocolo seguro, etc. Por exemplo, para

Tópicos Avançados 161

de�nir a idade máxima de um cookie devemos utilizar o métodosetMaxAge(), passando um inteiro como parâmetro. Se o inteirofor positivo indicará em segundos o tempo máximo de vida do co-okie. Um valor negativo indica que o cookie deve apagado quandoo navegador terminar. O valor zero indica que o cookie deve serapagado imediatamente. O trecho de código do exemplo 7.6 mos-tra algumas alterações no comportamento default de um cookie.

. . .Cookie cook i e = new Cookie (cName , cValor ) ;// todos os domínios como dpi . u fv . br mas não// como ∗ . dpi . u fv . brcook i e . setDomain ( " ∗ . uvf . br" ) ;

// uma hora de tempo de vidacook i e . setMaxAge ( 3 6 0 0 ) ;. . .

Exemplo 7.6 Mudanças no comportamento default do cookie.

7.10 Lidando com SessõesA manipulação direta de cookies para controle de sessão é umtanto trabalhosa, uma vez que o usuário deve se preocupar coma identi�cação, tempo de vida e outros detalhes. Por isso a APIServlet fornece um objeto com controles de nível mais alto paramonitorar a sessão, o HttpSession. O objeto HttpSession mo-nitora a sessão utilizando cookies de forma transparente. No en-tanto, se o cliente não aceitar o uso de cookies é possível utilizarcomo alternativa a codi�cação de URL para adicionar o identi�-cador da sessão. Essa opção, apesar de ser mais genérica, não áprimeira opção devido a possibilidade de criação de gargalos pelanecessidade da análise prévia de todas requisições que chegam aoservidor. O exemplo 7.7 mostra o uso de um objeto HttpSessionpara armazenar as informações digitadas em um formulário.

import java . i o . ∗ ;import java . u t i l . ∗ ;

162 Java na prática

import javax . s e r v l e t . ∗ ;import javax . s e r v l e t . http . ∗ ;

public class Ses s i onTes te extends HttpServ l e t{

public void doGet ( HttpServ letRequest req ,HttpServletResponse resp )

throws IOException , Se rv l e tExcept ion{

resp . setContentType ( " text /html" ) ;

Pr intWriter out = resp . getWriter ( ) ;out . p r i n t l n ( "<html><head>" ) ;out . p r i n t l n ( "<t i t l e >Teste de Sessao</ t i t l e >" ) ;out . p r i n t l n ( "</head>" ) ;out . p r i n t l n ( "<body>" ) ;out . p r i n t l n ( "<h3>Teste de Sessao</h3>" ) ;HttpSess ion s e s s i o n = req . g e tS e s s i on ( true ) ;out . p r i n t l n ( " I d e n t i f i c a d o r : " + s e s s i o n . ge t Id ( ) ) ;out . p r i n t l n ( "<br>" ) ;out . p r i n t l n ( "Data : " ) ;out . p r i n t l n (new Date ( s e s s i o n . getCreationTime ())+

"<br>" ) ;out . p r i n t l n ( "Ultimo ace s so : " ) ;out . p r i n t l n (new Date ( s e s s i o n . getLastAccessedTime ( ) ) ) ;

S t r ing nomedado = req . getParameter ( "nomedado" ) ;S t r ing valordado = req . getParameter ( " valordado " ) ;i f ( nomedado != null && valordado != null ){

s e s s i o n . s e tAt t r i bu t e (nomedado , valordado ) ;}

out . p r i n t l n ( "<P>" ) ;out . p r i n t l n ( "Dados da Sessao : " + "<br>" ) ;Enumeration valueNames =

s e s s i o n . getAttributeNames ( ) ;

while ( valueNames . hasMoreElements ( ) ){

St r ing name = ( St r ing ) valueNames . nextElement ( ) ;S t r ing value = ( St r ing )

s e s s i o n . ge tAt t r ibute (name ) ;out . p r i n t l n (name + " = " + value+"<br>" ) ;

}

Tópicos Avançados 163

out . p r i n t l n ( "<P>" ) ;out . p r i n t (

"<form act i on=\"Ses s i onTes te \" method=POST>" ) ;out . p r i n t l n (

"Nome:< input type=text s i z e =20 name=nomedado>" ) ;out . p r i n t l n ( "<br>Valor : "+

"<input type=text s i z e =20 name=valordado>" ) ;out . p r i n t l n ( "<br><input type=submit>" ) ;out . p r i n t l n ( "</form>" ) ;out . p r i n t l n ( "</body></html>" ) ;

}

public void doPost ( HttpServ letRequest req ,HttpServletResponse resp )

throws IOException , Se rv l e tExcept ion{

doGet ( req , re sp ) ;}

}

Exemplo 7.7 Servlet para lidar com Sessões.

Para controlar a sessão é necessário obter um objetoHttpSession por meio do método getSession() do objetoHttpServletRequest. Opcionalmente, o método getSession()recebe como argumento um valor booleano que indica se é paracriar o objeto HttpSession,caso ele não exista (argumento true)ou se é para retorna null (argumento false). Para se associar umobjeto ou informação à sessão usa-se o método setAttribute()do objeto HttpSession, passando para o método um String eum objeto que será identi�cado pelo String. Note que o métodoaceita qualquer objeto e, portanto, qualquer objeto pode ser asso-ciado à sessão. Os objetos associados a uma sessão são recupera-dos com o uso método getAttribute() do objeto HttpSession,que recebe como argumento o nome associado ao objeto. Parase obter uma enumeração do nomes associados à sessão usa-seo método getAttributeNames() do objeto HttpSession. A �-gura 7.13 mostra o resultado da execução do exemplo 7.7.

164 Java na prática

Figura 7.13 Saída resultante da execução do Servlet que lidacom Sessões.

7.11 JSPServlets é uma boa idéia, mas você se imaginou montando umapágina complexa usando println()? Muitas vezes o desenvol-vimento de um site é uma tarefa complexa que envolve váriospro�ssionais. A tarefa de projeto do layout da página �ca a cargodo Web Designer, incluindo a diagramação dos textos e imagens,aplicação de cores, tratamento das imagens, de�nição da estruturada informação apresentada no site e dos links para navegação pelamesma. Já o Desenvolvedor Web é responsável pela criação dasaplicações que vão executar em um site. O trabalho destes doispro�ssionais é somado na criação de um único produto, mas du-rante o desenvolvimento a interferência mútua deve ser a mínimapossível. Ou seja, um pro�ssional não deve precisar alterar o que éfoi feito pelo outro pro�ssional para cumprir sua tarefa. A tecno-logia Servlet não nos permite atingir esse objetivo. Por exemplo,suponha que um Web Designer terminou o desenvolvimento deuma página e a entregou para o Desenvolvedor Web codi�car emum Servlet. Se após a codi�cação o Web Designer desejar realizaruma alteração na página será necessário que ele altere o código doServlet (do qual ele nada entende) ou entregar uma nova páginapara o Desenvolvedor Web para que ele a codi�que totalmente,

Tópicos Avançados 165

mais uma vez. Qualquer uma dessas alternativas é indesejável efoi devido a esse problema a que Sun desenvolveu uma tecnologiabaseada em Servlets chamada de JSP.

Java Server Pages (JSP) são páginas HTML que incluem có-digo Java e outros tags especiais. Desta forma as partes estáticasda página não precisam ser geradas por println(). Elas se encon-tram na própria página. A parte dinâmica é gerada pelo códigoJSP. Assim a parte estática da página pode ser projetada por umWeb Designer que nada sabe de Java.

A primeira vez que uma página JSP é carregada pelo con-tainer JSP o código Java é compilado gerando um Servlet queé executado, gerando uma página HTML que é enviada para onavegador. As chamadas subsequentes são enviadas diretamenteao Servlet gerado na primeira requisição, não ocorrendo mais asetapas de geração e compilação do Servlet.

A �gura 7.14 mostra um esquema das etapas de execução deuma página JSP na primeira vez que é requisitada. Na etapa (1)a requisição é enviada para um servidor Web que reencaminha arequisição (etapa 2) para o container Servlet/JSP. Na etapa (3)o container veri�ca que não existe nenhuma instância de Servletcorrespondente à página JSP. Neste caso, a página JSP é traduzidapara código fonte de uma classe Servlet que será usada na respostaà requisição. Na etapa (4) o código fonte do Servlet é compilado,e na etapa (5) é criada uma instância da classe. Finalmente, naetapa (6) é invocado o método service() da instância Servletpara gerar a resposta à requisição.

A idéia de se usar scripts de linguagens de programação em pá-ginas HTML que são processados no lado servidor para gerar con-teúdo dinâmico não é restrita à linguagem Java. Existem váriassoluções desse tipo fornecida por outros fabricantes. Apresenta-se, a seguir, comparações com outras duas tecnologias similares aJSP.

166 Java na prática

Figura 7.14 Etapas da primeira execução de uma página JSP.

7.11.1 PHP X JSPPHP (Personal Home Pages) é uma linguagem script para ser exe-cutada no lado servidor criada em 1994 como um projeto pessoalde Rasmus Lerdorf. A sintaxe é fortemente baseada em C maspossui elementos de C++, Java e Perl. Possui suporte a progra-mação OO por meio de classes e objetos. Possui também suporteextensivo à Banco de dados ODBC, MySql, Sybase, Oracle e ou-tros. PHP é uma linguagem mais fácil no desenvolvimento de pe-quenas aplicações para Web em relação à JSP, uma vez que é umalinguagem mais simples e menos rígida do que JSP. No entanto,a medida que passamos para aplicações de maior porte, o uso dePHP não é indicado, uma vez que necessário o uso de linguagenscom checagem mais rígidas e com maior suporte à escalabilidade,como é o caso de Java.

7.11.2 ASP X JSPASP (ActiveServer Pages) é a solução desenvolvida pelaMicrosoft c©para atender as requisições feitas a servidores Web.Incorporada inicialmente apenas ao Internet Information Server

Tópicos Avançados 167

(IIS), atualmente já é suportada por outros servidores populares,como o Apache. O desenvolvimento de páginas que usam ASPenvolve a produção de um script contendo HTML misturado comblocos de código de controle ASP. Este código de controle podeconter scripts em JavaScript ou VBScript. A primeira vantagemde JSP sobre ASP é que a parte dinâmica é escrita em Java enão Visual Basic ou outra linguagem proprietária da Microsoft,portanto JSP é mais poderoso e fácil de usar. Em segundo lugarJSP é mais portável para outros sistemas operacionais e servidoresWEB que não sejam Microsoft.

7.11.3 Primeiro exemplo em JSPPara que o leitor possa ter uma idéia geral da tecnologia JSPapresentaremos agora a versão JSP do Olá mundo. O exemplo 7.8mostra o código da página.

<html><head>

<t i t l e>Exemplo JSP</ t i t l e></head><body>

<%String x = "Olá Mundo ! " ;

%><%=x%>

</body></html>

Exemplo 7.8 Versão JSP do Olá mundo.

Quem está habituado aos tags HTML notará que se trata basica-mente de uma página HTML contendo código Java delimitado pelossímbolos �<%� e �%>�. No primeiro trecho de script é declarada umavariável x com o valor �Olá mundo�. No segundo trecho de script oconteúdo da variável x é extraído e colocado na página resultante daexecução do Servlet correspondente. Em seguida mostraremos comoexecutar o exemplo 7.8.

168 Java na prática

7.11.4 Executando o arquivo JSPPara executar o exemplo 7.8 salve-o com a extensão .jsp. Por exem-plo, ola.jsp. Se você estiver usando o servidor Tomcat, coloque oarquivo no subdiretório /webapps/contexto do Tomcat. Por exemplowebapps/teste/. Para invocar o arquivo JSP basta embutir a URL emuma página ou digitar diretamente a seguinte URL no navegador.

http://localhost:8080/teste/ola.jsp

O Servlet criado a partir da página JSP é colocado em um diretóriode trabalho. No caso do Tomcat o Servlet é colocado em subdiretórioassociado à aplicação e subordinado ao diretório /work do Tomcat. Oexemplo 7.9 mostra os principais trechos do Servlet criado a partir datradução do arquivo ola.jsp pelo tradutor do Tomcat. Note que oServlet é subclasse de uma classe HttpJspBase e não da HttpServlet.Além disso, o método que é executado em resposta à requisição é ométodo _jspService() e não o método service(). Note também quetodas as partes estáticas da página JSP são colocadas como argumentosdo método write() do objeto referenciado por out.

. . .public class ola_jsp extends HttpJspBase {

. . .public void _jspServ i ce ( HttpServ letRequest request ,

HttpServletResponse re sponse )throws java . i o . IOException , Se rv l e tExcept i on {

JspFactory _jspxFactory = null ;javax . s e r v l e t . j sp . PageContext pageContext = null ;HttpSess ion s e s s i o n = null ;Se rv le tContext app l i c a t i on = null ;S e rv l e tCon f i g c on f i g = null ;JspWriter out = null ;Object page = this ;JspWriter _jspx_out = null ;

try {. . .out . wr i t e ( "<html>\r \n " ) ;out . wr i t e ( "<head>\r \n " ) ;out . wr i t e ( "<t i t l e >Exemplo JSP" ) ;out . wr i t e ( "</ t i t l e >\r \n " ) ;out . wr i t e ( "</head>\r \n " ) ;out . wr i t e ( "<body>\r \n " ) ;

Tópicos Avançados 169

St r ing x = "Olá Mundo ! " ;

out . wr i t e ( "\ r \n " ) ;out . p r i n t ( x ) ;out . wr i t e ( "\ r \n " ) ;out . wr i t e ( "</body>\r \n" ) ;out . wr i t e ( "</html>" ) ;

} catch ( Throwable t ) {. . .

}}

}

Exemplo 7.9 Servlet correspondente à página JSP do "Olá mundo".

7.11.5 Objetos implícitosNo exemplo 7.9 pode-se ver a declaração de variáveis que referenciamalguns objetos importantes. Estas variáveis estão disponíveis para oprojetista da página JSP. As variáveis mais importantes são:

Classe VariávelHttpServletRequest requestHttpServletResponse responsePageContext pageContextServletContext applicationHttpSession sessionJspWriter out

Tabela 7.2 Principais variáveis disponíveis na página JSP.

Os objetos referenciados pelas variáveis request e response já ti-veram seu uso esclarecido na seção sobre Servlets. O objeto do tipoJspWriter tem a mesma função do PrinterWriter do Servlet. Os ou-tros objetos terão sua função esclarecida mais adiante.

7.11.6 Tags JSPOs tags JSP possuem a seguinte forma geral:

<% Código JSP %>

170 Java na prática

O primeiro caractere % pode ser seguido de outros caracteres quedeterminam o signi�cado do código dentro do tag. Os tags JSP possuemcorrespondência com os tags XML. Existem cinco categorias de tagsJSP:

1. Expressões2. Scriptlets3. Declarações4. Diretivas5. Comentários

Em seguida comentaremos cada uma dessas categorias.

Expressões<%= expressões %>

Expressões são avaliadas, convertidas para String e inseridas napágina enviada. A avaliação é realizada em tempo de execução, quandoa página é requisitada.

Exemplos:

<%= new java.util.Date() %><%= request.getMethod() %>

No primeiro exemplo é inserido na página a data corrente em mi-lésimo de segundos e no segundo é inserido o nome do método (GET,POST, etc.) usado na requisição. Note que cada expressão contémapenas um comando Java. Note também que o comando Java não éterminado pelo caractere `;'.

Scriptlets<%= código Java %>

Quando é necessário mais de um comando Java ou, o resultado dacomputação não é para ser colocado na página de resposta, é precisousar outra categoria de tags JSP: os Scriptlets . Os Scriptlets permiteminserir trechos de código em Java na página JSP. O exemplo 7.10 mostrauma página JSP contendo um Scriptlet que transforma a temperaturadigitada em Celsius para o equivalente em Fahrenheit.

Tópicos Avançados 171

<html><head><t i t l e>Conversao Ce l s i u s Fahrenheit</ t i t l e></head><body>

<%String va lo r = reque s t . getParameter ( " c e l s i u s " ) ;i f ( va l o r != nu l l ){

double f = Double . parseDouble ( va l o r )∗9/5 +32;out . p r i n t l n ( "<P>" ) ;out . p r i n t l n (

"<h2>Valor em Fahrenheit : " + f +"<h2><br>" ) ;}

%><form action=conversao . j sp method=POST>

Ce l c iu s : <input type=text size=20 name=c e l s i u s><br><input type=submit>

</form>

</body></html>

Exemplo 7.10 Página JSP que converte graus Celsius paraFahrenheit.

Note o uso das variáveis request e out sem a necessidade de decla-ração. Todo o código digitado é inserido no método _jspService(). A�gura 7.15 mostra o resultado da requisição após a digitação do valor30 na caixa de texto do formulário.

Figura 7.15 Resultado da conversão de 30 graus Celsius.

172 Java na prática

O código dentro do scriptlet é inserido da mesma forma que é escritoe todo o texto HTML estático antes e após um scriptlet é convertidopara argumentos do método write(). Desta forma o scriptlet não pre-cisa conter comandos para código estático. Os blocos de controle aber-tos afetam o código HTML envolvido por scriptlets. O exemplo 7.11mostra duas formas de se produzir o mesmo efeito. No código da es-querda os Scriplets se intercalam com código HTML. O código HTML,quando da tradução da página JSP para Servlet, é inserido como argu-mentos de métodos println() ou write() gerando o código da direita.Ambas as formas podem ser usadas em páginas JSP e produzem omesmo efeito.

Previs&atilde;o do Tempo<% if (Math.random() < 0.5) { %>Hoje vai <B>fazer sol</B>!<% } else { %>Hoje vai <B>chover</B>!<% } %>

out.println("Previs&atilde;o do Tempo");if (Math.random() < 0.5) {

out.println("Hoje vai <B>fazer sol</B>!");} else {

out.println("Hoje vai <B>chover</B>!");}

Exemplo 7.11 Dois códigos equivalentes.

DeclaraçõesUma declaração JSP permite de�nir variáveis ou métodos que são inse-ridos no corpo do Servlet. Como as declarações não geram saída, elassão normalmente usadas em combinação com expressões e scriptlets.O exemplo 7.12 mostra a declaração de uma variável que é usada paracontar o número de vezes que a página corrente foi requisitada desdeque foi carregada.

<%! Pr ivate int numAcesso = 0; %>Acessos desde carregada :<%= ++ numAcesso %>

Exemplo 7.12 Declaração de uma variável usando o tag dedeclaração.

As variáveis declaradas desta forma serão variáveis de instância.Já as variáveis declaradas em Scriptlets são variáveis locais ao método_jspService(). Por isso é possível contar o número de requisiçõescom o exemplo 7.12. Se variável fosse declarada em um Scriptlet a

Tópicos Avançados 173

variável seria local ao método _jspService() e, portanto, teria seuvalor reinicializado a cada chamada.

Como já foi dito, os tags de declarações permitem a declaraçãode métodos. O exemplo 7.13 mostra a declaração de um método queconverte Celsius para Fahrenheit.

<%!private double converte (double c ){

return c ∗9/5 +32;}

%>

Exemplo 7.13 Declaração de um método para a conversão deCelsius para Fahrenheit.

ComentáriosExistem dois tipos de comentários utilizados em páginas JSP. O pri-meiro exclui todo o bloco comentado da saída gerada pelo processa-mento da página. A forma geral deste tipo de comentário é a seguinte:

<%� comentário �%>

O segundo tipo de comentário é o utilizado em páginas HTML.Neste caso o comentário é enviado dentro da página de resposta. Aforma geral deste tipo de comentário é a seguinte:

<!- comentário �>

DiretivasDiretivas são mensagens para JSP container. Elas não enviam nadapara a página mas são importantes para de�nir atributos JSP e depen-dências com o JSP container. A forma geral das diretivas é a seguinte:

<%@ Diretiva atributo="valor"%>

ou

174 Java na prática

<%@ Diretiva atributo1 ="valor1"atributo2 ="valor2". . .atributon ="valorn" %>

Em seguida comentaremos as principais diretivas.

Diretiva page

<%@ page atributo 1 ="valor 1". . . atributo n ="valor n"%>

Atributo e Forma Geral Descriçãoimport="package.class"ouimport="package.class1,. . .,

package.classn"

Permite especi�car os pacotes que devemser importados para serem usados na pá-gina JSP. Exemplo:<%@ page import="java.util.*" %>

contentType="MIME-Type" Especi�ca o tipo MIME da saída. O de-fault é text/html. Exemplo:<%@ page contentType="text/plain" %>possui o mesmo efeito do scriptlet

<%response.setContentType("text/plain");%>

isThreadSafe="true|false" Um valor true (default) indica um pro-cessamento normal do Servlet, onde múl-tiplas requisições são processadas simulta-neamente. Um valor false indica que oprocessamento deve ser feito por instânciasseparadas do Servlet ou de forma serial.

session="true|false" Um valor true (default) indica que a va-riável prede�nida session (HttpSession)deve ser associada à sessão, se existir, casocontrário uma nova sessão deve ser criadae associada a ela. Um valor false indicaque nenhuma sessão será usada.

buffer="tamkb|none" Especi�ca o tamanho do bu�er para escritausado pelo objeto JspWriter. O tamanhodefault não é menor que 8k.

autoflush="true|false" Um valor true (default) indica que o bu�erdeve ser esvaziado quando estiver cheio.

info="mensagem" De�ne uma cadeia de caracteres que podeser recuperada via getServletInfo().

Tópicos Avançados 175

errorPage="url" Especi�ca a página JSP que deve ser pro-cessada em caso de exceções não captura-das.

isErrorPage="true|false" Indica se a página corrente pode atuarcomo página de erro para outra páginaJSP. O default é false.

language="java" Possibilita de�nir a linguagem que estásendo usada. No momento a única pos-sibilidade é Java.

Tabela 7.3 Atributos da diretiva page.

A diretiva page permite a de�nição dos seguintes atributos, queestão descritos na tabela 7.3:

importcontentTypeisThreadSafesessionbufferautoflushinfoerrorPageisErrorPagelanguage

Tabela 7.4 Tabela de diretivas Page.

Diretiva include<%@ include file="uri relativa" %>

Permite incluir arquivos no momento em que a página JSP é tra-duzida em um Servlet. Por exemplo:

<%@ include file="/meuarq.html" %>

7.11.7 Extraindo Valores de FormuláriosUma página JSP, da mesma forma que nos Servlet, pode usar o objetoreferenciado pela variável request para obter os valores dos parâmetrosde um formulário. O exemplo 7.10 usado para converter graus Celciusem Fahrenheit fez uso deste recurso. O exemplo 7.14 mostra outrapágina JSP com formulário. Note que o scriptlet é usado para obtero nome e os valores de todos os parâmetros contidos no formulário.

176 Java na prática

Como o método getParameterNames() retorna uma referência a umobjeto Enumeration é preciso importar o pacote java.util, por meioda diretiva page.

<%@ page import=" java . u t i l .∗ " %><html><body><H1>Formulário</H1><%

Enumeration campos = reques t . getParameterNames ( ) ;while ( campos . hasMoreElements ( ) ) {

St r ing campo = ( St r ing ) campos . nextElement ( ) ;S t r ing va lo r = reques t . getParameter ( campo); %><l i ><%= campo %> = <%= va lo r %></l i >

<% } %>

<form method="POST" act i on="form . j sp ">Nome:< input type=" text " s i z e="20" name="nome" ><br>Tele fone :< input type=" text " s i z e="20" name=" t e l e f o n e "><br><INPUT TYPE=submit name=submit value=" env ie ">

</form></body></html>

Exemplo 7.14 Página JSP com formulário.

A �gura 7.16 mostra o resultado da requisição após a digitação dosvalores Alcione e 333-3333 nas caixas de texto do formulário.

Figura 7.16 Saída do exemplo 7.14.

Tópicos Avançados 177

7.11.8 Criando e Modi�cando Cookies

Da mesma forma que nos Servlets, os cookies em JSP são tratados pormeio da classe Cookie. Para recuperar os cookies enviados pelo navega-dor usa-se o método getCookies() do objeto HttpServletRequest queretorna um arranjo de Cookie. Os métodos getName() e getvalue()do objeto Cookie são utilizados para recuperar o nome e o valor dainformação associada ao cookie. O cookie é enviado para o navegadorpor meio do método addCookie() do objeto HttpServletResponse. Oexemplo 7.15 mostra uma página JSP que exibe todos os cookies rece-bidos em uma requisição e adiciona mais um na resposta.

<html><body><H3>Ses s i on id : <%= s e s s i o n . ge t Id () %></H3><%

Cookie [ ] c ook i e s = reques t . getCookies ( ) ;f o r ( i n t i = 0 ; i < cook i e s . l ength ; i++) { %>

Cookie name : <%= cook i e s [ i ] . getName() %> <br>Value : <%= cook i e s [ i ] . getValue () %><br>ant iga idade máxima em segundos :<%= cook i e s [ i ] . getMaxAge() %><br><% cook i e s [ i ] . setMaxAge (5) ; %>nova idade máxima em segundos :<%= cook i e s [ i ] . getMaxAge() %><br>

<% } %><%! i n t count = 0 ; i n t dcount = 0; %><% response . addCookie (new Cookie ("Cookie " + count++, "Valor " + dcount++)); %></body></html>

Exemplo 7.15 Página JSP que exibe os cookies recebidos.

A �gura 7.17 mostra o resultado após dois acessos seguidos à páginaJSP. Note que existe um cookie a mais com o nome JSESSIONID e valorigual à sessão. Este cookie é o usado pelo container para controlar asessão.

178 Java na prática

Figura 7.17 Saída do exemplo 7.15 após dois acessos.

7.11.9 Lidando com sessõesO atributos de uma sessão são mantidos em um objeto HttpSessionreferenciado pela variável session. Pode-se armazenar valores emuma sessão por meio do método setAttribute() e recuperá-los pormeio do método getAttribute(). O tempo de duração default deuma sessão inativa (sem o recebimento de requisições do usuário) é30 minutos mas esse valor pode ser alterado por meio do métodosetMaxInactiveInterval(). O exemplo 7.16 mostra duas páginas JSP.A primeira apresenta um formulário onde podem ser digitados dois va-lores. Além disso, exibe os atributos armazenados na sessão e de�neo intervalo máximo de inatividade da sessão em 10 segundos. A se-gunda página recebe a submissão do formulário, insere os valores nasessão e apresenta os valores relacionados com a sessão, assim como aidenti�cação da sessão.

<%@ page import=" java . u t i l .∗ " %><html><body><H1>Formulário</H1><H1>Id da s e s s&a t i l d e ; o : <%= s e s s i o n . ge t Id () %></H1><H3>< l i>Essa s e s s&a t i l d e ; o f o i c r i ada em<%= s e s s i o n . getCreationTime () %></ l i></H3>

<H3>< l i>Antigo i n t e r v a l o de i n a t i v i d ade =

Tópicos Avançados 179

<%= se s s i o n . ge tMaxInac t ive Inte rva l () %></ l i><% se s s i o n . s e tMaxInac t i v e In t e rva l (10) ; %>< l i>Novo i n t e r v a l o de i n a t i v i d ad e=

<%= s e s s i o n . ge tMaxInac t ive Inte rva l () %></ l i></H3>

<%Enumeration a t r i b s = s e s s i o n . getAttributeNames ( ) ;whi l e ( a t r i b s . hasMoreElements ( ) ) {String a t r i b =(String ) a t r i b s . nextElement ( ) ;String va lo r =(String ) s e s s i o n . ge tAt t r ibute ( a t r i b ) ; %>< l i><%= at r i b %> = <%= va lo r %></ l i>

<% } %>

<form method="POST" action=" se s sao2 . j sp ">Nome :<input type=" text " s ize="20" name="nome" ><br>Tele fone :<input type=" text " size="20" name=" t e l e f o n e "><br><INPUT TYPE=submit name=submit value=" env ie ">

</form></body></html>

<html><body><H1>Id da s e s s&a t i l d e ; o : <%= s e s s i o n . ge t Id () %></H1><%

String nome = reques t . getParameter ( "nome" ) ;String t e l e f o n e = reques t . getParameter ( " t e l e f o n e " ) ;

i f ( nome != nu l l && nome . l ength ( )>0)s e s s i o n . s e tAt t r i bu t e ( "nome" ,nome ) ;

i f ( t e l e f o n e != nu l l &&t e l e f o n e . l ength ()>0)s e s s i o n . s e tAt t r i bu t e ( " t e l e f o n e " , t e l e f o n e ) ;

%>

<FORMTYPE=POST ACTION=ses sao1 . j sp><INPUT TYPE=submit name=submit Value="Retorna"></FORM></body></html>

Exemplo 7.16 Exemplo do uso de sessão.

O exemplo 7.16 mostra que a sessão é mantida mesmo quando ousuário muda de página. As �gura 7.18 e 7.19 mostram o resultado

180 Java na prática

da requisição após a digitação dos valores Alcione e 333-3333 nascaixas de texto do formulário, a submissão para página sessao2.jsp eo retorno à página sessao1.jsp.

Figura 7.18 Tela da página sessao1.jsp.

Figura 7.19 Tela da página sessao2.jsp.

7.11.10 O Uso de JavaBeansA medida que o código Java, dentro da página JSP, torna-se cada vezmais complexo o desenvolvedor pode-se perguntar: Java em HTML nãoé o problema invertido do HTML em Servlet? O resultado não será tãocomplexo quanto produzir uma página usando println()? Em outraspalavras, estou novamente misturando conteúdo com a forma?

Para solucionar esse problema, a especi�cação de JSP permite o usode JavaBeans para tratar parte do conteúdo da aplicação. Podemosencarar um JavaBean como sendo apenas uma classe Java que obedecea uma certa padronização de nomeação de métodos, formando o que édenominado de propriedade. As propriedades de um bean são acessadaspor meio de métodos que obedecem a convenção getXxxx e setXxxx ,onde Xxxx é o nome da propriedade. Por exemplo, getItem() é ométodo usado para retornar o valor da propriedade item. A sintaxepara o uso de um bean em uma página JSP é:

Tópicos Avançados 181

<jsp:useBean id="nome "class="package.class "/>

Onde �nome� é o identi�cador da variável que conterá uma refe-rência para uma instância do JavaBean. Você também pode modi�caro atributo scope para estabelecer o escopo do bean além da páginacorrente.

<jsp:useBeanid="nome "scope="session "class="package.class "/>

Para modi�car as propriedades de um JavaBean você pode usaro tag jsp:setProperty ou chamar um método explicitamente em umscriptlet. Para recuperar o valor de uma propriedade de um JavaBeanvocê pode usar o jsp:getProperty ou chamar um método explicita-mente em um scriptlet. Quando é dito que um bean tem uma pro-priedade prop do tipo T signi�ca que o bean deve prover um métodogetProp() e um método do tipo setProp(T). O exemplo 7.17 mostrauma página JSP e um JavaBean. A página instancia o JavaBean, alteraa propriedade mensagem e recupera o valor da propriedade, colocando-ona página.

Página bean.jsp

<HTML> <HEAD><TITLE>Uso de beans</TITLE></HEAD> <BODY> <CENTER><TABLEBORDER=5> <TR><TH CLASS="TITLE"> Uso de JavaBeans</TABLE> </CENTER> <P><jsp : useBean id=" t e s t e " c l a s s=" curso . BeanSimples" /><j sp : se tProper ty name=" t e s t e " property="mensagem"

value="Ola mundo ! " /><H1> Mensagem : <I><jsp : getProperty name=" t e s t e " property="mensagem" /></ I></H1></BODY> </HTML>

Arquivo Curso/BeanSimples.java

package curso ;

public class BeanSimples {

182 Java na prática

private St r ing men = "Nenhuma mensagem" ;

public St r ing getMensagem ( ) {return (men ) ;

}

public void setMensagem ( St r ing men) {this .men = men ;

}}

Exemplo 7.17 Exemplo do uso de JavaBean.

A �gura 7.20 mostra o resultado da requisição dirigida à páginabean.jsp.

Figura 7.20 Resultado da requisição à página bean.jsp.

Se no tag setProperty usarmos o valor �*� para o atributo propertyentão todos os valores de elementos de formulários que possuírem nomesiguais à propriedades serão transferidos para as respectivas proprieda-des no momento do processamento da requisição. Por exemplo, sejauma página jsp contendo um formulário com uma caixa de texto comnome mensagem, como mostrado no exemplo 7.18. Note que, neste caso,a propriedade mensagem do JavaBean tem seu valor atualizado para ovalor digitado na caixa de texto, sem a necessidade de uma chamadaexplícita no tag setProperty. Os valores são automaticamente conver-tidos para o tipo correto no bean (somente para tipos primitivos comexceção do tipo double).

<HTML> <HEAD><TITLE>Uso de beans</TITLE> </HEAD>

Tópicos Avançados 183

<BODY> <CENTER><TABLEBORDER=5> <TR><TH CLASS="TITLE"> Uso de JavaBeans</TABLE> </CENTER> <P><jsp : useBean id=" t e s t e " c l a s s=" curso . BeanSimples" /><j sp : se tProper ty name=" t e s t e " property="∗" /><H1> Mensagem : <I><jsp : getProperty name=" t e s t e " property="mensagem" /></ I></H1>

<form method="POST" action="bean2 . j sp ">Texto : <input type=" text " s ize="20" name="mensagem" ><br><INPUT TYPE=submit name=submit value=" env ie ">

</form>

</BODY> </HTML>

Exemplo 7.18 Atualização automática da propriedade.

A �gura 7.21 mostra o resultado da requisição dirigida à páginabean2.jsp após a digitação do texto �Olá�.

Figura 7.21 Resultado da requisição à página bean2.jsp.

7.11.11 Escopo do beanExistem quatro valores possíveis para o escopo de um objeto: page,request, session e application. O default é page. A tabela 7.5descreve cada tipo de escopo.

184 Java na prática

Escopo Descriçãopage Objetos declarados com esse escopo são válidos até a

resposta ser enviada ou a requisição ser encaminhadapara outro programa no mesmo ambiente, ou seja, sópodem ser referenciados nas páginas onde forem de-clarados. Objetos declarados com escopo page são re-ferenciados pelo objeto pagecontext.

request Objetos declarados com esse escopo são válidos du-rante a requisição e são acessíveis mesmo quando arequisição é encaminhada para outro programa nomesmo ambiente. Objetos declarados com escoporequest são referenciados pelo objeto request.

session Objetos declarados com esse escopo são válidos du-rante a sessão desde que a página seja de�nida parafuncionar em uma sessão. Objetos declarados com es-copo session são referenciados pelo objeto session.

application Objetos declarados com esse escopo são acessíveis porpáginas no mesmo servidor de aplicação. Objetos de-clarados com escopo application são referenciadospelo objeto application.

Tabela 7.5 Escopo dos objetos nas páginas JSP.

Implementação de um Carrinho de ComprasO exemplo abaixo ilustra o uso de JSP para implementar um carrinhode compras virtual. O carrinho de compras virtual simula um carrinhode compras de supermercado, onde o cliente vai colocando os produ-tos selecionados para compra até se dirigir para o caixa para fazer opagamento. No carrinho de compras virtual os itens selecionados pelousuário são armazenados em uma estrutura de dados até que o usuá-rio efetue o pagamento. Esse tipo de exemplo exige que a página JSPfuncione com o escopo session para manter o carrinho de comprasdurante a sessão. O exemplo 7.19 mostra um exemplo simples de im-plementação de carrinho de compras. O exemplo é composto por doisarquivos: um para a página JSP e um para o JavaBean que armazenaos itens selecionados.

Página compras.jsp

<html><jsp : useBean id=" car r inho " scope=" s e s s i o n "

c l a s s="compra . Carrinho " /><j sp : se tProper ty name=" car r inho " property="∗" />

Tópicos Avançados 185

<body bgcolor="#FFFFFF">

<%car r inho . processRequest ( r eque s t ) ;String [ ] i tems = car r inho . get Items ( ) ;i f ( i tems . l ength>0) {

%><font size=+2 color="#3333FF">Voc&e c i r c ; comprou os s e gu i n t e s i t e n s :</ font><ol>

<%fo r ( i n t i =0; i<items . l ength ; i++) {

out . p r i n t l n ( "<l i >"+items [ i ] ) ;}

}%></ol><hr><form type=POST action= compras . j sp>

<br><font color="#3333FF" size=+2>Entre um item para ad i c i ona r ou remover :

</ font><br><select NAME=" item">

<opt ion>Te l ev i s&a t i l d e ; o<opt ion>R&aacute ; d io<opt ion>Computador<opt ion>V&iacu t e ; deo Cassete

</ select><p><input TYPE=submit name="submit" value=" ad i c i one ">

<input TYPE=submit name="submit" value="remova"></form></body></html>

JavaBean compra/Carrinho.java

package compra ;

import javax . s e r v l e t . http . ∗ ;import java . u t i l . Vector ;import java . u t i l . Enumeration ;

public class Carrinho {Vector v = new Vector ( ) ;

186 Java na prática

St r ing submit = null ;S t r ing item = null ;

private void addItem ( St r ing name){v . addElement (name ) ; }

private void removeItem ( St r ing name){v . removeElement (name ) ; }

public void set I tem ( St r ing name){ item = name ; }

public void setSubmit ( S t r ing s ){ submit = s ; }

public St r ing [ ] get Items ( ) {St r ing [ ] s = new St r ing [ v . s i z e ( ) ] ;v . copyInto ( s ) ;return s ;

}

private void r e s e t ( ) {submit = null ;item = null ;

}

public void processRequest ( HttpServ letRequest r eque s t ){

i f ( submit == null ) return ;

i f ( submit . equa l s ( " ad i c i one " ) ) addItem ( item ) ;else i f ( submit . equa l s ( "remova" ) ) removeItem ( item ) ;r e s e t ( ) ;

}}

Exemplo 7.19 Implementação de um carrinho de compras Virtual.

O exemplo 7.19 implementa apenas o carrinho de compras, dei-xando de fora o pagamento dos itens, uma vez que esta etapa dependede cada sistema. Geralmente o que é feito é direcionar o usuário paraoutra página onde ele digitará o número do cartão de crédito que serátransmitido por meio de uma conexão segura para o servidor. Existemoutras formas de pagamento, como boleto bancário e dinheiro virtual.O próprio carrinho de compras geralmente é mais complexo, uma vez

Tópicos Avançados 187

que os itens para compra devem ser obtidos dinamicamente de umbanco de dados. A �gura 7.22 mostra a tela resultante de algumasinterações com o carrinho de compras.

Figura 7.22 Carrinho de compras virtual.

7.12 Reencaminhando ou Redirecio-nando requisições

Existem algumas situações onde pode ser desejável transferir uma re-quisição para outra URL. Isto é feito com frequência em sistemas quecombinam o uso de Servlets juntamente com páginas JSP. No entanto,a transferência pode ser para qualquer recurso. Assim, podemos trans-ferir uma requisição de um Servlet para uma página JSP, HTML ouum outro Servlet. Da mesma forma uma página JSP pode transferiruma requisição para uma outra página JSP, HTML ou um Servlet.

Existem dois tipos de transferência de requisição: o redireciona-mento e o reencaminhamento. O redirecionamento é obtido usando ométodo sendRedirect() de uma instância HttpServletResponse, pas-sando como argumento a URL de destino. O exemplo 7.20 mostra ocódigo de um Servlet redirecionando para a página JSP do exemplo 7.8.

import javax . s e r v l e t . ∗ ;import javax . s e r v l e t . http . ∗ ;import java . i o . ∗ ;

188 Java na prática

public class Redi rec iona extends HttpServ l e t{

public void doGet ( HttpServ letRequest req ,HttpServletResponse r e s )

throws Serv le tExcept ion , IOException{

r e s . sendRedi rect ( " o la . j sp " ) ;}

}

Exemplo 7.20 Redirecionamento de requisição.

No caso de redirecionamento a requisição corrente é perdida euma nova requisição é feita para a URL de destino. Por isso nãose deve associar nenhum objeto à requisição, uma vez que o objetoHttpServletRequest corrente será perdido. O que ocorre na prática éque o servidor envia uma mensagem HTTP 302 de volta para o clienteinformando que o recurso foi transferido para outra URL e o clienteenvia uma nova requisição para a URL informada.

Já no caso de reencaminhamento a requisição é encaminhada di-retamente para a nova URL mantendo todos os objetos associadose evitando uma nova ida ao cliente. Portanto, o uso de reencami-nhamento é mais e�ciente do que o uso de redirecionamento. Oreencaminhamento é obtido usando o método forward() de umainstância RequestDispatcher, passando como argumento os objetosHttpServletRequest e HttpServletResponse para a URL de des-tino. Uma instância RequestDispatcher é obtida por meio do métodogetRequestDispatcher() de uma instância ServletContext, que é ob-tido, por sua vez, por meio do método getServletContext() do Ser-vlet. O exemplo 7.21 mostra o código de um Servlet reencaminhandoa requisição para a página JSP do exemplo 7.8.

import javax . s e r v l e t . ∗ ;import javax . s e r v l e t . http . ∗ ;

public class Reencaminha extends HttpServ l e t{

public void doGet ( HttpServ letRequest request ,HttpServletResponse re sponse )

{try{

Tópicos Avançados 189

getSe rv l e tContext ( ) .getRequestDispatcher ( "/ o la . j sp " ) .

forward ( request , r e sponse ) ;}catch ( Exception e ) {

System . out . p r i n t l n ( " S e r v l e t f a lhou : " ) ;e . pr intStackTrace ( ) ;

}}

}

Exemplo 7.21 Reencaminhamento de requisição.

7.13 Uma Arquitetura para comércioeletrônico

O projeto de uma solução para comércio eletrônico é uma tarefa com-plexa e deve atender diversos requisitos. Nesta seção mostraremos ummodelo de arquitetura básico para comércio eletrônico que pode seradaptado para soluções mais especí�cas. Este modelo implementa opadrão de projeto MVC (Modelo-Visão-Controle), procurando, destaforma, isolar os diferentes aspectos de um sistema de computação.

7.13.1 Tipos de aplicações na WEBPodemos enquadrar a maioria das aplicações na Web em um dos se-guintes tipos:

Business-to-consumer (B2C) - entre empresa e consumidor.Exemplo: uma pessoa compra um livro na Internet.

Business-to-business (B2B) - Troca de informações e serviços entreempresas. Exemplo: o sistema de estoque de uma empresa deautomóveis detecta que um item de estoque precisa ser reposto efaz o pedido diretamente ao sistema de produção do fornecedor deautopeças. Neste tipo de aplicação a linguagem XML possui umpapel muito importante, uma vez que existe a necessidade de umapadronização dos metadados3 para comunicação de conteúdo.

3Dados sobre dados. Por exemplo, em um banco de dados os dados são osvalores contidos em cada coluna e os metadados são os nomes das colunas etabelas.

190 Java na prática

User-to-data - acesso à bases de informação. Exemplo: um usuárioconsulta uma base de informação.

User-to-user - chat, e troca de informações entre usuários (KaZaA).

O exemplo que mostraremos é tipicamente um caso de User-to-data,(agenda eletrônica na Web) mas possui a mesma estrutura de um B2C.

7.13.2 Arquitetura MVC para a WebA �gura 7.23 contém um diagrama de blocos que mostra a participaçãode Servlets, JSP e JavaBeans na arquitetura proposta. A idéia é isolarcada aspecto do modelo MVC com a tecnologia mais adequada. Apágina JSP é ótima para fazer o papel da visão, uma vez que possuifacilidades para a inserção de componentes visuais e para a apresentaçãode informação. No entanto, é um pouco estranho usar uma página JSPpara receber e tratar uma requisição. Esta tarefa, que se enquadra noaspecto de controle do modelo MVC é mais adequada a um Servlet, umavez que, neste momento, componentes de apresentação são indesejáveis.Finalmente, é desejável que a modelagem do negócio �que isolada dosaspectos de interação. A proposta é que a modelagem do negócio �quecontida em classes de JavaBeans. Em aplicações mais so�sticadas amodelagem do negócio deve ser implementada por classes de EnterpriseJavaBeans (EJB), no entanto esta forma de implementação foge aoescopos deste livro. Cada componente participa da seguinte forma:

• Servlets - Atua como controlador, recebendo as requisições dosusuários. Após a realização das análises necessárias sobre a re-quisição, instanciam os JavaBeans e os armazenam no escopoadequado (ou não, caso os beans já tenham sido criados no es-copo) e encaminha a requisição para a página JSP.

• JavaBeans - Atuam como o modelo da solução, independentesda requisição e da forma de apresentação. Comunicam-se com acamada intermediária que encapsula a lógica de acesso aos dados.

• JSP - Atuam na camada de apresentação utilizando os JavaBeanspara obtenção dos dados a serem exibidos, isolando-se assim datarefa de acessar os dados. O objetivo é minimizar a quantidadede código colocado na página.

• Camada Intermediária (Middleware) - Incorpora a lógica deacesso aos dados. Permite isolar os outros módulos de proble-mas como estratégias de acesso aos dados e desempenho. O uso

Tópicos Avançados 191

de EJB (Enterprise JavaBeans) é recomendado para a implemen-tação do Middleware, uma vez que os EJBs possuem capacidadespara gerência de transações e persistência. Isto implica na adoçãode um servidor de aplicação habilitado para EJB.

A �gura 7.23 ilustra a interação entre os componentes. Essa arqui-tetura possui as seguintes vantagens:

Facilidade de manutenção: a distribuição lógica das funções entreos módulos do sistema isola o impacto das modi�cações.

Escalabilidade: as modi�cações necessária para acompanhar o au-mento da demanda de serviços (database pooling, clustering, etc)�cam concentradas na camada intermediária.

Minimização de Interferências entre Desenvolvedores: umavez que as tarefas �cam mais isoladas, menor será a interferênciaentre os diferentes tipos de desenvolvedores durante a criação daaplicação.

Figura 7.23 Arquitetura de uma aplicação para Comércio Eletrônico.

192 Java na prática

Figura 7.24 Arquitetura física de uma aplicação para ComércioEletrônico.

A �gura 7.24 mostra a arquitetura física de uma aplicação de comér-cio eletrônico. Demilitarized Zone (DMZ) é onde os servidores HTTPsão instalados. A DMZ é protegida da rede púbica por um �rewall,também chamado de �rewall de protocolo. O �rewall de protocolodeve ser con�gurado para permitir tráfego apenas através da porta 80.Um segundo �rewall, também chamado de �rewall de domínio separa aDMZ da rede interna. O �rewall de domínio deve ser con�gurado parapermitir comunicação apenas por meio das portas do servidor de apli-cação. Desta forma os dados corporativos �cam protegidos de ataquesexternos.

7.13.3 Agenda Web: Um Exemplo de uma apli-cação Web usando a arquitetura MVC

O exemplo a seguir mostra o desenvolvimento da agenda eletrônica parao funcionamento na Web. A arquitetura adotada é uma implementaçãodo modelo MVC. Apenas, para simpli�car a solução, a camada inter-mediária foi simpli�cada e é implementada por um JavaBean que tema função de gerenciar a conexão com o banco de dados. O sistema écomposto pelos arquivos mostrados na tabela 7.6.

Tópicos Avançados 193

Arquivo Descriçãoagenda.html Página inicial do site, contendo o formulário

para a entrada do login e senha para entrar norestante do site.

principal.jsp Página JSP contendo o formulário para en-trada de dados para inserção, remoção ou con-sulta de itens da agenda.

LoginBean.java JavaBean responsável por veri�car se o usuárioestá autorizado a acessar a agenda.

AgendaServlet.java Servlet responsável pelo tratamento de requisi-ções sobre alguma função da agenda (consulta,inserção e remoção).

AcaoBean.java JavaBean responsável pela execução da açãosolicitada pelo usuário.

ConnectionBean.java JavaBean responsável pelo acesso ao DB e con-trole das conexões.

Tabela 7.6 Arquivos do sistema e-agenda.

O banco de dados é composto por duas tabelas, uma para armazenaros usuários autorizados a usar a tabela e outra para armazenar os itensda agenda. A �gura 7.25 mostra o esquema conceitual do banco dedados e a �gura 7.26 mostra o comando para a criação das tabelas. Noteque existe um relacionamento entre a tabela USUARIO e a tabela PESSOA,mostrando que os dados pessoais sobre o usuário �cam armazenados naagenda.

Figura 7.25 Esquema conceitual do banco de dados para a agenda.

As tabelas do BD devem ser criadas de acordo com o seguinte script:

CREATE TABLE PESSOA ( ID INT PRIMARYKEY,NOME VARCHAR( 5 0 ) NOT NULL,TELEFONE VARCHAR( 50 ) ,ENDERECOVARCHAR( 80 ) ,EMAIL VARCHAR( 50 ) ,HP VARCHAR( 50 ) ,CELULAR VARCHAR( 20 ) ,DESCRICAO VARCHAR( 8 0 ) ) ;

194 Java na prática

CREATE TABLE USUARIO ( ID INT PRIMARYKEY,LOGIN VARCHAR( 2 0 ) NOT NULL,SENHA VARCHAR( 2 0 ) NOT NULL,CONSTRAINT FK_USU FOREIGN KEY ( ID)REFERENCES PESSOA(ID ) ) ;

Figura 7.26 Script para criação das tabelas.

Para se usar a agenda é necessário que exista pelo menos um usuáriocadastrado. Como no exemplo não vamos apresentar uma tela paracadastro de usuários será preciso cadastrá-los por meio de comandosSQL. Os comandos da �gura 7.27 mostram como cadastrar um usuário.

INSERT INTO PESSOA(ID ,NOME,TELEFONE,ENDERECO,EMAIL)VALUES(0 , ' Alc ione de Paiva O l i v e i r a ' , ' 3899−1769 ' ,'PH Ro l f s ' , ' a l c i onepa iva@ig . com . br ' ) ;

INSERT INTO USUARIO(ID ,LOGIN,SENHA)VALUES(0 , ' Alc ione ' , ' senha ' ) ;

Figura 7.27 Script para cadastrar um usuário.

O diagrama de colaboração, apresentado na �gura 7.28, mostra asinteração entre os componentes do sistema.

Figura 7.28 Interação entre os componentes do sistema.

Tópicos Avançados 195

Descreveremos agora cada componente da aplicação. O exem-plo 7.22 mostra o código HTML da página agenda.html. Esta é apágina inicial da aplicação. Ela contém o formulário para a entrada dologin e senha para entrar no restante do site.

1 <HTML>2 <HEAD>3 <TITLE>Agenda</TITLE>4 </HEAD>56 <BODY BGCOLOR="#FFFFFF">7 <P a l i g n=" cente r "><IMG sr c=" t i t . g i f " width="350"8 he ight="100" border="0"></P>9 <BR>

1011 <CENTER>12 <FORM method="POST" name="TesteSub"13 onsubmit=" return TestaVal ( ) "14 ac t i on="/agenda/agenda"><BR>15 Login :<INPUT s i z e="20" type=" text " name=" l o g i n ">16 <BR><BR>17 Senha:<INPUT s i z e="20" type="password"18 name="senha">19 <BR><BR><BR>20 <INPUT type="submit" name=" envia " value="Enviar ">21 <INPUT s i z e="3" type="Hidden" name=" co r r en t e "22 value="0">23 <BR>24 </FORM>25 </CENTER>26 <SCRIPT language=" JavaScr ipt ">27 <!−−28 f unc t i on TestaVal ( )29 {30 i f ( document . TesteSub . l o g i n . va lue == "" )31 {32 a l e r t ( "Campo Login nao Preenchido ! " )33 return fa l se34 }35 else i f ( document . TesteSub . senha . va lue == "" )36 {37 a l e r t ( "Campo Senha nao Preenchido ! " )38 return fa l se39 }40 else41 {

196 Java na prática

42 return true43 }44 }45 //−−></SCRIPT>46 </BODY></HTML>

Exemplo 7.22 agenda.html.

O formulário está de�nido nas linha 12 a 24. Na linha 14 o pa-râmetro action indica a URL que deve receber a requisição. A URLé virtual e sua associação com o Servlet AgendaServlet será de�nidano arquivo web.xml. Na linha 17 é de�nido um campo oculto (Hidden)como o nome de corrente e valor 0. Ele será usado pelo AgendaServletpara reconhecer a página de onde saiu a requisição. As linhas 26 a 45de�nem uma função em JavaScript que será usada para veri�car se ousuário digitou o nome e a senha antes de enviar a requisição ao usuário.O uso de JavaScript no lado cliente para criticar a entrada do usuárioé muito comum pois diminui a sobrecarga do servidor.

O exemplo 7.23 mostra código da página principal.jsp. Esta pá-gina contém o formulário para entrada de dados para inserção, remoçãoou consulta de itens da agenda. Na linha 4 a diretiva page de�ne queo servidor deve acompanhar a sessão do usuário e importa o pacoteagenda. Na linha 7 um objeto da classe agenda.LoginBean é recupe-rado da sessão por meio do método getAttribute(). Para recuperaro objeto é preciso passar para o método o nome que está associadoao objeto na sessão. De forma semelhante, na linha 9 um objeto daclasse agenda.AcaoBean é recuperado da requisição por meio do mé-todo getAttribute(). Este objeto é recuperado da requisição porquecada requisição possui uma ação diferente associada. Na linha 11 éveri�cado se objeto agenda.LoginBean foi recuperado e se o retorno dométodo getStatus() é true. Se o objeto agenda.LoginBean não foirecuperado signi�ca que existe uma tentativa de acesso direto à páginaprincipal.jsp sem passar primeiro pela página agenda.html ou quea sessão se esgotou. Se o método getStatus() retornar false signi�caque o usuário não está autorizado a acessar essa página. Nestes casos éprocessado o código associado ao comando else da linha 43 que apagaa sessão por meio do método invalidate() do objeto HttpSession(linha 45) e mostra a mensagem �Usuário não autorizado� (linha 47).Caso o objeto indique que o usuário está autorizado os comandos inter-nos ao if são executados. Na linha 13 é mostrada uma mensagem com

Tópicos Avançados 197

o nome do usuário, obtido por meio do método getNome() do objetoagenda.LoginBean. Na linha 15 é mostrado o resultado da ação anteriorpor meio do método toString() do objeto agenda.AcaoBean. A açãopode ter sido de consulta, de inserção de um novo item na agenda, oude remoção de um item na agenda. No primeiro caso é mostrado umalista dos itens que satis�zeram a consulta. No segundo e terceiro casosé exibida uma mensagem indicando se a operação foi bem sucedida.

1 <HTML><HEAD>2 <TITLE>Tela da Agenda </TITLE>3 </HEAD><BODY bgco lo r="#FFFFFF">4 <%@ page s e s s i o n=" true " import="agenda .∗ " %>56 <%7 agenda . LoginBean lb=(agenda . LoginBean )8 s e s s i o n . ge tAt t r ibute ( " log inbean " ) ;9 agenda . AcaoBean ab = ( agenda . AcaoBean )

10 r eques t . g e tAt t r ibute ( "acaobean" ) ;11 i f ( lb != null && lb . ge tStatus ( ) )12 { %>13 <H2>Sess&a t i l d e ; o do <%= lb . getNome() %></H2>14 <%15 i f ( ab!=null ) out . p r i n t l n ( ab . t oS t r i ng ( ) ) ;16 %>1718 <P><BR></P>19 <FORM method="POST" name=" formprin "20 onsubmit=" return TestaVal ( ) " ac t i on="/agenda/agenda">21 Nome: <INPUT s i z e="50" type=" text " name="nome"><BR>22 Tele fone : <INPUT s i z e="20" type=" text " name=" t e l e f o n e ">23 <BR>24 Endere&c c e d i l ; o : <INPUT s i z e="50" type=" text "25 name=" endereco "><BR>26 Email : <INPUT s i z e="50" type=" text " name=" emai l "><BR>27 <BR>28 P&aacute ; g ina : <INPUT s i z e="50" type=" text "29 name="pagina "><BR>30 Celu la r : <INPUT s i z e="20" type=" text " name=" c e l u l a r ">31 <BR>32 Descr i&c c e d i l ;& a t i l d e ; o : <INPUT s i z e="20" type=" text "33 name=" de s c r i c a o ">34 <BR><CENTER>35 <INPUT type="submit" name="acao" value="Consulta ">36 <INPUT type="submit" name="acao" value=" In s e r e ">37 <INPUT type="submit" name="acao" value="Apaga"></CENTER>38 <INPUT s i z e="3" type="Hidden" name=" co r r en t e " value="1">39 </FORM>4041 <%42 }43 else44 {45 s e s s i o n . i n v a l i d a t e ( ) ;

198 Java na prática

46 %>47 <H1>Usu&aacute ; r i o n&a t i l d e ; o autor izado </H1>48 <%49 }50 %>5152 <SCRIPT language=" JavaScr ipt "><!−−53 f unc t i on TestaVal ( )54 {55 i f ( document . formprin . nome . value == "" &&56 document . formprin . d e s c r i c a o . va lue== "" )57 {58 a l e r t ( "Nome ou Descr i cao devem se r Preenchidos ! " )59 return fa l se60 }61 else62 {63 return true64 }65 }66 //−−></SCRIPT>6768 </BODY></HTML>

Exemplo 7.23 principal.jsp.

As linhas 19 a 39 de�nem o código do formulário de entrada. Naslinhas 19 e 20 são de�nidos os atributos do formulário. O atributomethod indica que a requisição será enviada por meio do método POST.O atributo name de�ne o nome do formulário como sendo formprin. Oatributo onsubmit de�ne que a função JavaScript TestaVal() deveser executada quando o formulário for submetido. Finalmente, o atri-buto action de�ne a URL para onde a requisição deve ser enviada.Neste caso a URL é agenda/agenda que está mapeada para o ServletAgendaServlet. O mapeamento é feito no arquivo web.xml do dire-tório web-inf do contexto agenda, como mostrado na �gura 7.30. Aslinhas 21 a 33 de�nem os campos de texto para entrada dos valores. Aslinhas 35 a 38 de�nem os botões tipo submit. Todos possuem o mesmonome, de forma que o Servlet precisa apenas examinar o valor do pa-râmetro acao para determinar qual ação foi solicitada. Na linha 38 éde�nido um campo oculto (Hidden) com o nome de corrente e valor 1.Ele será usado pelo AgendaServlet reconhecer a página de onde saiua requisição. As linha 52 a 66 de�nem uma função em JavaScript queserá usada para veri�car se o usuário entrou com valores nos camposde texto nome ou descricao. No mínimo um desses campos deve serpreenchido para que uma consulta possa ser realizada.

Tópicos Avançados 199

O exemplo 7.24 mostra o código do JavaBean usado para interme-diar a conexão com o banco de dados. O JavaBean ConnectionBeantem a responsabilidade de abrir uma conexão com o banco de dados,retornar uma referência desta conexão quando solicitado e registrar sea conexão está livre ou ocupada. Neste exemplo o ConnectionBean seencarrega apenas de obter a conexão e fechá-la, no entanto, em umaaplicação, com um número maior de acessos ao banco de dados, podeser necessário um maior controle sobre o uso das conexões, mantendo-as em uma estrutura de dados para fazer o papel de pool de conexões.Na linha 13 podemos observar que o construtor da classe foi declaradocom o modi�cador de acesso private. Isto signi�ca que não é possívelinvocar o construtor por meio de um objeto de outra classe. Isto éfeito para que se possa ter um controle sobre a criação de instâncias daclasse. No nosso caso permitiremos apenas que uma instância da classeseja criada, de modo que todas as referências apontem para um mesmoobjeto. Esta técnica de programação, onde se permite uma única ins-tância de uma classe, é denominada de padrão de projeto Singleton. Oobjetivo de utilizarmos este padrão é porque desejamos que apenas umobjeto controle a conexão com o banco de dados. Mas se o construtornão pode ser chamado fora da classe, então como uma instância daclasse é criada e sua referência é passada para outros objetos? Estaé a tarefa do método estático getInstance() (linhas 15 a 23). Estemétodo veri�ca se já existe a instância e retorna a referência. Caso ainstância não exista ela é criada antes de se retornar a referência. Ométodo init() (linhas 25 a 35) é chamado pelo construtor para esta-belecer a conexão com o SGBD. Ele carrega o driver JDBC do tipo 4para HSQLDB (veja a seção 4.3) e obtém uma conexão com o SGBD.O método devolveConnection() (linhas 37 a 45) é chamado quandose deseja devolver a conexão. Finalmente, o método getConnection()(linhas 57 a 57) é chamado quando se deseja obter a conexão.

1 package agenda ;2

3 import java . s q l . ∗ ;4 import java . lang . ∗ ;5 import java . u t i l . ∗ ;6

7 public class ConnectionBean8 {9 private Connection con=null ;

10 private stat ic int c l i e n t s =0;

200 Java na prática

11 stat ic private ConnectionBean in s t anc e=null ;12

13 private ConnectionBean ( ) { i n i t ( ) ; }14

15 stat ic synchronized16 public ConnectionBean ge t In s tance ( )17 {18 i f ( i n s t ance == null )19 {20 i n s t ance = new ConnectionBean ( ) ;21 }22 return i n s t anc e ;23 }24

25 private void i n i t ( )26 {27 try28 {29 Class . forName ( " org . hsqldb . jdbcDr iver " ) ;30 con= DriverManager . getConnect ion (31 " jdbc : hsqldb : hsq l : // l o c a l h o s t " , " sa " , "" ) ;32 } catch ( Exception e ){33 System . out . p r i n t l n ( e . getMessage ( ) ) ;34 }35 }36

37 public synchronized38 void devolveConnect ion ( Connection con )39 {40 i f ( this . con==con )41 {42 c l i e n t s −−;43 no t i f y ( ) ;44 }45 }46

47 public synchronized Connection getConnect ion ( )48 {49 i f ( c l i e n t s >0)50 {51 try { wait ( 5000 ) ; }

Tópicos Avançados 201

52 catch ( Inter ruptedExcept ion e ) { } ;53 i f ( c l i e n t s >0) return null ;54 }55 c l i e n t s ++;56 return con ;57 }58 }

Exemplo 7.24 ConnectionBean.java.

O exemplo 7.25 mostra código do JavaBean usado para veri�carse o usuário está autorizado a usar a agenda. O JavaBean LoginBeanrecebe o nome e a senha do usuário, obtém a conexão com o SGBD everi�ca se o usuário está autorizado, registrando o resultado da consultana variável status (linha 11). Tudo isso é feito no construtor da classe(linhas 13 a 40). Note que na construção do comando SQL (linhas 18a 21) é inserido uma junção entre as tabelas PESSOA e USUARIO, demodo a ser possível recuperar os dados relacionados armazenados emambas as tabelas. Os métodos getLogin(), getNome() e getStatus()(linhas 42 a 44) são responsáveis pelo retorno do login, nome e statusda consulta respectivamente.

1 package agenda ;23 import java . s q l . ∗ ;4 import java . lang . ∗ ;5 import java . u t i l . ∗ ;67 public c lass LoginBean8 {9 protected St r ing nome = null ;

10 protected St r ing l o g i n= null ;11 protected boolean s t a tu s= fa l se ;1213 public LoginBean ( St r ing log in , S t r ing senha )14 {15 this . l o g i n =l o g i n ;16 Connection con =null ;17 Statement stmt =null ;18 St r ing consu l ta="SELECT NOME FROM PESSOA, USUARIO "+19 "WHERE USUARIO. ID = PESSOA. ID AND "+20 "USUARIO.SENHA = '"+senha+" ' AND "+21 "USUARIO.LOGIN = '"+l og i n+" ' " ;22 try23 {24 con=ConnectionBean . ge t In s tance ( ) . getConnect ion ( ) ;

202 Java na prática

25 stmt = con . createStatement ( ) ;26 Resul tSet r s =stmt . executeQuery ( consu l ta ) ;27 i f ( r s . next ( ) )28 {29 s t a tu s = true ;30 nome = rs . g e tS t r i ng ( "NOME" ) ;31 }32 } catch ( Exception e ){33 System . out . p r i n t l n ( e . getMessage ( ) ) ;34 }35 f ina l ly36 {37 ConnectionBean . ge t In s tance ( ) . devolveConnect ion ( con ) ;38 try{stmt . c l o s e ( ) ; } catch ( Exception ee ) { } ;39 }40 }4142 public St r ing getLogin ( ){ return l o g i n ; }43 public St r ing getNome (){ return nome ; }44 public boolean getStatus ( ){ return s t a tu s ; }45 }

Exemplo 7.25 LoginBean.java.

O exemplo 7.26 mostra código do Servlet que implementa a camadade controle do modelo MVC. O Servlet AgendaServlet recebe as requi-sições e, de acordo com os parâmetros, instancia os JavaBeans apro-priados e reencaminha as requisições para as páginas corretas. Tanto ométodo doGet() (linhas 9 a 13) quanto o método doPost()(linhas 14a 18) invocam o método performTask() (linhas 20 a 64) que realiza otratamento da requisição. Na linhas 25 a 28 do método performTask()é obtido o valor do parâmetro corrente que determina a página queoriginou a requisição. Se o valor for nulo é assumido o valor defaultzero. Na linha 32 é executado um comando switch sobre esse valor, demodo a desviar para o bloco de comandos adequado. O bloco que vaida linha 34 até a linha 45 trata a requisição originada em uma páginacom a identi�cação 0 (página agenda.html). Nas linhas 34 e 35 sãorecuperados o valor de login e senha digitados pelo usuário. Se algumdesses valores for nulo então a requisição deve ser reencaminhada paraa página de login (agenda.html) novamente (linha 37). Caso contrárioé instanciado um objeto LoginBean, inserido na sessão corrente e de�-nida a página principal.jsp como a página para o reencaminhamentoda requisição (linhas 40 a 43). Já o bloco que vai da linha 46 até alinha 56 trata a requisição originada em uma página com a identi�-cação 1 (página principal.jsp). Na linha 47 é recuperado o objeto

Tópicos Avançados 203

HttpSession corrente. O argumento false é utilizado para impedir acriação de um novo objeto HttpSession caso não exista um corrente. Seo valor do objeto for null, então a requisição deve ser reencaminhadapara a página de login (linha 49). Caso contrário é instanciado umobjeto AcaoBean, inserido na requisição corrente, e de�nida a páginaprincipal.jsp como a página para o reencaminhamento da requisição(linhas 52 a 54). Na linha 58 a requisição é reencaminhada para apágina de�nida (página agenda.html ou principal.jsp).

1 package agenda ;23 import javax . s e r v l e t . ∗ ;4 import javax . s e r v l e t . http . ∗ ;5 import agenda . ∗ ;67 public class AgendaServlet extends HttpServ le t8 {9 public void doGet ( HttpServ letRequest req ,

10 HttpServletResponse resp )11 {12 performTask ( req , re sp ) ;13 }14 public void doPost ( HttpServ letRequest req ,15 HttpServletResponse resp )16 {17 performTask ( req , re sp ) ;18 }1920 public void performTask ( HttpServ letRequest req ,21 HttpServletResponse resp )22 {23 St r ing u r l ;24 HttpSess ion s e s s ao ;25 St r ing co r r en t e = req . getParameter ( " co r r en t e " ) ;26 int i c o r r =0;27 i f ( c o r r en t e != null )28 i c o r r = In t eg e r . pa r s e In t ( co r r en t e ) ;2930 try31 {32 switch ( i c o r r )33 {34 case 0 : S t r ing l o g i n = req . getParameter ( " l o g i n " ) ;35 St r ing senha = req . getParameter ( " senha" ) ;36 i f ( l o g i n == null | | senha == null )37 u r l = "/agenda . html" ;

204 Java na prática

38 else39 {40 s e s s ao = req . g e tS e s s i on ( true ) ;41 s e s s ao . s e tAt t r i bu t e ( " log inbean " ,42 new agenda . LoginBean ( log in , senha ) ) ;43 u r l = "/ p r i n c i p a l . j sp " ;44 }45 break ;46 case 1 :47 s e s s ao = req . g e tS e s s i on ( fa l se ) ;48 i f ( s e s s ao == null )49 u r l = "/agenda . html" ;50 else51 {52 req . s e tAt t r i bu t e ( "acaobean" ,53 new agenda . AcaoBean ( req ) ) ;54 u r l = "/ p r i n c i p a l . j sp " ;55 }56 break ;57 }58 getSe rv l e tContext ( ) . getRequestDispatcher ( u r l ) .59 forward ( req , re sp ) ;60 }catch ( Exception e ) {61 System . out . p r i n t l n ( "AgendaServlet f a lhou : " ) ;62 e . pr intStackTrace ( ) ;63 }64 }65 }

Exemplo 7.26 AgendaServlet.java.

O exemplo 7.27 mostra o código do JavaBean usado para realizara manutenção da agenda. O JavaBean AcaoBean é responsável pelaconsulta, remoção e inserção de novos itens na agenda. Um objetoStringBuffer referenciado pela variável ret é utilizado pelo JavaBeanpara montar o resultado da execução. O construtor (linhas 17 a 28)veri�ca o tipo de requisição e invoca o método apropriado.

O método consulta() (linhas 30 a 96) é responsável pela realiza-ção de consultas. As consultas podem ser realizadas sobre o camponome ou descrição e os casamentos podem ser parciais, uma vez queé usado o operador LIKE. A consulta SQL é montada nas linhas 42 a53. Na linha 56 é obtida uma conexão com SGBD por meio do objetoConnectionBean. Na linha 64 o comando SQL é executado e as linhas67 a 85 montam o resultado da consulta.

Tópicos Avançados 205

O método insere() (linhas 98 a 176) é responsável por inserir umitem na agenda. Na linha 119 é obtida uma conexão com SGBD pormeio do objeto ConnectionBean. Para inserir um novo item é precisoobter o número do último identi�cador usado, incrementar o identi�-cador e inserir na base o item com o identi�cador incrementado. Estaoperação requer que não seja acrescentado nenhum identi�cador entrea operação de leitura do último identi�cador e a inserção de um novoitem. Ou seja, é necessário que essas operações sejam tratadas comouma única transação e o isolamento entre as transações seja do nívelSerializable. A de�nição do início da transação é feita no comando dalinha 127. A mudança do nível de isolamento é feita pelos comandoscodi�cados nas linhas 130 a 134. Na linha 136 é invocado o métodoobtemUltimo() (linhas 178 a 201) para obter o último identi�cador uti-lizado. As linhas 139 a 153 montam o comando SQL para a execução.O comando SQL é executado na linha 155. O �m da transação é de�-nido pelo comando da linha 156. Ao �m da transação, de forma a nãoprejudicar a concorrência, o nível de isolamento deve retornar para umvalor mais baixo. Isto é feito pelos comandos das linhas 157 a 161.

O método apaga() (linhas 203 a 236) é responsável por remover umitem da agenda. As linhas 206 a 211 contém o código para veri�car seo usuário digitou o nome associado ao item que deve ser removido. Aslinhas 212 e 213 montam o comando SQL para a execução. Na linha 216é obtida uma conexão com SGBD por meio do objeto ConnectionBean.O comando SQL é executado na linha 224.

1 package agenda ;2 import javax . s e r v l e t . http . ∗ ;3 import java . lang . ∗ ;4 import java . u t i l . ∗ ;5 import java . s q l . ∗ ;67 public c lass AcaoBean8 {9 private Connection con=null ;

10 private St r i ngBu f f e r r e t = null ;11 private Statement stmt=null ;12 private St r ing [ ] l egenda=13 {"C&oacute ; d igo " , "Nome" , "Te le fone " ,14 "Endere&c c e d i l ; o" , " emai l " , "hp" ,15 " c e l u l a r " , "Descr i&c c e d i l ;& a t i l d e ; o" } ;1617 public AcaoBean ( HttpServ letRequest req )18 {19 St r ing acao = req . getParameter ( "acao" ) ;20 i f ( acao . equa l s ( "Consulta " ) )21 {22 St r ing nome = req . getParameter ( "nome" ) ;

206 Java na prática

23 St r ing d e s c r i = req . getParameter ( " d e s c r i c a o " ) ;24 consu l ta (nome , d e s c r i ) ;25 }26 else i f ( acao . equa l s ( " In s e r e " ) ) i n s e r e ( req ) ;27 else i f ( acao . equa l s ( "Apaga" ) ) apaga ( req ) ;28 }2930 private void consu l ta ( St r ing nome , St r ing d e s c r i )31 {32 St r ing consu l ta = null ;3334 i f ( ( nome == null | | nome . l ength ()<1) &&35 ( d e s c r i == null | | d e s c r i . l ength () <1))36 {37 r e t = new St r i ngBu f f e r (38 "Dig i t e o nome ou de s c r i c a o ! " ) ;39 return ;40 }4142 i f ( d e s c r i == null | | d e s c r i . l ength ()<1)43 consu l ta =44 "SELECT ∗ FROM PESSOA WHERE NOME LIKE '%"45 +nome+"%'"+" ORDER BY NOME" ;46 else i f ( nome == null | | nome . l ength ()<1)47 consu l ta =48 "SELECT ∗ FROM PESSOA WHERE DESCRICAO LIKE '%"49 +de s c r i+"%'"+" ORDER BY NOME" ;50 else consu l ta=51 "SELECT ∗ FROM PESSOA WHERE DESCRICAO LIKE '%"52 +de s c r i+53 "%' AND NOME LIKE '%"+nome+"%' ORDER BY NOME" ;54 try55 {56 con=ConnectionBean . ge t In s tance ( ) . getConnect ion ( ) ;57 i f ( con == null )58 {59 r e t = new St r i ngBu f f e r (60 " Serv idor ocupado . Tente mais tarde . ! " ) ;61 return ;62 }63 stmt = con . createStatement ( ) ;64 Resul tSet r s = stmt . executeQuery ( consu l ta ) ;6566 r e t = new St r i ngBu f f e r ( ) ;67 r e t . append ( "<br><h3>Resultado</h3><br>" ) ;68 while ( r s . next ( ) )69 {70 r e t . append ( "ID : " ) . append ( r s . g e tS t r i ng ( " id " ) ) ;71 r e t . append ( "<br>Nome : " ) . append (72 r s . g e tS t r i ng ( "Nome" ) ) ;73 r e t . append ( "<br>Tele fone : " ) . append (74 r s . g e tS t r i ng ( "Te le fone " ) ) ;75 r e t . append ( "<br>Endereco : " ) . append (76 r s . g e tS t r i ng ( "Endereco" ) ) ;77 r e t . append ( "<br>emai l : " ) . append (78 r s . g e tS t r i ng ( " emai l " ) ) ;

Tópicos Avançados 207

79 r e t . append ( "<br>hp : " ) . append (80 r s . g e tS t r i ng ( "hp" ) ) ;81 r e t . append ( "<br>c e l u l a r : " ) . append (82 r s . g e tS t r i ng ( " c e l u l a r " ) ) ;83 r e t . append ( "<br>de s c r i c a o : " ) . append (84 r s . g e tS t r i ng ( " de s c r i c a o " ) ) ;85 r e t . append ( "<br><br>" ) ;86 }87 }catch ( Exception e )88 {89 System . out . p r i n t l n ( e . getMessage ( ) ) ;90 }91 f ina l ly {92 ConnectionBean . ge t In s tance ( ) .93 devolveConnect ion ( con ) ;94 try{stmt . c l o s e ( ) ; } catch ( Exception ee ) { } ;95 }96 }9798 private void i n s e r e ( HttpServ letRequest req )99 {

100 St r ing [ ] par =101 {" t e l e f o n e " , " endereco " , " emai l " , "hp" ,102 " c e l u l a r " , " d e s c r i c a o " } ;103104 St r i ngBu f f e r comando =105 new St r i ngBu f f e r ( "INSERT INTO PESSOA(" ) ;106 St r i ngBu f f e r va lues =107 new St r i ngBu f f e r ( " VALUES( " ) ;108109 St r ing aux = req . getParameter ( "nome" ) ;110 i f ( aux == null | | aux . l ength ()<1)111 {112 r e t = new St r i ngBu f f e r (113 "<br><h3>Dig i t e o nome!</h3><br>" ) ;114 return ;115 }116117 try118 {119 con=ConnectionBean . ge t In s tance ( ) . getConnect ion ( ) ;120 i f ( con == null )121 {122 r e t = new St r i ngBu f f e r (123 " Serv idor ocupado . Tente mais tarde ! " ) ;124 return ;125 }126127 con . setAutoCommit ( fa l se ) ;128 DatabaseMetaData meta=con . getMetaData ( ) ;129130 i f (meta . suppo r t sTran sac t i on I s o l a t i onLeve l (131 con .TRANSACTION_SERIALIZABLE) ) {132 con . s e tT r an s a c t i o n I s o l a t i o n (133 con .TRANSACTION_SERIALIZABLE) ;134 }

208 Java na prática

135136 int u l t = obtemUltimo ( con ) ;137 i f ( u l t==−1) return ;138 u l t++;139 comando . append ( " id , nome" ) ;140 va lues . append ( u l t+" , ' " ) . append ( aux ) . append ( " ' " ) ;141142 for ( int i =0; i<par . l ength ; i++)143 {144 aux = req . getParameter ( par [ i ] ) ;145 i f ( aux != null && aux . l ength ()>0)146 {147 comando . append ( " , " ) . append ( par [ i ] ) ;148 va lues . append ( " , ' " ) . append ( aux ) . append ( " ' " ) ;149 }150 }151 comando . append ( " ) " ) ;152 va lues . append ( " ) " ) ;153 aux = comando . t oS t r i ng ()+ va lues . t oS t r i ng ( ) ;154 stmt = con . createStatement ( ) ;155 stmt . executeUpdate ( aux ) ;156 con . setAutoCommit ( true ) ;157 i f (meta . suppo r t sTran sac t i on I s o l a t i onLeve l (158 con .TRANSACTION_READ_COMMITTED) ) {159 con . s e tT r an s a c t i o n I s o l a t i o n (160 con .TRANSACTION_READ_COMMITTED) ;161 }162 r e t = new St r i ngBu f f e r (163 "<br><h3>In s e r i d o !</h3><br>" ) ;164 return ;165 }catch ( Exception e )166 {167 r e t = new St r i ngBu f f e r ( "<br><h3>Erro : "+168 e . getMessage ()+"!</h3><br>" ) ;169 }170 f ina l ly171 {172 ConnectionBean . ge t In s tance ( ) .173 devolveConnect ion ( con ) ;174 try{stmt . c l o s e ( ) ; } catch ( Exception ee ) { } ;175 }176 }177178 private int obtemUltimo ( Connection con )179 {180 St r ing consu l ta =181 "SELECT MAX(ID ) AS maior FROM PESSOA" ;182 try183 {184 i f ( con == null )185 {186 r e t = new St r i ngBu f f e r (187 " Serv idor ocupado . Tente mais tarde . ! " ) ;188 return −1;189 }190 stmt = con . createStatement ( ) ;

Tópicos Avançados 209

191 Resul tSet r s = stmt . executeQuery ( consu l ta ) ;192 i f ( r s . next ( ) )193 return I n t eg e r . pa r s e In t ( r s . g e tS t r i ng ( "maior" ) ) ;194 else return 0 ;195 } catch ( Exception e ) {196 r e t = new St r i ngBu f f e r ( "<br><h3>Erro : "+197 e . getMessage ()+"!</h3><br>" ) ;198 return −1;199 }200 f ina l ly {try{stmt . c l o s e ( ) ; } catch ( Exception ee ){} ; }201 }202203 private void apaga ( HttpServ letRequest req )204 {205 St r ing aux = req . getParameter ( "nome" ) ;206 i f ( aux == null | | aux . l ength ()<1)207 {208 r e t = new St r i ngBu f f e r (209 "<br><h3>Dig i t e o nome!</h3><br>" ) ;210 return ;211 }212 St r ing consu l ta = "DELETE FROM PESSOA WHERE NOME = '"213 +aux+" ' " ;214 try215 {216 con=ConnectionBean . ge t In s tance ( ) . getConnect ion ( ) ;217 i f ( con == null )218 {219 r e t = new St r i ngBu f f e r (220 " Serv idor ocupado . Tente mais tarde . ! " ) ;221 return ;222 }223 stmt = con . createStatement ( ) ;224 stmt . executeUpdate ( consu l ta ) ;225226 r e t=new St r i ngBu f f e r ( "<br><h3>Removido!</h3><br>" ) ;227 } catch ( Exception e ){228 r e t = new St r i ngBu f f e r ( "<br><h3>Erro : "+229 e . getMessage ()+"!</h3><br>" ) ;230 }231 f ina l ly {232 ConnectionBean . ge t In s tance ( ) .233 devolveConnect ion ( con ) ;234 try{stmt . c l o s e ( ) ; } catch ( Exception ee ) { } ;235 }236 }237238 public St r ing [ ] getLeg ( ){ return l egenda ; }239 public St r ing toS t r i ng ( ){ return r e t . t oS t r i ng ( ) ; }240 }

Exemplo 7.27 AcaoBean.java.

210 Java na prática

Instalação da AplicaçãoPara instalar crie a seguinte estrutura de diretório abaixo do diretóriowebapps do Tomcat:

Figura 7.29 Estrutura de diretórios para a aplicação agenda.

O arquivo web.xml deve ser alterado para conter mapeamento entrea URL agenda e o Servlet AgendaServlet.

. . .<web−app>

<s e r v l e t><s e r v l e t−name>

agenda</ s e r v l e t−name><se r v l e t−c l a s s>

agenda . AgendaServlet</ s e r v l e t−c l a s s>

</ s e r v l e t>

<s e r v l e t−mapping><se r v l e t−name>

agenda</ s e r v l e t−name><url−pattern>

/agenda</ur l−pattern>

</ s e r v l e t−mapping>. . .</web−app>

Figura 7.30 Arquivo web.xml para a agenda.

Tópicos Avançados 211

Considerações sobre a soluçãoA aplicação descrita implementa uma agenda que pode ser acessada pormeio da Internet, no entanto, devido à falta de espaço e à necessidadede destacarmos os pontos principais, alguns detalhes foram deixados delado, como por exemplo, uma melhor interface com o usuário. Abaixoseguem alguns comentários sobre algumas particularidades da aplica-ção:

1. O JavaBean da classe LoginBean é armazenado na sessão parapermitir a veri�cação se o acesso ao site é autorizado. Istoimpede que os usuários tentem acessar diretamente a páginaprincipal.jsp da agenda. Caso tentem fazer isso, a sessão nãoconterá um objeto LoginBean associado e, portanto, o acesso serárecusado.

2. O JavaBean da classe AcaoBean é armazenado no objeto req umavez que suas informações são alteradas a cada requisição. Umaforma mais e�ciente seria manter o objeto AcaoBean na sessão ecada nova requisição invocar um método do AcaoBean para geraros resultados. No entanto, o objetivo da nossa implementaçãonão é fazer a aplicação mais e�ciente possível, e sim mostrarpara o leitor uma aplicação com variadas técnicas.

3. Apesar de termos adotado o padrão MVC de desenvolvimento aaplicação não exibe uma separação total da camada de apresen-tação (Visão) em relação à camada do modelo. Parte do códigoHTML da visão é inserido pelo AcaoBean no momento da cons-trução da String contendo o resultado da ação. Isto foi feito paraminimizar a quantidade de código Java na página JSP. Pode-se argumentar que neste caso a promessa da separação entre ascamadas não é cumprida totalmente. Uma solução para o pro-blema seria gerar o conteúdo em XML e utilizar um analisadorde XML para gerar a página de apresentação. No entanto, o usoda tecnologia XML foge ao escopo deste livro.

4. A solução apresenta código redundante para criticar as entradasdo usuário. Existe código JavaScript nas páginas, e código Javano Servlet e JavaBeans. O uso de código JavaScript nas páginaspara críticas de entrada é indispensável para aliviarmos a cargasobre o servidor. Já o código para crítica no servidor não causaimpacto perceptível e é útil para evitar tentativas de violação.

Índice

B2B, 189B2C, 189BLOB, 77bytecode, 2

carrinho de compras, 184CGI, 72coleta de lixo, 4Container, 138convenções, 6CORBA, 111, 119

Daemon Threads, 23DMZ, 192

FTP, 69

green-threads, 25

HSQLDB, 79HTTP, 69

Internet, 138IP: número, 58

java.rmi.Remote, 113java.rmi.RemoteException,

113JDBC, 75JDBC-ODBC, 107JNDI, 115join, 22

JSP, 137, 164

localhost, 62lookup, 117

Metadados, 89MIME, 143monitor, 33MVC, 190

nível de isolamento, 94Naming, 116notify, 39

ODBC, 77, 107ORB, 119

ponteiros, 3pool de conexões, 101preemptivo, 25prepared statements, 97procedimentos armazenados,

98

Race Conditions, 32rebind, 116redes, 55redirecionando requisições, 187reencaminhando requisições,

187Regiões Críticas, 32registry, 115

212

Tópicos Avançados 213

resume, 23RMI, 111rmic, 115Round-Robin, 26Runnable, 13

Scriptlets, 170Seções Críticas, 32semáforo, 50Servlets, 137SGBD, 75sleep, 19socket, 61SQL, 75stop, 23stored procedures, 98suspend, 23synchronized, 34

TCP, 57Thread, 7ThreadGroup, 44Tomcat, 143transação, 92

UDP, 58, 66UnicastRemoteObject, 114URI, 70URL, 69, 85

wait, 39

yield, 21

214 Java na prática

Bibliogra�a

[1] Eckel B. Thinking in Java. 3rd Ed. New Jersey: Prentice Hall,2003.

[2] Elmasri R. e Navathe S. B. Fundamentals of Database Systems.3rd Ed. Vancouver: Addison Wesley, 2000.

[3] Gosling J., Joy W., Steele G. The Java Language Speci�cation.Massachusetts : Addison-Wesley, 1996.

[4] Elmasri R., Navathe S. Fundamentals of Database Systems. 3rdEd. Massachusetts : Addison-Wesley, 2000.

[5] Oaks S., Wong H. Java Threads. 2nd Ed. California : O'Reilly andAssociates, Inc, 1999.

[6] Sadtler C. et al. Patterns for e-business: User-to-Business Pat-terns for Topology 1 and 2 using WebSphere Advanced Edition.IBM RedBooks, California, April 2000.

[7] Silberschatz A., Forth H., Sudarshan S. Database Systems Con-cepts. McGraw-Hill, 1997.

[8] Wahli U. et al. Servlet and JSP Programming with IBM WebSphereStudio and VisualAge for Java, IBM RedBooks, California, May2000.

[9] Watt D. A. Programming Language Concepts and Paradigms.Great Britain : Prentice Hall, 1990.

215