java na pratica vol2

177
UNIVERSIDADE FEDERAL DE VIÇOSA DEPARTAMENTO DE INFORMÁTICA JAVA NA PRÁTICA Volume II Alcione de Paiva Oliveira Vinícius Valente Maciel 2002

Upload: manoel-guilherme

Post on 01-Jul-2015

7.312 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Java na Pratica vol2

UNIVERSIDADE FEDERAL DE VIÇOSA

DEPARTAMENTO DE INFORMÁTICA

JAVA NA PRÁTICAVolume II

Alcione de Paiva OliveiraVinícius Valente Maciel

2002

Page 2: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

1

SumárioCapítulo I - Concorrência ....................................... 3

CRIANDO THREADS EM JAVA ..................................................................................................... 5Criando threads por meio da interface Runnable ............................................................ 8

A CLASSE THREAD..................................................................................................................... 9Hierarquia............................................................................................................................ 9Construtores ......................................................................................................................... 9Métodos .............................................................................................................................. 10Variáveis públicas .............................................................................................................. 11

CICLO DE VIDA DOS THREADS ................................................................................................. 12sleep(), yield(), join(), destroy(), stop(), suspend() e resume(). ................ 13

DAEMON THREADS .................................................................................................................. 17INFLUÊNCIA DO SISTEMA OPERACIONAL NO COMPORTAMENTO DOS THREADS....................... 18

Forma de escalonamento de threads.................................................................................. 19Relacionamento entre os níveis de prioridades definidas na linguagem Java e os níveis deprioridades definidas nos Sistemas Operacionais.............................................................. 20

COMPARTILHAMENTO DE MEMÓRIA E SINCRONIZAÇÃO .......................................................... 22Atomicidade de Instruções e Sincronização do Acesso à Sessões Críticas ........................ 25Comunicação entre Threads: wait() e notify() ................................................................... 31

Capítulo II - Animação ......................................... 46Capítulo III - Programação em rede ........................... 50

CONCEITOS SOBRE PROTOCOLOS USADOS NA INTERNET......................................................... 50TCP..................................................................................................................................... 52UDP.................................................................................................................................... 52IDENTIFICAÇÃO DE HOSTS (Número IP)...................................................................... 53Identificação de Processos (Portas)................................................................................... 54

PROGRAMAÇÃO EM REDE COM JAVA....................................................................................... 54Comunicação Básica Entre Aplicações.............................................................................. 55Comunicação Sem Conexão (UDP) ................................................................................... 60Comunicação por meio de URL ......................................................................................... 63

Capítulo IV – Computação Distribuída (RMI) ................... 69CRIANDO NOSSA AGENDA DISTRIBUÍDA ................................................................................... 69

Implementar interface do objeto remoto ............................................................................ 69Capítulo V - Acesso a Banco de Dados ......................... 71

MODELOS DE ACESSO A SERVIDORES ...................................................................................... 71TIPOS DE DRIVERS JDBC......................................................................................................... 72

Obtendo os Drivers JDBC.................................................................................................. 74PREPARANDO UM BANCO DE DADOS ....................................................................................... 74

Configurando o ODBC....................................................................................................... 77EXEMPLO INICIAL .................................................................................................................... 78

Carregando o Driver.......................................................................................................... 79Estabelecendo a conexão ................................................................................................... 79Criando e Executando Comandos ...................................................................................... 81

RECUPERANDO VALORES......................................................................................................... 82TRANSAÇÕES E NÍVEL DE ISOLAMENTO................................................................................... 83

Transação........................................................................................................................... 83

Page 3: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

2Níveis de isolamento........................................................................................................... 85

PREPARED STATEMENTS .......................................................................................................... 87PROCEDIMENTOS ARMAZENADOS (STORED PROCEDURES)...................................................... 88AGENDA ELETRÔNICA VERSÃO JDBC ..................................................................................... 90

Capítulo VI Servlets e JSP ................................... 97SERVLETS ................................................................................................................................ 97

Applets X Servlets ............................................................................................................... 98CGI X Servlets .................................................................................................................... 99

A API SERVLET....................................................................................................................... 99Exemplo de Servlet ........................................................................................................... 101

COMPILANDO O SERVLET....................................................................................................... 102Instalando o Tomcat......................................................................................................... 103

PREPARANDO PARA EXECUTAR O SERVLET............................................................................ 106Compilando o Servlet ....................................................................................................... 106Criando uma aplicação no Tomcat .................................................................................. 107

EXECUTANDO O SERVLET ...................................................................................................... 108Invocando diretamente pelo Navegador........................................................................... 108Invocando em uma página HTML.................................................................................... 109Diferenças entre as requisições GET e POST.................................................................. 109

CONCORRÊNCIA..................................................................................................................... 110OBTENDO INFORMAÇÕES SOBRE A REQUISIÇÃO .................................................................... 112LIDANDO COM FORMULÁRIOS................................................................................................ 114LIDANDO COM COOKIES......................................................................................................... 115LIDANDO COM SESSÕES ......................................................................................................... 118JSP......................................................................................................................................... 122

PHP X JSP ....................................................................................................................... 123ASP X JSP ........................................................................................................................ 124Primeiro exemplo em JSP ................................................................................................ 124Executando o arquivo JSP................................................................................................ 125Objetos implícitos............................................................................................................. 126Tags JSP........................................................................................................................... 127Comentários ..................................................................................................................... 130Diretivas ........................................................................................................................... 131Extraindo Valores de Formulários................................................................................... 133Criando e Modificando Cookies....................................................................................... 134Lidando com sessões ........................................................................................................ 136O Uso de JavaBeans......................................................................................................... 138

REENCAMINHANDO OU REDIRECIONANDO REQUISIÇÕES ....................................................... 146UMA ARQUITETURA PARA COMÉRCIO ELETRÔNICO ............................................................... 148

Tipos de aplicações na WEB ............................................................................................ 148Arquitetura MVC para a Web .......................................................................................... 148Agenda Web: Um Exemplo de uma aplicação Web usando a arquitetura MVC.............. 151

Capítulo VII Perguntas Frequentes ........................... 171Bibliografia ................................................ 172Links ....................................................... 173Índice ...................................................... 175

Page 4: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

3

Capítulo I - ConcorrênciaUm sistema operacional é dito concorrente se permite que mais que uma

tarefa seja executada ao mesmo tempo. Na prática a concorrência real ouparalelismo só é possível se o hardware subjacente possui mais de umprocessador. No entanto, mesmo em computadores com apenas um processadoré possível obter um certo tipo de concorrência fazendo com o processadorcentral execute um 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 programa emexecução é chamada de processo. Um processo ocupa um espaço em memóriaprincipal para o código e para as variáveis transientes (variáveis que sãoeliminadas ao término do processo). Cada processo possui pelo menos uma linhade execução (Thread). Para ilustrarmos o que é uma linha de execução suponhaum determinado programa prog1. Ao ser posto em execução é criado umprocesso, digamos A, com uma área de código e uma área de dados e é iniciadaa execução do processo a partir do ponto de entrada. A instrução inicial assimcomo as instruções subsequentes formam uma linha de execução do processo A.Portanto, um thread nada mais é que uma sequência de instruções que está emexecução de acordo com que foi determinado pelo programa. O estado correnteda linha de execução é representada pela instrução que está sendo executada. Afigura IX.1 mostra a relação entre estes elementos.

arquivo prog1 Memória Principal

Figura IX.1 – Relação entre Programa, Processo e Thread.

É possível existir mais de uma linha de execução em um único processo.Cada linha de execução pode também ser vista como um processo, com a

101001101010110010010101100100011101

100101010101001010

Área de dados

Área de códigoLinha de execução(thread)

Processo

101001101010110010010101100100011101

Page 5: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

4

diferença que enquanto cada processo possui sua área de código e dadosseparada de outros processos, os threads em um mesmo processo compartilhamo código e a área de dados. O que distingue um thread de outro em um mesmoprocesso é a instrução corrente e uma área de pilha usada para armazenar ocontexto da sequência de chamadas de cada thread. Por isso os threads tambémsão chamados de processos leves (light process). A figura IX.2 mostraesquematicamente a diferença entre processos e threads.

Memória Processo

Figura IX.2 – (a) Processos; (b) Threads.

Sistemas monotarefas e monothreads como o DOS possuem apenas umprocesso em execução em um determinado instante e apenas um thread noprocesso. Sistemas multitarefas e monothreads como o Windows 3.1 permitemvários processos em execução e apenas um thread por processo. Sistemasmultitarefas e multithread como o Solaris, OS/2, Linux e Windows 95/98/NTpermitem vários processos em execução e vários threads por processo.

Como os threads em um mesmo processo possuem uma área de dados emcomum, surge a necessidade de controlar o acesso a essa área de dados, de modoque thread não leia ou altere dados no momento que estão sendo alterados poroutro thread. A inclusão de instruções para controlar o acesso a áreascompartilhadas torna o código mais complexo do que o código de processosmonothreads.

Uma pergunta pode surgir na mente do leitor: se a inclusão de mais deum thread torna o código mais complexo porque razão eu deveria projetarcódigo multithread. Processos com vários threads podem realizar mais de uma

10100010011100101010

10100111

10100010011100101010

10100111

10100010011100101010

10100111

AB

C

101000100111001010101010101010000101101101010001010101010110110010101010100101010101001010101010010000001010101010101

1010011100101010101010101010101010101010100011110101010

Thread 1

Thread 2Código

DadosÁrea de pilha do thread1

Área de pilhado thread2

Page 6: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

5

tarefa simultaneamente. São úteis na criação de processos servidores, criação deanimações e no projeto de interfaces com o usuário que não ficam travadasdurante a execução de alguma função. Por exemplo, imagine um processoservidor a espera de requisições de serviços, podemos projetá-lo de modo que aosurgir uma solicitação de um serviço por um processo cliente ele crie um threadpara atender a solicitação enquanto volta a esperar a requisição de novosserviços. Com isto os processos clientes não precisam esperar o término doatendimento de alguma solicitação para ter sua requisição atendida.

O mesmo pode ser dito em relação ao projeto de interfaces com ousuário. O processo pode criar threads para executar as funções solicitadas pelousuário, enquanto aguarda novas interações. Caso contrário, a interface ficariaimpedida de receber novas solicitações enquanto processa a solicitação corrente,o que poderia causar uma sensação de travamento ao usuário.

Outra aplicação para processos multithread é a animação de interfaces.Nesse caso cria-se um ou mais threads para gerenciar as animações enquantooutros threads cuidam das outras tarefas como por exemplo entrada de dados.

A rigor todas as aplicações acima como outras aplicações de processosmultithread podem ser executados por meio de processos monothreads. Noentanto, o tempo gasto na mudança de contexto entre processos na maioria dossistemas operacionais é muito mais lenta que a simples alternância entre threads,uma vez que a maior parte das informações contextuais são compartilhadas pelosthreads de um mesmo processo.

Mudança de Contexto (task switch)É o conjunto de operações necessárias para gravar o estado atual do processo corrente e

recuperar o estado de outro processo de modo a torná-lo o processo corrente.

Mesmo que você não crie mais de um thread todo processo Java possuivários threads: thread para garbage collection, thread para monitoramento deeventos, thread para carga de imagens, etc.

Criando threads em Java

Processos Multithread não é uma invenção da linguagem Java. É possívelcriar processos multithread com quase todas as linguagens do mercado, comoC++, e Object Pascal. No entanto Java incorporou threads ao núcleo básico dalinguagem tornado desta forma mais natural o seu uso. Na verdade o uso de

Page 7: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

6

threads está tão intimamente ligado a Java que é quase impossível escrever umprograma útil que não seja multithread.

A classe Thread agrupa os recursos necessários para a criação de umthread. A forma mais simples de se criar um thread é criar uma classe derivadada classe Thread. Por exemplo:

class MeuThread extends Thread {...}

É preciso também sobrescrever o método run() da classe Thread. Ométodo run() é o ponto de entrada do thread, da mesma forma que o métodomain() é ponto de entrada de uma aplicação. O exemplo IX.1 mostra umaclasse completa.

public class MeuThread extends Thread { String s; public MeuThread (String as) { super(); s = new String(as); } public void run() { for (int i = 0; i < 5; i++) System.out.println(i+” “+s); System.out.println("FIM! "+s); }}

Exemplo IX.1 – Subclasse da classe Thread.

No exemplo IX.1foi inserido um atributo para identificar o thread, apesarde existir formas melhores de se nomear um thread como veremos mais adiante.O método run() contém o código que será executado pelo thread. No exemploIX.1 o thread imprime cinco vezes o atributo String. Para iniciar a execução deum thread cria-se um objeto da classe e invoca-se o método start() doobjeto. O método start() cria o thread e inicia sua execução pelo métodorun(). Se o método run() for chamado diretamente nenhum thread novo serácriado e o método run() será executado no thread corrente. O exemplo IX.2mostra uma forma de se criar um thread usando a classe definida no exemploIX.1.

Page 8: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

7public class TesteThread1 { public static void main (String[] args) { new MeuThread("Linha1").start(); }}

Exemplo IX.2 – Criação de um Thread.

No exemplo acima apenas um thread, além do principal é criado. Nadaimpede que sejam criados mais objetos da mesma classe para disparar umnúmero maior de threads. O exemplo IX.3 mostra a execução de dois threadssobre dois objetos de uma mesma classe.

public class TesteThread2 { public static void main (String[] args) { new MeuThread("Linha1").start(); new MeuThread("Linha2").start(); }}

Exemplo IX.3 – Criação de dois Threads.

Cada thread é executado sobre uma instância da classe e, porconsequência, sobre uma instância do método run(). A saída gerada pelaexecução do exemplo IX.3 depende do sistema operacional subjacente. Umasaída possível é a seguinte:

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

A saída acima mostra que os threads executam intercaladamente. Noentanto, em alguns sistemas operacionais os threads do exemplo IX.3executariam um após o outro. A relação entre a sequência de execução e osistema operacional e dicas de como escrever programas multithread com

Page 9: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

8

sequência de execução independente de plataforma operacional serão em umaseção mais adiante neste mesmo capítulo.

Criando threads por meio da interface Runnable

Algumas vezes não é possível criar uma subclasse da classe Threadporque a classe já deriva outra classe, por exemplo a classe Applet. Outrasvezes, por questões de pureza de projeto o projetista não deseja derivar a classeThread simplesmente para poder criar um thread uma vez que isto viola osignificado da relação de classe-subclasse. Para esses casos existe a interfaceRunnable. A interface Runnable possui apenas um método para serimplementado: o método run(). Para criar um thread usando a interfaceRunnable é preciso criar um objeto da classe Thread, passando para oconstrutor uma instância da classe que implementa a interface. Ao invocar ométodo start() do objeto da classe Thread, o thread criado, inicia suaexecução no método run() da instância da classe que implementou a interface.O exemplo IX.4 mostra a criação de um thread usando a interface Runnable.

public class TesteThread2 implements Runnable{ private String men; public static void main(String args[]) {

TesteThread2 ob1 = new TesteThread2 (“ola”);Thread t1 = new Thread(ob1);t1.start();

} public TesteThread2 (String men) {this.men=men;} public void run() { for(;;) System.out.println(men); }}

Exemplo IX.4 – Criação de um thread por meio da interface Runnable.

Note que agora ao invocarmos o método start() o thread criadoiniciará a execução sobre o método run() do objeto passado como parâmetro, enão sobre o método run() do objeto Thread.

Page 10: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

9

Nada impede que seja criado mais de um thread executando sobre omesmo objeto:

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

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

A classe Thread

A classe Thread é extensa, possuindo vários construtores, métodos evariá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

Construtor DescriçãoThread(ThreadGroup g, String nome) Cria um novo thread com o nome

especificado dentro do grupo g.Thread(Runnable ob, String nome) Cria um novo thread para executar

sobre o objeto ob, com o nomeespecificado.

Thread(ThreadGroup g, Runnable ob, String nome)

Cria um novo thread para executarsobre o objeto ob, dentro do grupog, com o nome especificado.

Thread(String nome) Cria um novo thread com o nomeespecificado.

Thread() Cria um novo thread com o nomedefault.

Thread(Runnable ob) Cria um novo thread para executar

Page 11: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

10

sobre o objeto ob.Thread(ThreadGroup g, Runnable ob) Cria um novo thread para executar

sobre o objeto ob, dentro do grupog.

Tabela IX.1 – Construtores da classe Thread.

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

Métodos

Método DescriçãocurrentThread() Retorna uma referência para o thread corrente em

execução.destroy() Destroi o thread sem liberar os recursos.dumpStack() Imprime a pilha de chamadas do thread corrente.enumerate(Thread[] v) Copia para o array todos os thread ativos no

grupo do thread.getName() Obtém o nome do thread.getPriority() Obtém a prioridade do thread.getThreadGroup() Retorna o grupo do thread.resume() Reassume a execução de um thread previamente

suspenso.run() Se o thread foi construído usando um objeto

Runnable separado então o método do objetoRunnable é chamado. Caso contrário nadaocorre.

setName(String name) Muda o nome do thread.setPriority(int newPriority) Muda a prioridade do thread.sleep(long millis) Suspende o thread em execução o número de

milisegundos especificados.sleep(long millis, intnanos)

Suspende o thread em execução o número demilisegundos mais o número de nanosegundosespecificados.

start() Inicia a execução do thread. A máquina virtualchama o método run() do thread.

stop() Força o encerramento do thread.suspend() Suspende a execução de um thread.yield() Faz com que o thread corrente interrompa

permitindo que outro thread seja executado.

Tabela IX.2 – Métodos da classe Thread.

Page 12: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

11

A tabela X.2 apresenta os principais métodos do classe Thread. Algunsmétodos muito usados nas versões anteriores do SDK1.2 foram depreciados naversão atual por serem considerados inseguros ou com tendência a causaremdeadlock . Os métodos depreciados são: stop(), suspend(), resume() edestroy().

DeadlockTravamento causado pela espera circular de recursos em um conjunto de threads. O

travamento por deadlock mais simples é o abraço mortal onde um thread A espera que um threadB libere um recurso, enquanto que o thread B só libera o recurso esperado por A se obter umrecurso mantido por A. Desta forma os dois threads são impedidos indefinidamente deprosseguir.

Existem alguns métodos da classe Object que são importantes para ocontrole dos threads. O leitor pode estar se perguntando porque métodosrelacionados threads estão na superclasse Object que é “mãe” de todas asclasse em Java. A razão disso é que esses métodos lidam com um elementoassociado a todo objeto e que é usado para promover o acesso exclusivo aosobjetos. Esse elemento é chamado de monitor. Na seção que aborda asincronização os monitores serão discutidos mais detalhadamente. Os métodosherdados relacionados com controle dos threads estão descritos na tabela IX.3.

Método Descriçãonotify() Notifica um thread que está esperando sobre

um objeto.notifyAll() Notifica todos os threads que está esperando

sobre um objeto.wait() Espera para ser notificado por outro thread.wait(long timeout, int nanos) Espera para ser notificado por outro thread.wait(long timeout) Espera para ser notificado por outro thread.

Tabela IX.3 – Métodos da classe Object relacionados com threads.

Variáveis públicas

As variáveis públicas da classe Thread definem valores máximo, mínimoe default para a prioridade de execução dos threads. Java estabelece dez valoresde prioridade. Como essas prioridades são relacionadas com as prioridades do

Page 13: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

12

ambiente operacional depende da implementação máquina virtual e podeinfluenciar no resultado final da execução do programa. Mais adianteabordaremos a influência do ambiente operacional na execução de programasmultithread.

Variável Descriçãostatic final int MAX_PRIORITY A prioridade máxima que um thread pode ter.static final int MIN_PRIORITY A prioridade mínima que um thread pode ter.static final int NORM_PRIORITY A prioridade default associado a um thread.

Tabela IX.4 – Variáveis públicas.

Ciclo de Vida dos Threads

Um thread pode possuir quatro estados conforme mostra a figura IX.3.Podemos observar que uma vez ativo o thread alterna os estados em execução esuspenso até que passe para o estado morto. A transição de um estado paraoutro pode ser determinada por uma chamada explícita a um método ou devida aocorrência de algum evento a nível de ambiente operacional ou de programa.

Estados Ativos

Figura IX.3 – Estados de um thread.

A transição de um thread do estado novo para algum estado ativo ésempre realizada pela invocação do método start() do objeto Thread. Já astransições do estado em execução para o estado suspenso e vice-versa e dessespara o estado morto podem ser disparadas tanto pela invocação de variadosmétodos como pela ocorrência de eventos. O exemplo IX.5 mostra as ocorrênciade transição em um código.

threadnovo

thread emExecução

threadmorto

threadsuspenso

Page 14: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

13

public class TesteThread3 extends Thread{ public TesteThread3 (String str) {super(str);} public void run() { for (int i = 0; i < 10; i++) { System.out.println(i + " " + getName()); try { // Comando para suspender o thread por // 1000 milisegundos (1 segundo) // Transição do estado em execução para o // estado suspenso sleep(1000); } catch (InterruptedException e) {} // Evento: fim do tempo de suspensão // Transição do estado em suspenso para o // estado em execução } System.out.println("FIM! " + getName()); // Evento: fim da execução do thread // Transição do estado ativo suspenso para o // estado morto } public static void main(String args[]) {

TesteThread3 t1 = new TesteThread3(args[0]);t1.start(); // Transição para um estado ativo

}}

Exemplo IX.5 – Alguns comandos e eventos que acarretam transição deestados.

sleep(), yield(), join(), destroy(), stop(), suspend() eresume().

Agora que vimos os estados que podem ser assumidos por um thread emseu ciclo de vida vamos examinar mais detalhadamente alguns dos métodosresponsáveis pela mudança de estado de um thread.

Page 15: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

14

sleep()

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

static void sleep(long ms) throws InterruptedException

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

Onde ms é um valor em milisegundos e ns é um valor em nanosegundos.O método sleep() faz com que o thread seja suspenso por um

determinado tempo, permitindo que outros threads sejam executados. Como ométodo pode lançar a exceção InterruptedException, é preciso envolver achamada em um bloco try/catch ou propagar a exceção. O exemplo IX.6define uma espera mínima de 100 milisegundos entre cada volta do loop. Noteque o tempo de suspensão do thread pode ser maior que o especificado, uma vezque outros threads de maior ou mesmo de igual prioridade podem estar sendoexecutados no momento em que expira o tempo de suspensão solicitado.

public class ThreadComYield extends Thread { String s; public ThreadComYield(String as) { super(); s = new String(as); } public void run() { for (int i = 0; i < 5; i++) { System.out.println(i+” “+s); try{ Thread.sleep(100); catch(InterruptedException e){} } System.out.println("FIM! "+s); }}

Exemplo IX.6 – Uso do método sleep().

Outro problema com o sleep() é que a maioria dos SistemasOperacionais não suportam resolução de nanosegundos. Mesmo a resolução anível de unidade de milisegundo não é suportada pela maioria dos SOs. No casodo SO não suportar a resolução de tempo solicitada, o tempo será arredondadopara a nível de resolução suportado pela plataforma operacional.

Page 16: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

15

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 corrente libereautomaticamente a CPU para outro thread de mesma prioridade. Se não houvernenhum outro thread de mesma prioridade aguardando, então o thread correntemantém a posse da CPU. O exemplo IX.7 altera o exemplo IX.1 de modo apermitir que outros threads de mesma prioridade sejam executados a cada voltado loop.

Public class ThreadComYield extends Thread { String s; public ThreadComYield(String as) { super(); s = new String(as); } public void run() { for (int i = 0; i < 5; i++) { System.out.println(i+” “+s); Thread.yield(); } System.out.println("FIM! "+s); }}

Exemplo IX.7 – Uso do método yield().

join()

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

public final void join();public final void join(long millisecond);public final void join(long millisecond, int nanosecond);

Page 17: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

16

Na primeira versão o thread corrente espera indefinidamente peloencerramento da execução do segundo thread. Na segunda e terceira versão othread corrente espera pelo término da execução do segundo thread até nomáximo um período de tempo prefixado. O exemplo IX.8 mostra o como usar ométodo join().

class ThreadComJoin extends Thread { String s; public ThreadComJoin(String as) { super(); s = new String(as); } public void run() { for (int i = 0; i < 10; i++) System.out.println(i+” “+s); System.out.println("Fim do thread!"); }}

public class TestaJoin{ public static void main(String args[]) {

ThreadComJoin t1 = new ThreadComJoin(args[0]);t1.start(); // Transição para um estado ativot1.join(); // Espera pelo término do threadSystem.out.println("Fim do programa!");

}}

Exemplo IX.8 – Uso do método join().

stop(), suspend(), resume() e destroy()

A partir da versão 1.2 do SDK os métodos stop(), suspend(), andresume() tornaram-se deprecated uma vez que a utilização desses métodostendia a gerar erros. No entanto, devido a grande quantidade de código que aindautiliza estes método, acreditamos que seja importante mencioná-los.

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

Page 18: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

17

O método suspend() é um método de instância que suspende aexecução do thread ao qual pertence. Nenhum recurso é liberado, inclusive osmonitores que possuir no momento da suspensão (os monitores serão vistos maisadiante e servem para controlar o acesso à variáveis compartilhadas). Isto fazcom que o método suspend() tenda a ocasionar deadlocks. O métodoresume() é um método de instância que reassume a execução do thread ao qualpertence. Os métodos suspend() e resume() devem ser substituídosrespectivamente pelos métodos wait() e notify(), como veremos maisadiante.

O método destroy() é um método de instância que encerra a execuçãodo thread ao qual pertence. Os recursos alocados ao thread não são liberados.Não é um método deprecated mas é recomendável substituí-lo pelo simplesretorno do método run().

Daemon Threads

Daemon threads são threads que rodam em background com a função deprover algum serviço mas não fazem parte do propósito principal do programa.Quando só existem threads do tipo daemon o programa é encerrado. Umexemplo de daemon é o thread para coleta de lixo.

Um thread é definido como daemon por meio do método de instânciasetDaemon(). Para verificar se um thread é um daemon é usado o método deinstância isDaemon(). O exemplo IX.9 mostra o como usar esses métodos.

import java.io.*;class ThreadDaemon extends Thread{ public ThreadDaemon() { setDaemon(true); start(); } public void run() { for(;;) yield(); }}

public class TestaDaemon{

Page 19: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

18

public static void main(String[] args) { Thread d = new ThreadDaemon(); System.out.println("d.isDaemon() = " + d.isDaemon()); BufferedReader stdin = new BufferedReader( new InputStreamReader(System.in)); System.out.println("Digite qualquer coisa"); try { stdin.readLine(); } catch(IOException e) {} }}

Exemplo IX.9 – Uso dos métodos relacionados com daemons.

No exemplo IX.9 o método main() da classe TestaDaemon cria umobjeto da classe ThreadDaemon. O construtor da classe ThreadDaemondefine o thread como daemon por meio do método setDaemon() e inicia aexecução do thread. Como é apenas um thread de demonstração o método run()da classe ThreadDaemon não faz nada, apenas liberando a posse da CPU todavez que a adquire. Após a criação da instância da classe ThreadDaemon nométodo main() é testado se o thread criado é um daemon, utilizando para essefim o método isDaemon(). Depois disso o programa simplesmente espera ousuário pressionar a tecla <enter>. O programa termina logo em seguida aoacionamento da tecla, mostrando dessa forma que o programa permanece ativoapenas enquanto existem threads não daemons ativos.

Influência do Sistema Operacional no Comportamentodos Threads

Apesar da linguagem Java prometer a construção de programasindependentes de plataforma operacional, o comportamento dos threads pode serfortemente influenciado pelo sistema operacional subjacente. Portanto, oprogramador deve tomar alguns cuidados se deseja construir programas quefuncionem da mesma forma, independente do ambiente onde está sendoexecutado.

Page 20: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

19

Alguns sistemas operacionais não oferecem suporte a execução dethreads. Neste caso, cada processo possui apenas um thread. Mesmo em sistemasoperacionais que oferecem suporte a execução de múltiplos threads por processoo projetista da máquina virtual pode optar por não usar o suporte nativo athreads. Deste modo, é responsabilidade da máquina virtual criar um ambientemultithread. Threads implementados desta forma, a nível de usuário, sãochamados de green-threads.

As influências da plataforma operacional podem agrupadas em doistipos:

1) Forma de escalonamento de threads. O ambiente pode adotar umescalonamento não preemptivo ou preemptivo. No escalonamentonão preemptivo (também chamado de cooperativo) um thread emexecução só perde o controle da CPU (Central Processing Unit) se aliberar voluntariamente ou se necessitar de algum recurso que aindanão está disponível. Já no escalonamento preemptivo, além dasformas acima um thread pode perde o controle da CPU por eventosexternos, como o fim do tempo máximo definido pelo ambiente paraa execução contínua de um thread (fatia de tempo) ou porque umthread de mais alta prioridade está pronto para ser executado.Exemplos de sistemas operacionais não preemptivos são Windows3.1 e IBM OS/2. Exemplos de sistemas operacionais preemptivos sãoWindows 95/98/NT e Linux, QNX, e muitos outros. Alguns sistemasoperacionais adotam uma abordagem híbrida, suportando tanto omodelo cooperativo como o preemptivo, como o Solaris da Sun.

2) Relacionamento entre os níveis de prioridades definidas nalinguagem Java e os níveis de prioridades definidas nos SistemasOperacionais. Em um SO preemptivo um thread de umadeterminada prioridade perde a posse da CPU para um thread deprioridade mais alta que esteja pronto para ser executado. Alinguagem Java prevê dez níveis de prioridades que podem seratribuídas aos threads. No entanto, cada SO possui um número deprioridades diferente e o mapeamento das prioridades da linguagemJava para as prioridades do SO subjacente pode influenciar ocomportamento do programa.

Forma de escalonamento de threads

Page 21: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

20

A especificação da máquina virtual Java determina que a forma deescalonamento de threads seja preemptiva. Portanto, mesmo em ambienteoperacionais cooperativos a máquina virtual deve garantir um escalonamentopreemptivo. No entanto, um escalonamento preemptivo não obriga a preempçãopor fim de fatia de tempo. Podemos ter um escalonamento preemptivo onde umthread de mais alta prioridade interrompe o thread que tem a posse da CPU masnão existe preempção por fim de fatia de tempo. Um escalonamento ondethreads de mesma prioridade intercalam a posse da CPU por força do fim dafatia de tempo é chamado de escalonamento Round-Robin. A especificação damáquina virtual Java não prevê o escalonamento Round-Robin, mas também nãoo descarta, abrindo uma possibilidade de implementações distintas de máquinavirtual e introduzindo o não determinismo na execução de programasmultithread. Por exemplo, o exemplo IX.3 poderia ter uma saída distinta daapresentada anteriormente caso seja executado por uma máquina virtual que nãoimplementa o escalonamento Round-Robin. Nesse caso a saída seria a seguinte:

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

Neste caso, se o programador deseja que a execução de threads seprocesse de forma alternada, independentemente da implementação da máquinavirtual, então é necessário que ele insira código para a liberação voluntária daCPU. Isso pode ser feito com o método yield() ou com o método sleep().

Relacionamento entre os níveis de prioridades definidas nalinguagem Java e os níveis de prioridades definidas nos SistemasOperacionais.

Como já dissemos a linguagem Java prevê dez níveis de prioridades quepodem ser atribuídas aos threads. Na verdade são onze prioridades, mas aprioridade nível 0 é reservada para threads internos. As prioridades atribuídas

Page 22: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

21

aos threads são estáticas, ou seja não se alteram ao longo da vida do thread, anão ser que por meio de chamadas a métodos definidos para esse propósito. Aclasse thread possui variáveis públicas finais com valores de prioridadepredefinidos, como mostrado na tabela IX.4. No entanto, os sistemasoperacionais podem possuir um número maior ou menor de níveis deprioridades. Vamos citar um exemplo: o MSWindows 95/98/NT. Este sistemapossui apenas sete níveis de prioridades e estes sete níveis devem ser mapeadospara os onze níveis de prioridades especificados em Java. Cada máquina virtualfará este mapeamento de modo diferente, porém a implementação comum émostrada na tabela IX.5.

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_HIGHEST9 THREAD_PRIORITY_HIGHEST10(Thread.MAX_PRIORITY) THREAD_PRIORITY_TIME_CRITICAL

Tabela IX.5 –Mapeamento das prioridades de Java para MSWindows.

Note que nesta implementação níveis de prioridades diferentes em Javaserão mapeados para um mesmo nível de prioridade em MSWindows. Isto podelevar a resultados inesperados caso o programador projete uma aplicaçãoesperando, por exemplo, que um thread de prioridade 4 irá interromper umthread de prioridade 3. Para evitar este tipo de problema o programador podeadotar dois tipos de abordagem:

1) utilizar, se for possível, apenas as prioridades Thread.MIN_PRIORITY,Thread.NORM_PRIORITY e Thread.MAX_PRIORITY para atribuir prioridadesaos threads; ou

2) não se basear em níveis de prioridades para definir o escalonamento dethreads, utilizando, alternativamente, primitivas de sincronização que serãoabordadas na próxima seção.

Page 23: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

22

Compartilhamento de Memória e Sincronização

Como já foi dito, mais de um thread pode ser criado sobre um mesmoobjeto. Neste caso cuidado especiais devem ser tomados, uma vez que os threadscompartilham as mesmas variáveis e problemas podem surgir se um thread estáatualizando uma variável enquanto outro thread está lendo ou atualizando amesma variável. Este problema pode ocorrer mesmo em threads que executamsobre objetos distintos, já que os objetos podem possuir referências para ummesmo objeto. O exemplo IX.8 mostra a execução de dois threads sobre ummesmo objeto. O nome do thread é usado para que o thread decida que açãotomar. O thread de nome “um” ontem um número de 0 a 1000 geradoaleatoriamente e o coloca na posição inicial de um array de dez posições. Asoutras posições do array são preenchidas com os nove números inteirosseguintes ao número inicial. O thread de nome “dois” imprime o conteúdo dovetor. Obviamente o programa é apenas ilustrativo, não possuindo aplicaçãoprática. A intenção inicial do projetista é obter na tela sequências de deznúmeros inteiros consecutivos iniciados aleatoriamente. No entanto, como osdois threads compartilham o mesmo objeto e não existe qualquer sincronismoentre sí, é pouco provável que o projetista obtenha o resultado esperado.

public class CalcDez implements Runnable{ private int vetInt[];

public CalcDez () {vetInt=new int[10]; } public void run() { if (Thread.currentThread().getName().equals(”um”)) for (;;) { vetInt[0] = (int)(Math.random() * 1000); for (int i=1;i<10;i++) vetInt[i]= vetInt[0]+i; } else for (;;) { System.out.println(“Serie iniciada por”+ vetInt[0]); for (int i=1;i<10;i++) System.out.println(vetInt[i]+ “ “); }

} public static void main(String args[]) {

Page 24: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

23CalcDez ob = new CalcDez();Thread t1 = new Thread(ob,”um”);Thread t2 = new Thread(ob,”dois”);t1.start();t2.start();

}}

Exemplo IX.8 – Dois threads executando sobre o mesmo objeto.

Se a máquina virtual não implementar um escalonamento Round-Robinapenas um thread será executado, visto que os dois threads possuem a mesmaprioridade.

Já no caso da máquina virtual implementar um escalonamento Round-Robin a alternância da execução dos threads produzirá resultados imprevisíveis.Um trecho de uma das saídas possíveis pode ser visto na figura IX.4. Ele foiobtido em Pentium 100MHz executando a máquina virtual da Sun, versão 1.2,sob o sistema operacional MSWindows 95.

258259Serie iniciada por573574575576577578579580581582Serie iniciada por808182

Figura IX.4 – Saída do exemplo IX.8.

Podemos notar as sequências estão misturadas, mostrando que cadathread interrompe o outro no meio da execução da tarefa especificada. O mesmoproblema pode mesmo em threads que executam sobre objetos diferentes,bastando que cada thread possua referência para um mesmo objeto. O exemploIX.9 mostra a execução de dois threads sobre objetos distintos.

Page 25: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

24

class Compartilhada{ private int vetInt[]; public Compartilhada() {vetInt=new int[10];} public void setVal() { for (;;) { vetInt[0] = (int)(Math.random() * 1000); for (int i=1;i<10;i++) vetInt[i]= vetInt[0]+i; } } public int getVal(int i) {return vetInt [i];}}

public class CalcDez2 extends Thread{ private Compartilhada obj; private int tipo;

public CalcDez2 (Compartilhada aObj, int aTipo) { obj = aObj; tipo = aTipo;} public void run() { for (;;) if (tipo==1) obj.setVal(); else { System.out.println(“Serie iniciada por”+ obj.getVal(0)); for (int i=1;i<10;i++) System.out.println(obj.getVal(i)+ “ “); }

} public static void main(String args[]) { Compartilhada obj = new Compartilhada();

CalcDez2 t1 = new CalcDez2(obj,1);CalcDez2 t2 = new CalcDez2(obj,2);t1.start();t2.start();

}}

Exemplo IX.9 – Dois threads executando sobre objetos distintos.

Page 26: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

25

É importante que o leitor não confunda o exemplo IX.9 com o exemploIX.8 achando que nos dois exemplos os dois threads executam sobre o mesmoobjeto, uma vez que a etapa da criação dos threads é bem parecida. No entanto,no exemplo IX.9 foi declarada uma subclasse da classe Thread e não umaclasse que implementa a interface Runnable. Apesar de parecer que noexemplo IX.9 ambos os threads executaram sobre um mesmo objeto da classeCompartilhada que é passado como argumento, na verdade cada threadexecutará sobre sua própria instância da classe CalcDez2, sendo que o objetoda classe Compartilhada é referenciado pelos dois threads. Ocomportamento do código do exemplo IX.9 é semelhante ao do exemplo IX.8,com a diferença que no primeiro a sequência de inteiros é encapsulado peloobjeto da classe Compartilhada.

Este tipo de situação, onde o resultado de uma computação depende daforma como os threads são escalonados, é chamada de condições de corrida(Race Conditions). É um problema a ser evitado uma vez que o programa passaa ter um comportamento não determinístico.

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

A condição de corrida ocorre porque os acesso à áreas de memóriacompartilhada não é feita de forma atômica, e nem de forma exclusiva. Porforma atômica queremos dizer que o acesso é feito por meio de várias instruçõese pode ser interrompido por outro thread antes que toda as instruções quecompõem o acesso sejam executadas. Por forma exclusiva queremos dizer queum thread podem consultar/atualizar um objeto durante a consulta/atualização domesmo objeto por outros threads. Poucas operações são atômicas em Java. Emgeral, as atribuições simples, com exceção dos tipos long e double, sãoatômicas, de forma que o programador não precisa se preocupar em serinterrompido no meio de uma operação de atribuição. No entanto, no caso deoperações mais complexas sobre variáveis compartilhadas é preciso que oprogramador garanta o acesso exclusivo a essas variáveis. Os trechos de códigoonde é feito o acesso às variáveis compartilhadas são chamados de SeçõesCríticas ou Regiões Críticas.

Uma vez determinada uma região crítica como garantir o acessoexclusivo? A linguagem Java permite que o programador garanta o acessoexclusivo por meio utilizando o conceito de monitor. O conceito de monitor foiproposto por C. A. R. Hoare em 1974 e pode ser encarado como um objeto que

Page 27: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

26

garante a exclusão mútua na execução dos procedimentos a ele associados. Ouseja, apenas um procedimento associado ao monitor pode ser executado em umdeterminado momento. Por exemplo, suponha que dois procedimentos A e Bestão associados a um monitor. Se no momento da invocação do procedimento Aalgum o procedimento B estiver sendo executando o processo ou thread queinvocou o procedimento A fica suspenso até o término da execução doprocedimento B. Ao término do procedimento B o processo que invocou oprocedimento A é “acordado” e sua execução retomada.

O uso de monitores em Java é uma variação do proposto por Hoare. nalinguagem Java todo objeto possui um monitor associado. Para facilitar oentendimento podemos encarar o monitor como um detentor de um “passe”.Todo thread pode pedir “emprestado” o passe ao monitor de um objeto antes derealizar alguma computação. Como o monitor possui apenas um passe, apenasum thread pode adquirir o passe em um determinado instante. O passe tem queser devolvido para o monitor para possibilitar o empréstimo do passe a outrothread. A figura IX.5 ilustra essa analogia.

Page 28: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

27

Instante 1: o thread t1 solicita Instante 2: o thread t2 solicita o passe ao monitor o passe ao monitor do objeto x. do objeto x e é

bloqueado.

Instante 3: o thread t1 libera Instante 4: o thread t2 recebe o passe. o passe do monitor

do objeto x.

Figura IX.5 – Uma possível sequência na disputa de dois threads pelaautorização de um monitor.

Nos resta saber como solicitar o passe ao monitor. Isto é feito por meioda palavra chave synchronized. Existem duas formas de se usar a palavrachave synchronized: na declaração de métodos e no início de blocos. Oexemplo IX.10 mostra duas versões da classe FilaCirc que implementa umafila circular de valores inteiros: uma com métodos synchronized e outracom blocos synchronized. Um objeto desta classe pode ser compartilhadopor dois ou mais threads para implementar o exemplo clássico de concorrênciado tipo produtor/consumidor.

Objeto x

Monitor de x!passe

thread t1 !

!

thread t2

Monitor de x!

Monitor de x

Monitor de x

Objeto x

Objeto x Objeto x

Page 29: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

28

a) Versão com métodossynchronized

b) Versão com blocossynchronized

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 new Exception("Fila vazia!"); int temp = vetInt[inicio]; inicio = (++inicio)%TAM; total--; return temp; }}

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 IX.10 – Duas versões de uma classe que implementa uma fila circularde inteiros.

A palavra chave synchronized na frente dos métodos de instânciasignifica que o método será executado se puder adquirir o monitor do objeto aquem pertence o método1. Caso contrário o thread que invocou o método serásuspenso até que possa adquirir o monitor. Este forma de sincronização éabordada no exemplo IX.10.a. Portanto, se algum thread chamar algum métodode um objeto da classe FilaCirc nenhum outro thread que compartilha omesmo objeto poderá executar um método do objeto até que o método chamado 1 Não usaremos mais a analogia com a aquisição do passe do monitor. Ela foi usada apenas parafacilitar o entendimento do leitor. Quando se trata de monitores os termos mais usados são:“adquirir o monitor” e “liberar o monitor”.

Page 30: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

29

pelo primeiro thread termine. Caso outro thread invoque um método do mesmoobjeto ficará bloqueado até que possa adquirir o monitor.

O leitor pode estar se perguntando sobre a necessidade de sincronizar osmétodos da classe FilaCirc uma vez que ocorrem apenas atribuições simplesa elementos individuais de um vetor e as atribuições de inteiros são atômicas. Defato o problema ocorre não na atribuição dos elementos e sim na indexação doarray. Por exemplo, a instrução

inicio = (++inicio)%TAM;

do método getElement() não é atômica. Suponha que a os métodos daclasse FilaCirc não são sincronizados e que as variáveis inicio e totalpossuem os valores 9 e 1 respectivamente. Suponha também que thread invocouo método getElement() e foi interrompido na linha de código mostradaacima após o incremento da variável inicio mas antes da conclusão da linhade código. Nesse caso o valor de inicio é 10. Se neste instante outro threadexecutar o método getElement() do mesmo objeto ocorrerá uma exceçãoIndexOutOfBoundsException ao atingir a linha 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 retornar mais de umavez o mesmo elemento. Por exemplo, se um thread for interrompido no mesmolocal do caso anterior, outro thread pode obter o mesmo elemento, uma vez queos valores de inicio e total não foram alterados. Na verdade o número desituações problemáticas, mesmo para esse exemplo pequeno, é enorme eperderíamos muito tempo se tentássemos descreve-las em sua totalidade.

Em alguns casos pode ser indesejável sincronizar todo um método, oupode-se desejar adquirir o monitor de outro objeto, diferente daquele a quempertence o método. Isto pode ser feito usando a palavra chave synchronizedna frente de blocos. Este forma de sincronização é mostrada no exemploIX.10.b. Neste modo de usar a palavra-chave synchronized é necessárioindicar o objeto do qual tentara-se adquirir o monitor. Caso o monitor sejaadquirido o bloco é executado, caso contrário o thread é suspenso até que possaadquirir o monitor. O monitor é liberado no final do bloco.

Page 31: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

30

No exemplo IX.10.b o monitor usado na sincronização é o do próprioobjeto do método, indicado pela palavra chave this. Qualquer outro objetoreferenciável no contexto poderia ser usado. O que importa que os grupos dethreads que possuem áreas de código que necessitam de exclusão mútua usem omesmo objeto.

No exemplo IX.10 não existe vantagem da forma de implementação a)sobre a forma de implementação b) ou vice-versa. Isso ocorre principalmentequando os métodos são muito pequenos ou não realizam computações muitocomplexas. No entanto, se o método for muito longo ou levar muito tempo paraser executado, sincronizar todo o método pode “travar” em demasia a execuçãoda aplicação. Nesses casos, a sincronização somente das seções críticas é maisindicada. Outra vantagem da segunda forma de sincronização é a liberdade nouso de monitores qualquer objeto referenciável. Isto permite a implementaçãosincronizações mais complexas como veremos mais adiante.

O exemplo IX.11 mostra como pode ser usado um objeto da classeFilaCirc.

public class TestaFilaCirc extends Thread { private FilaCirc obj; private int tipo;

public TestaFilaCirc (FilaCirc aObj, int aTipo) { obj = aObj; tipo = aTipo;} public void run() { for (;;) try { if (tipo==1){ int i = (int)(Math.random() * 1000); System.out.println("Elemento gerado:"+i); obj.addElement(i); } else System.out.println("Elemento obtido:"+obj.getElement()); } catch(Exception e) {System.out.println(e.getMessage());} } public static void main(String args[]) { FilaCirc obj = new FilaCirc(); TestaFilaCirc t1 = new TestaFilaCirc(obj,1); TestaFilaCirc t2 = new TestaFilaCirc(obj,2); t1.start(); t2.start(); }}

Exemplo IX.11 – Uso da fila circular de inteiros.

Um trecho possível da saída obtida na execução do programa do exemploIX.11 seria o seguinte:...Elemento obtido:154

Page 32: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

31

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

É importante observar que o monitor em Java por si só não implementa aexclusão mútua. Ele é apenas um recurso que pode ser usado pelo programadorpara implementar o acesso exclusivo à variáveis compartilhadas. Cabe aoprogramador a responsabilidade pela uso adequado deste recurso. Por exemplose o programador esquecer de sincronizar um bloco ou método que necessita deexclusão mútua, de nada adianta ter sincronizado os outros métodos ou blocos.O thread que executar o trecho não sincronizado não tentará adquirir o monitor,e portanto de nada adianta os outros threads terem o adquirido.

Outro ponto que é importante chamar a atenção é ter o cuidado de usar apalavra chave synchronized com muito cuidado. A sincronização custamuito caro em se tratando de ciclos de CPU. A chamada de um métodosincronizado é 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ão sincronize oque não for preciso.

Comunicação entre Threads: wait() e notify()

O exemplo IX.10 não é um modelo de uma boa implementação deprograma. O thread que adiciona elementos à fila tenta adicionar um elemento àcada volta do laço de iteração mesmo que a fila esteja cheia. Por outro lado, othread que retira os elementos da fila tenta obter um elemento a cada volta dolaço de iteração mesmo que a fila esteja vazia. Isto é um desperdício de tempode processador e pode tornar o programa bastante ineficiente.

Alguém poderia pensar em uma solução onde o thread testaria se acondição desejada para o processamento ocorre. Caso a condição não ocorra othread poderia executar o método sleep() para ficar suspenso por algumtempo para depois testar novamente a condição. O thread procederia desta formaaté que a condição fosse satisfeita. Este tipo de procedimento economizariaalguns ciclos de CPU, evitando que a tentativa incessante de executar oprocedimento mesmo quando não há condições. O nome desta forma de ação,

Page 33: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

32

onde o procedimento a cada intervalo de tempo pré-determinado testa se umacondição é satisfeita é chamado de espera ocupada (pooling ou busy wait).

No entanto, existem alguns problemas com este tipo de abordagem.Primeiramente, apesar da economia de ciclos de CPU ainda existe apossibilidade de ineficiência, principalmente se o tempo não for bem ajustado.Se o tempo for muito curto ocorrerá vários testes inúteis. Se for muito longo, othread ficará suspenso além do tempo necessário. Porém, mais grave que isto éque o método sleep() faz com que o thread libere o monitor. Portanto, se otrecho de código for uma região sincronizada, como é o caso do exemplo IX.10,de nada adiantará o thread ser suspenso. O thread que é capaz de realizar acomputação que satisfaz a condição esperada pelo primeiro thread ficaráimpedido de entrar na região crítica, ocorrendo assim um deadlock: o thread quedetém o monitor espera que a condição seja satisfeita e o thread que podesatisfazer a condição não pode prossegui porque não pode adquirir o monitor.

O que precisamos é um tipo de comunicação entre threads quecomunique que certas condições foram satisfeitas. Além disso, é preciso que, aoesperar por determinada condição, o thread libere o monitor. Esta forma deinteração entre threads é obtido em Java com o uso dos métodos de instânciawait(), notify() e notifyAll(). Como vimos anteriormente, essesmétodos pertencem à classe Object e não à classe Thread. Isto ocorre porqueesses métodos atuam sobre os monitores, que são objetos relacionados a cadainstância de uma classe Java e não sobre os threads.

Ao invocar o método wait() de um objeto o thread é suspenso einserido em uma fila do monitor do objeto, permanecendo na fila até receberuma notificação. Cada monitor possui sua própria fila. Ao invocar o métodonotify() de um objeto, um thread que está na fila do monitor do objeto énotificado. Ao invocar o método notifyAll() de um objeto, todos os threadsque estão na fila do monitor do objeto são notificados.

A única exigência é que esses métodos sejam invocados em um threadque detenham a posse do monitor do objeto a que pertencem. Essa exigência fazsentido uma vez que eles sinalizam a threads que esperam na fila dessesmonitores. Devido a essa exigência a invocação desses métodos ocorre emmétodos ou blocos sincronizados. O exemplo IX.12 mostra as formas maiscomuns de chamadas desses métodos.

Note que o thread deve possuir o monitor do objeto ao qual pertence ométodo. Por isso, nos exemplo IX.12 b e c, o objeto sincronizado no bloco é omesmo que invoca os métodos notify() e notifyAll().

a) b)class X class Y

Page 34: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

33{ ... public synchronized int ma() { ... // Espera uma condição while(!cond) wait(); // Prossegue com a // condição satisfeita ... } ...}

{ X ob; ... public int mb() { ... synchronized (ob) { // Notifica algum thread ob.notify(); ... } ...}

c)class Z{ X ob; ... public int mc() { ... synchronized (ob) { // Notifica todos os threads que esperam na fila // do monitor de ob ob.notifyAll(); ... } ...}

Exemplo IX.12 – Exemplos de chamadas dos métodos wait(), notify() enotifyAll().

Outra observação importante é que o thread que invoca o métodowait() o faz dentro de um laço sobre a condição de espera. Isto ocorre porqueapesar de ter sido notificado isto não assegura que a condição está satisfeita. Othread pode ter sido notificado por outra razão ou entre a notificação e aretomada da execução do thread a condição pode ter sido novamente alterada.

Uma vez notificado o thread não retoma imediatamente a execução. Épreciso primeiro retomar a posse do monitor que no momento da notificaçãopertence ao thread que notificou. Mesmo após a liberação do monitor nada

Page 35: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

34

garante que o thread notificado ganhe a posse do monitor. Outros threads podemter solicitado a posse do monitor e terem preferência na sua obtenção.

O exemplo IX.12 mostra apenas um esquema para uso dos métodos paranotificação. O exemplo IX.13 é uma versão do exemplo IX.10a que usa osmétodos de notificação para evitar problemas como a espera ocupada. Oexemplo IX.11 pode ser usado sem modificações para testar essa versão.

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 { while (total == TAM) wait(); vetInt[(inicio+total)%TAM] = v; total++; notify(); } public synchronized int getElement() throws Exception { while (total == 0 ) wait(); int temp = vetInt[inicio]; inicio = (++inicio)%TAM; total--; notify(); return temp; }}

Exemplo IX.13 – Classe que implementa uma fila circular de inteiros comnotificação.

A necessidade de se testar a condição em loop pode ser observada nafigura IX.6 que mostra a evolução da execução de três threads sobre objetos quecompartilham uma 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 notificação. O próximo

Page 36: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

35

thread a assumir a CPU é o thread 1 que executa o método getElement(), queestabelece a condição total<TAM e executa um notify(). No entanto, opróximo thread a assumir a CPU é o thread 2 e não o thread 3. O thread 2executa o método addElement(), o qual estabelece novamente a condiçãototal==TAM. Quando o thread 3 assumi novamente a CPU, uma vez que foinotificado, testa a condição e invoca novamente o método wait() para esperara condição favorável à execução. Caso não testasse a condição em um loop othread 3 tentaria inserir um elemento em uma fila cheia.

O método notify() não indica que evento ocorreu. No caso doexemplo IX.13 existem dois tipos de eventos (a fila não está cheia e a fila nãoestá vazia), no entanto, podemos observar que não existe a possibilidade de umthread ser notificado em decorrência de um evento diferente do que estáaguardando.

Tempo

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

Porém, existem alguns casos mais complexos onde podem existir váriosthreads aguardando em um mesmo monitor mas esperando por evento diferentes.Neste caso podemos usar o notifyAll() para notificar todos os threads queesperam em um único monitor que um evento ocorreu. Cada thread, a medidaque fosse escalado, testaria se ocorreu condição para a execução e em casopositivo prosseguiria na execução e em caso contrário voltaria a aguardar nomonitor.

O exemplo IX.14 mostra o código de um gerenciador de mensagens. Eleé responsável por receber mensagens destinadas à vários threads. As mensagens

thread 3

thread 2

thread 1

Método: addElement()condição: total == TAM

Método: getElement()

Método: addElement()condição: total < TAM

Executando Esperando CPU Esperando notificação

Page 37: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

36

de cada thread são colocadas em uma fila implementada por um objeto da classeVector. Cada fila é por sua vez colocada em uma tabela hash onde a chave éum nome associado ao thread a que as mensagens se destinam. As filas sãocriadas na primeira tentativa de acesso, tanto na leitura quanto noarmazenamento. Não existe bloqueio devido à fila cheia, uma vez que as filassão implementadas por objetos da classe Vector que crescem conforme anecessidade. Portanto, o único evento que necessita ser notificado é a chegada dealguma mensagem. Como todos os threads aguardam sobre o mesmo monitor éusado o método notifyAll() para notificar todos os threads.

import java.util.*;

class GerenteMen { private Hashtable tamMen;

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

// Método para adicionar uma mensagem à fila de // um destinatário public synchronized void addMen(String dest, String men){ if (dest==null || men==null) return; Vector listaMen = (Vector) tamMen.get(dest); if (listaMen==null) listaMen = new Vector(); listaMen.addElement(men); tamMen.put(dest, listaMen); notifyAll(); }

// Método para obtenção da mensagem public synchronized String getMen(String dest) throws Exception { if (dest==null) return null; Vector listaMen = (Vector) tamMen.get(dest);

// Se não existe a fila para esse thread cria uma vazia if (listaMen==null) { listaMen = new Vector(); tamMen.put(dest, listaMen); } // A fila está vazia, portanto thread deve esperar // a chegada de mensagens while(listaMen.size()==0) wait(); String temp = (String) listaMen.firstElement();

Page 38: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

37 // A mensagem é removida da fila listaMen.removeElementAt(0); return temp; }}

Exemplo IX.14 – Gerenciador de mensagens.

O exemplo IX.15 mostra como pode ser usado o gerente de filas doexemplo IX.14. Devido o uso da classe ThreadGroup assim como vários deseus métodos, resolvemos numerar as linhas de código do exemplo IX.15 paramelhor podermos explicar o seu funcionamento.

12345678910111213141516171819202122232425262728293031323334353637383940414243

class Receptor extends Thread{ private GerenteMen ger; public Receptor(ThreadGroup tg, String nome, GerenteMen aGer) { super(tg,nome); ger = aGer; } public void run() { String nome = Thread.currentThread().getName(); for (;;) try { String men = ger.getMen(nome); if (men.equals("fim")) return; System.out.println(nome+">Mensagem recebida:"+men); } catch(Exception e) {System.out.println(e.getMessage());} }}

class Gerador extends Thread{ private GerenteMen ger; public Gerador(ThreadGroup tg, String nome, GerenteMen aGer) { super(tg,nome); ger = aGer; } public void run() { String nome = Thread.currentThread().getName(); ThreadGroup tg = Thread.currentThread().getThreadGroup(); Thread[] tl=null; for (int i=0;i<100;i++) { if (tl==null || tl.length!=tg.activeCount()) tl= new Thread[tg.activeCount()]; tg.enumerate(tl); int n = (int)(Math.random() * 1000)%tl.length; if (tl[n]!= Thread.currentThread()) { System.out.println(nome+">Mensagem enviada para "+ tl[n].getName()+":mensagem "+i); ger.addMen(tl[n].getName(),"mensagem "+i); }

Page 39: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

38444546474849505152535455565758596061626364

} tl= new Thread[tg.activeCount()]; tg.enumerate(tl); for (int i=0;i<tl.length;i++) if (tl[i]!= Thread.currentThread()) ger.addMen(tl[i].getName(),"fim"); }}

public class TestaGerenteMen { public static void main(String args[])throws Exception { GerenteMen ger = new GerenteMen(); ThreadGroup tg = new ThreadGroup("tg"); Receptor r1 = new Receptor(tg,"r_um",ger); Receptor r2 = new Receptor(tg,"r_dois",ger); Gerador g = new Gerador(tg,"g",ger); r1.start(); r2.start(); g.start(); }}

Exemplo IX.15 – Uso do gerenciador de filas.

Um objeto da classe ThreadGroup agrupa um conjunto de threads.Um ThreadGroup pode possuir como membros outros objetos daThreadGroup formando assim uma árvore onde todos os grupos, exceto oprimeiro possui um grupo pai. O objetivo de se agrupar os threads em conjuntosé facilitar a sua manipulação. No caso do exemplo IX.15 usaremos esseagrupamento para poder acessar cada thread.

As linhas 1 a 18 definem a classe que será usada para criação de objetosreceptores de mensagens. Na linha 3 é declarada a variável que irá referenciarum objeto do tipo GerenteMen. As linhas 4 a 8 contém o código do únicoconstrutor da classe. Ele recebe uma referência para o grupo de threads ao qualdeve se associar, o nome que deve ser atribuído ao thread e a referência aogerente de filas. Na linha 6 os primeiros dois parâmetros são passados aoconstrutor da superclasse. Na linha 7 a referência ao gerente de filas é atribuída àvariável da instância. As linhas 9 a 17 contém o código do método run() que éo método de entrada do thread. Na linha 10 é invocado o método

Thread.currentThread().getName();

para se obter o nome do thread corrente. O nome do thread é usado parareferenciar a fila de mensagens do thread. Entre as linhas 11 e 16 é executadoum laço infinito onde o thread recebe e imprime as mensagens recebidas. Nalinha 14 o thread testa se a mensagem recebida é igual a “fim”. Neste caso othread encerra sua execução.

Page 40: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

39

As linhas 20 a 51 definem a classe que será usada para criação do objetogerador de mensagens. Este exemplo foi projetado para lidar com apenas umthread gerador de mensagem. Modificações devem ser realizadas para tratar deaplicações com mais de um thread gerador de mensagens. Na linha 22 édeclarada a variável que irá referenciar um objeto do tipo GerenteMen. Aslinhas 23 a 27 contém o código do único construtor da classe. Ele recebe umareferência para o grupo de threads ao qual deve se associar, o nome que deve seratribuído ao thread e a referência ao gerente de filas. Na linha 25 os primeirosdois parâmetros são passados ao construtor da superclasse. Na linha 26 areferência ao gerente de filas é atribuída à variável da instância. As linhas 28 a50 contém o código do método run() que é o método de entrada do thread. Nalinha 29 é obtido o nome do thread corrente que será usado na impressão demensagens. Na linha 30 o método

Thread.currentThread().getThreadGroup();

obtém uma referência para o grupo de threads ao qual pertence o thread corrente.Na linha 31 é declarada uma variável que irá referenciar um vetor contendoreferências a todos os threads ativos do grupo. Entre as linhas 32 e 44 éexecutado um laço com 100 iterações que produz e armazena as mensagens. Nalinha 34 é realizado um teste para a verificação da necessidade de criar o vetorque irá conter as referências para os threads ativos. Ele deve ser criado aprimeira vez e toda vez que a capacidade do vetor for diferente do número dethreads ativos do grupo. O tamanho do vetor é determinado pelo método deinstância activeCount() da classe ThreadGroup. A linha 36 contém ocódigo

tg.enumerate(tl);

que atribui as referencias aos threads no vetor. O comando

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

da linha 37 calcula um número que será usado para acessar o thread dentro dovetor de referências. O teste da linha 38 impede que seja enviada uma mensagempara o próprio gerador. Essas mensagens são descartadas. As linhas 40 a 42tratam da impressão e envio da mensagem construída. Já fora da iteração, aslinhas 45 a 49 tratam da do envio da mensagem “fim” para todos os threadsreceptores, o que fará com que encerrem sua execução.

Page 41: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

40

As linhas 53 a 64 definem a classe que será usada como ponto de entradada aplicação. Ela é responsável pela criação dos objetos e disparos dos threads.No exemplo, além do thread gerador apenas dois threads receptores são criados.É interessante notar que não é preciso indicara para o thread gerador asreferências para os threads receptores. Elas são obtidas dinamicamente por meiodo grupo de threads.

Um trecho possível da saída obtida na execução do programa do exemploIX.15 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 99Pressione qualquer tecla para continuar . . .

Otimizando a Programação Multithread

Existe um problema óbvio com a abordagem do exemplo IX.14: amensagem é dirigida a apenas um thread mas todos serão notificados,sobrecarregando o sistema, uma vez que todos os threads precisaram testar se amensagem é destinada a eles. Para contornar esses problema é necessáriovislumbrar uma forma de notificar apenas o thread destinatário.

Essa solução pode ser obtida se cada thread esperar em um monitor deum objeto diferente. Não importa o tipo do objeto desde que seja referenciávelpelo thread receptor e pelo thread que irá armazenar a mensagem. Um candidatonatural é a fila de mensagem de cada thread. Existe uma fila para cada thread e o

Page 42: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

41

thread que armazena a mensagem tem acesso a todas a filas por meio da tabelahash. O exemplo IX.16 mostra uma versão do exemplo IX.14 que utiliza estatécnica para criar uma aplicação multi-thread mais otimizada.

import java.util.*;

class GerenteMen { private Hashtable tamMen;

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

// Método para adicionar uma mensagem à fila de // um destinatário public void addMen(String dest, String men){ if (dest==null || men==null) return; Vector listaMen = (Vector) tamMen.get(dest); if (listaMen==null) listaMen = new Vector(); synchronized (listaMen) { listaMen.addElement(men); tamMen.put(dest, listaMen); listaMen.notify(); }; }

// Método para obtenção da mensagem public String getMen(String dest) throws Exception { if (dest==null) return null; Vector listaMen = (Vector) tamMen.get(dest);

// Se não existe a fila para esse thread cria uma vazia if (listaMen==null) { listaMen = new Vector(); tamMen.put(dest, listaMen); } // A fila está vazia, portanto thread deve esperar while(listaMen.size()==0) synchronized (listaMen) {listaMen.wait();} String temp = (String) listaMen.firstElement();

// A mensagem é removida da fila listaMen.removeElementAt(0); return temp; }}

Page 43: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

42

Exemplo IX.16 – Gerenciador de mensagens otimizado.

Note que os métodos wait() e notify() invocados pertencem à filarelacionada com cada thread. O exemplo IX.15 pode ser usado semmodificações para testar essa versão.

Criando outros mecanismos de sincronização

Existem várias propostas de primitivas de sincronização. Dentre as maiscomuns podemos citar os semáforos, mutex, variáveis condicionais, monitores eencontros (rendevouz). Cada uma dessas primitivas é mais adequada a umdeterminado propósito. A implementação de monitores na linguagem Java,juntamente com os métodos wait() e notify() que formam um tipo devariáveis condicionais podem ser combinadas para implementar muitas dessasoutras primitivas, de modo a atender objetivos específicos. Para exemplificaressa possibilidade mostraremos como implementar um semáforo usando asprimitivas de sincronização da linguagem Java.

Um semáforo é uma variável inteira sobre a qual pode-se realizar asseguintes operações:

Operação Descriçãoinicializar Um valor inteiro maior ou igual a zero é atribuí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 umvalor maior que zero.

V Incrementa o semáforo e acorda os threads que estiverembloqueados na fila de espera do semáforo.

Tabela IX.6 –Operações sobre um semáforo.

Semáforo é um mecanismo de sincronização muito utilizado quandoexiste a necessidade de comunicação entre dois ou mais processos, como no casode sistemas do tipo produtor/consumidor. Por exemplo, suponha dois processosonde um coloca mensagens em buffer e outro retira as mensagens. Os processospodem usar dois semáforos para sincronizar o acesso ao buffer de mensagens:um para controlar a entrada na região crítica e outro para contar o número demensagens. A figura IX.xx mostra os esquemas dos processos. A implementaçãodos semáforos em Java pode ser visto no exemplo IX.xx e o uso dos semáforos

Page 44: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

43

em uma situação como a ilustrada pela figura IX.xx pode ser visto no exemploIX.xx.

Produtor Consumidor

s=1; n=0;

início loop início loop

Produz mensagem P(n) // Verifica se existe mensagens

P(s) // Verifica se pode entrar na região

// crítica

P(s) // Verifica se pode entrar na região

//crítica

Coloca mensagem no buffer Retira mensagem

V(n) // incrementa no, de mensagens V(s) // sai da região crítica

V(s) // sai da região crítica Consome Mensagem

fim loop fim loop

Figura IX.xx – 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 InterruptedException { while(cont <=0) this.wait(); cont--; }

public synchronized void V() { cont++; notifyAll(); }}

Exemplo IX.XX – Implementação de um Semáforo.

Page 45: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

44

import java.util.Vector;

class Consumidor extends Thread{ private Vector buff; private Semaforo s,n;

public Consumidor(Vector aBuff, Semaforo as, Semaforo an) { super(); buff = aBuff; s = as; n = an; } public void run() { for (;;) try { n.p(); // Verifica se existe mensagens s.p(); // Verifica se pode entrar na região crítica String men = (String)buff.firstElement(); buff.removeElementAt(0); s.v(); if (men.equals("fim")) return; System.out.println("Mensagem recebida:"+men); } catch(Exception e) {System.out.println(e.getMessage());} }}

class Produtor extends Thread{ private Vector buff; private Semaforo s,n; public Produtor(Vector aBuff, Semaforo as, Semaforo an) { super(); buff = aBuff; s = as; n = an; } public void run() { for (int i=0;i<11;i++) { try { s.p();// Verifica se pode entrar na região crítica if (i<10) { buff.addElement(""+i);

Page 46: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

45 System.out.println("Mensagem enviada: "+ i); } else buff.addElement("fim"); n.v(); // incrementa o número de mensagens s.v(); // abandona a região crítica Thread.yield(); } catch(Exception e) {System.out.println(e.getMessage());} } }}

public class TestaSemaforo{ public static void main(String args[])throws Exception { Vector buff = new Vector(); Semaforo s = new Semaforo(1); Semaforo n = new Semaforo(0); Produtor t1 = new Produtor(buff,s,n); Consumidor t2 = new Consumidor (buff,s,n); t1.start(); t2.start(); }}

Exemplo IX.XX – Uso de semáforos por dois threads.

Page 47: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

46

Capítulo II - AnimaçãoAnimação é exibir uma figura que muda com o tempo. No momento o

suporte a animação da API central do Java é limitado. Espera-se para o final de1997 uma API que dê suporte avançado para animação. A animação pode sercontrolado por um thread que é executado em um certo intervalo pré-definido.

Exemplo básico de animação “in-place”.

import java.awt.*; import java.applet.Applet;

public class exemplo10 extends Applet implements Runnable{ Image imgs[]; int ind=0; Thread t1;

public void init(){imgs = initImgs(); t1=new Thread(this); t1.start();}

public void paint(Graphics g){g.draw.Image(imgs[ind],0,0,this);}

public void start() {if (t1 == null) { t1 = new Thread(this);

t1.start();} }

public void stop() {if (t1 != null) {t1.stop();t1 = null;}

}

public void run() {while (true){ try {Thread.sleep(100};} catch(InterruptedException ex){} repaint(); ind=++ind % imgs.length;}

}}

Problemas com o exemplo

Page 48: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

47

Ele não permite interromper a animação. O repaint() chama oupdate() default que repinta todo o fundo, o que causa “flicker” na animação.Existe um problema relacionado com a integridade da variável ind. A variável éatualizada pelo Thread t1 (run) e lida pelo Thread update (paint).

permitir interromper a animação.

boolean pause = false;

public boolean mouseDown(Event e, int x, int y){ if (pause) {t1.resume();}

else {t1.suspend();}pause = !pause;return true;

}

Eliminar o “flicker”

Default

public void update(Graphics g){

g.setColor(getBackground());g.fillRect(0,0,width, height);g.setColor(getForeground());paint(g);

}

Mudança

public void update(Graphics g) {paint(g);}

Eliminando conflitos

public synchronized void paint(Graphics g){

g.draw.Image(imgs[ind],0,0,this);}

public synchronized void mudaInd()

Page 49: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

48

{ind = ++ind % imgs.length;

}

Copiando a figura

public void drawStickFigure (Graphics g, int nX, int nY){ g.drawOval (nX + 10, nY + 20, 20, 40); g.drawLine (nX + 20, nY + 60, nX + 20, nY + 100); g.drawLine (nX + 10, nY + 70, nX + 30, nY + 70); g.drawLine (nX + 10, nY + 150, nX + 20, nY + 100); g.drawLine (nX + 20, nY + 100, nX + 30, nY + 150);}public void paint (Graphics g, Applet Parent){ if (bFirstTime) { bFirstTime = false; drawStickFigure (g, nX, nY); } else { g.copyArea (nX, nY, 35, 155, 5, 0);}}

Double-buffer

offScreenImage = createImage (nWidth, nHeight);offScreenGraphic = offScreenImage.getGraphics();

...offScreenGraphic.setColor (Color.lightGray);offScreenGraphic.fillRect (0, 0,nWidth, nHeight);offScreenGraphic.setColor (Color.black);

...offScreenGraphic. drawOval(10,10,20,20); ...g.drawImage (offScreenImage, 0, 0, this);

Ticker-Tape

class TextScrolling extends AnimationObject{ String pcMessage; // The message int nXPos; // The location of the message int nYPos; // The location of the message int nAppletWidth; // The width of the applet int nMessageWidth; // The width of the message

public TextScrolling (String pcMsg, int nWide)

Page 50: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

49{

pcMessage = pcMsg;nAppletWidth = nWide;nMessageWidth = -1;nYPos = -1;nXPos = 0;

}

public void paint (Graphics g, Applet parent){ if (nYPos < 0) {

nYPos = (g.getFontMetrics ()).getHeight ();

char pcChars []; pcChars = new char [pcMessage.length() + 2]; pcMessage.getChars(0, pcMessage.length()- 1, pcChars, 0); nMessageWidth = (g.getFontMetrics ()).charsWidth (pcChars, 0, pcMessage.length()); }

g.drawString (pcMessage, nXPos, nYPos);}public void clockTick (){ if (nMessageWidth < 0) return;

// Move Right nXPos -= 10; if (nXPos < -nMessageWidth)

nXPos = nAppletWidth - 10;}public void run(){ int ndx = 0;

Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

while (size().width > 0 && size().height > 0&& kicker != null)

{AnimatedObjects[0].clockTick ();

repaint(); try {Thread.sleep(nSpeed);}

catch (InterruptedException e){} }}

Page 51: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

50

Capítulo III - Programação em redeDiferentemente das linguagens mais populares atualmente, Java foi

projetada na era da Internet, e por isso mesmo ferramentas para comunicaçãodentro da Grande Rede foram incorporadas à linguagem desde a sua concepção.Classes para manipulação de URLs e dos protocolos que constituem a Internetfazem parte do núcleo básico da linguagem. Isto facilita muito a tarefa dedesenvolver aplicações que para a Internet ou outras redes que fazem uso domesmo conjunto de protocolos. Esta é uma das principais forças da linguagemJava. De modo a entendermos como desenvolver aplicações em rede com Java éimportante o compreensão de alguns conceitos básicos sobre protocolos decomunicação.

Conceitos Sobre Protocolos Usados na Internet

Um protocolo de comunicação é um conjunto de formatos e regrasusadas para transmitir informação. Computadores distintos devem obedecerestas regras e formatos de modo que se possam comunicar. Podemos encarar oprotocolo como a definição de uma linguagem comum de modo a possibilitar acomunicação entre diferentes entidades.

Visando diminuir a complexidade de implementação e uso do protocolo,ele é divido e organizado em forma de camadas de protocolos, onde a camadarelativamente inferior na pilha a outra estabelece as regras para a camadasuperior sobre a utilização de seus serviços. As camadas inferiores fornecemserviços mais básicos de transmissão de dados, enquanto que as camadassuperiores oferecem serviços de mais alto nível. Esta forma de organizaçãohierárquica de protocolos é também chamada de pilha de protocolos.

A principal pilha de protocolo sobre o qual a Internet se organiza é oTCP/IP. Por simplicidade chamaremos a pilha de protocolos TCP/IP apenascomo protocolo TCP/IP ou TCP/IP. A figura XI.1 mostra como se organizamalguns dos protocolos que fazem parte do TCP/IP.

A camada física é a responsável pela transporte efetivo dos dados sobre omeio físico. A camada de rede é responsável pela interface lógica entre oscomputadores. A camada de transporte provê transferência de dados a nível de

Page 52: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

51

serviço, e a camada de aplicação provê comunicação a nível de processos ouaplicações. Exemplos de protocolos a nível de aplicação são: FTP, usado paratransferência de arquivos; HTTP, usado para transmissão de páginas Web;TELNET provê capacidade de log-on remoto; SMTP provê serviços básicos decorreio eletrônico; SNMP usado para gerência da rede; e MIME que é umaextensão do SMTP para lidar com mensagens com conteúdos diversos.

Figura XI.1 –Alguns protocolos da pilha TCP/IP.

No processo de transmissão de dados sobre uma rede TCP/IP os dadossão divididos em grupos chamados de pacotes. Cada camada adiciona um algunsdados a mais no início de cada pacote para permitir que o pacote chegue aodestino. Os dados adicionados são chamados de headers.

Ethernet, X.25, Token RingCamada física

IPCamada de rede

UDPTCPCamada de Transporte

SNMPFTP HTTP SMTP TELNET

MIME

Camada de Aplicação

FTP - File Transfer Protocol SMTP - Simple Mail Transfer ProtocolHTTP - Hypertext Transfer Protocol SNMP - Simple Network Management ProtocolIP - Internet Protocol TCP - Transmission Control ProtocolMIME - Multi-purpose Internet Mail Extensions UDP - User Datagram Protocol

Page 53: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

52

Figura XI.2 –Headers adicionados a cada camada de protocolo.

Na camada de transporte existem dois protocolos que fazem uso doprotocolo IP: o protocolo TCP/IP e o UDP.

TCP

O protocolo TCP é um protocolo orientado a conexão que provê umfluxo confiável de dados entre dois computadores. Por protocolo orientado aconexão queremos dizer que é estabelecido um canal de comunicação ponto-a-ponto onde os dados podem trafegar em ambas as direções. O TCP garante queos dados enviados em uma ponta cheguem ao destino, na mesma ordem queforam enviados. Caso contrário, um erro é reportado. Protocolos como HTTP,FTP e TELNET exigem um canal de comunicação confiável e a ordem derecebimento dos dados é fundamental para o sucesso dessas aplicações.

UDP

No entanto, nem todas as aplicações necessitam destas características doprotocolo TCP e o processamento adicional exigido para garantir aconfiabilidade e a ordenação dos dados podem inviabilizá-las. Para esses casosexiste o protocolo de transporte UDP. UDP é um protocolo para envio depacotes independentes de dados, chamados de datagramas, de um computador aoutro, sem garantias sobre a chegada dos pacotes. O protocolo UDP não éorientado a conexão.

DadosTCPHeader

Dados

Dados Aplicação

DadosTCPHeader

Transporte

TCPHeader

IPHeader

Rede

IPHeader

EthernetHeader

Física

Page 54: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

53

IDENTIFICAÇÃO DE HOSTS (Número IP)

Cada computador conectado a uma rede TCP/IP é chamado de Host e éidentificado por um único número de 32 bits, denominado de número IP. Onúmero IP é representado por quatro grupos de 8 bits, limitando desta forma ovalor numérico de cada grupo ao valor máximo de 255. Um exemplo de númeroIP é 200.65.18.70. Uma vez que é muito difícil lembrar e atribuir significado anúmeros existe uma forma alternativa de identificar os computadores da rede pormeio de nomes. Um ou mais computadores da rede fazem o papel deresolvedores de nomes, mantendo bases de dados que associam o nomes doHosts à seus números IP. Desta forma é possível um computador comunicar comoutro computador por meio do nome lógico e não por meio do número IP. Afigura XI.3 ilustra a comunicação entre dois computadores. A Internet érepresentada como uma nuvem devido a complexidade da rede.

Figura XI.3 –Representação da comunicação entre dois computadores.

O representação dos nomes dos computadores na Internet é feita porsubstrings separadas por ‘.’ e obedecem uma regra de nomeação que define queo primeiro substring representa o nome da máquina, e os restantes representa odomínio onde está inserida a máquina. Um domínio é um agrupamento decomputadores que pertencem a uma instituição, órgão, empresa, ou umaorganização qualquer. Assim, no exemplo da figura XI.3 o computadormeucomp pertence ao domínio com.br. No Brasil a FAPESP (Fundação deAmparo à Pesquisa do Estado de São Paulo) responsável pela gerência dosnomes dos domínios na Internet.

Host meucomp.com.brIP: 200.18.46.12

Host outrocomp.eduIP: 205.50.30.75

Page 55: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

54

Identificação de Processos (Portas)

A comunicação entre dois processos em uma rede TCP/IP é assimétrica,no sentido um processo faz o papel de servidor, oferecendo um serviço e outrofaz o papel de cliente do serviço. Em um único Host vários processos podemestar fazendo o papel de servidor, oferecendo serviços através de um único meiofísico. Portanto, é preciso uma forma de identificar as servidores em um mesmoHost. Isto é feito por meio da associação de um número inteiro, chamado deporta, ao processo servidor. Essa associação é feita pelo processo assim que écarregado, por meio de uma chamada ao sistema. O número da porta pode variarde 1 a 65535, no entanto os números de 1 a 1023 são reservados para serviçosconhecidos como FTP e HTTP. O programador não deve usar estas portas a nãoser que esteja implementando algum desses serviços. Nos ambientes Unix asportas que vão de 6000 a 6999 são usadas pelo gerenciador de Interfaces XWindows 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. A tabela XI.1mostra o número da porta de alguns dos serviços mais conhecidos.

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

Tabela XI.1 – Número das portas dos principais serviços.

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

Programação em Rede com Java

O pacote java.net contém as classes e interfaces usadas paraprogramação de sistemas em rede com Java. As classes podem ser enquadradasem três categorias:

Page 56: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

55

1. Classes para comunicação básica em rede. Tratam da comunicaçãoem baixo nível entre aplicações. Outros protocolos podem serimplementados usando como base esta comunicação básica.

2. Classes para comunicação dentro da Web. Estas classes provêemfacilidades para acessar conteúdos por meio de URLs.

3. Classes para tratamento dos formatos estendidos da Web. Utilizadaspara tratar novos protocolos e tipos MIME.

Comunicação Básica Entre Aplicações

As classes Socket, ServerSocket, DatagramSocket,DatagramPacket e InetAddress, fornecem os métodos necessários paraa comunicação básica entre dois processos. A tabela XI.2 descreve sucintamentecada uma das classes.

Classe 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 XI.2 – Classes para comunicação básica.

As classes Socket e ServerSocket são utilizadas para comunicaçãoorientada à conexão, enquanto que as classes DatagramSocket eDatagramPacket são utilizadas para comunicação não orientada à conexão.

Comunicação orientada à conexão (cliente)

Para comunicar via protocolo TCP é preciso que a aplicação cliente crieum objeto Socket. É preciso passar o nome ou número IP do Host e o númeroda porta onde o servidor está esperando as solicitações de serviço. A ClasseSocket possui os métodos getInputStream() e

Page 57: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

56

getOutputStream(), que são usados para obter Streams associados aoSocket. Deste modo, a transmissão de dados via Socket é idêntica à leitura eescrita em arquivos via Streams. O exemplo XI.1 mostra o código de um clienteque acessa um servidor de Daytime. O serviço de Daytime é disponibilizado nasplataformas UNIX e é acessado via porta 13. Sua função enviar, aos processosclientes, uma linha de texto contendo a data e a hora corrente .

import java.io.*;import java.net.*;

public class ClienteData {public static void main(String[] args) throws IOException{

Socket socket = null;BufferedReader in = null;

try {socket = new Socket(args[0], 13);in = new BufferedReader(new InputStreamReader(

socket.getInputStream()));}catch (UnknownHostException e){System.err.println("Não achou o host:"+args[0]);

System.exit(1);} catch (IOException e){System.err.println("Erro de I/O."+e.getMessage());

System.exit(1);}

System.out.println("Data: " + in.readLine());}

in.close();socket.close();

}}

Exemplo XI.1 – Cliente para o serviço de Daytime.

No programa do exemplo XI.1 o usuário precisa passar o nome do Hostservidor pela linha de comando. Você pode testar este programa mesmo que seucomputador não esteja conectado em uma rede, desde que o protocolo TCP/IPesteja instalado. É que você pode passar como parâmetro o nome do seucomputador ou, alternativamente, o nome localhost, ainda o número IP127.0.0.0. O nome localhost e o número IP127.0.0.0 sempre identificam ocomputador local. Note que após a criação do objeto Socket, o métodogetInputStream() é chamado e o objeto retornado é envolvido por um

Page 58: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

57

objeto BufferedReader de modo a comunicar dados via rede da mesma formaque é realizada uma operação de E/S. Ao se terminar a operação é preciso fechartanto a instância BufferedReader quanto o objeto Socket. A instância da Classede E/S sempre deve ser fechada primeiro.

Os Hosts que utilizam as variações do sistema operacional UNIXpossuem o serviço de Daytime, no entanto, outros sistemas operacionais podemnão implementar este serviço. O programador pode resolver este problemaimplentando ele mesmo um servidor de Daytime. A próxima seção mostrarácomo isto pode ser feito.

Comunicação orientada à conexão (servidor)

Para criar um processo servidor é preciso associá-lo à uma porta. Isto éfeito ao se criar uma instância da classe ServerSocket. Se estamos criando umnovo serviço é preciso associar a uma porta com valor maior que 1023. Seestamos implementando um serviço já estabelecido é preciso obedecer asespecificações definidas para o serviço. O exemplo XI.2 mostra o código de umservidor do serviço Daytime. Como não queremos substituir o serviço padrão deDaytime utilizaremos o número de porta 5013 no lugar do número 13. Para setestar este servidor com o programa cliente do exemplo X.1 é preciso alterar onúmero da porta no código do cliente.

Page 59: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

58

import java.util.*;import java.io.*;import java.net.*;

public class ServerData {public static void main(String[] args) throws IOException{

SeverSocket ssocket = null;Socket socket = null;BufferedWriter out = null;

ssocket = new SeverSocket (5013,5);for(;;){

socket = ssocket.accept();out = new BufferedWriter (new OuputStreamWriter (

socket.getOuputStream()));out.write((new Date()).toString()+”\n”);

}

out.close();socket.close();

}}

Exemplo XI.2 – Servidor de Daytime.

Ao criar uma instância da classe ServerSocket o programador podeindicar o tamanho da fila de solicitações de conexão. As conexões são colocadasna fila até que o servidor possa atende-las. Se chegar alguma conexão e nãohouver espaço na fila a conexão será recusada. No nosso exemplo passamoscomo parâmetro o valor 5. Após criar o objeto ServerSocket o servidor deveindicar que está disposto a receber conexões. Isto é feito por meio da execuçãodo método accept() do objeto ServerSocket. Ao executar este método oprocesso passa para o estado bloqueado até que alguma conexão seja solicitada.Quando a conexão é solicitada o método accept() retorna um objeto Socketigual ao do processo cliente, que será usado para obter os Streams onde seráefetuada a comunicação. O Stream obtido do objeto Socket é encapsulado emobjeto BufferedWriter que será encarregado de enviar a cadeia de caracterescontendo a data e a hora para o cliente. O Servidor em implementa um laçoinfinito, recebendo e tratando solicitações de serviços.

Page 60: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

59

O servidor implementado acima trata uma solicitação de serviço por vez.Isto pode ser problemático quando existem vários clientes solicitando serviçosao mesmo tempo e o servidor leva um longo tempo tratando cada solicitação.Nesta situação o cliente pode ficar um longo tempo a espera de atendimento. Istopode ser remediado por meio da implementação de servidores Multithreaded.Apesar do serviço implementado pelo exemplo XI.2 não exigir um servidorMultithreaded, uma vez que o cliente é atendido rapidamente, o servidorDaytime foi alterado para exemplificar a implementação de um servidorMultithreaded. O exemplo XI.3 mostra o código da versão Multithreaded doservidor.

import java.util.*;import java.net.*;import java.io.*;

public class ServerData{

public static void main(String args[]){

ServerSocket ssocket=null;

try { ssocket = new ServerSocket(pt); }catch(Exception e) {System.err.println(e);

System.exit(1);}

while(true){

try{ Socket socket = ssocket.accept(); (new serversec(socket)).start();}catch(Exception e) {System.err.println(e);}

}}

}

class serversec extends Thread{

Socket socket;

public serversec(Socket aSocket) {socket = aSocket;}

public void run(){

Page 61: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

60try{

BufferedWriter out = new BufferedWriter (newOuputStreamWriter ( socket.getOuputStream()));

out.write((new Date()).toString()+”\n”);out.flush();out.close();socket.close();

}catch(Exception e) {System.err.println(e);}

}}

Exemplo XI.3 – Servidor de Daytime Multithreaded.

No exemplo XI.3, ao receber uma conexão o servidor cria um Threadpara atender o cliente e fica disponível para receber novas solicitações. Esseexemplo pode ser usado como esqueleto para desenvolvimento de servidoresmais complexos, porém, neste caso é necessário limitar o número de Threadsque podem ser criados.

Comunicação Sem Conexão (UDP)

Como já dissemos na seção anterior, nem sempre é necessário um canalde comunicação confiável entre duas aplicações. Para estes casos existe oprotocolo UDP, que provê uma forma de comunicação onde a aplicação enviapacotes de dados, chamados de datagramas, para outra aplicação, sem garantiasse e quando a mensagem vai chegar, nem se o conteúdo está preservado.

As portas do protocolo UDP obedecem a mesma distribuição das TCPporém são distintas uma da outra, de modo que o programador pode associaruma porta TCP de um determinado número à uma aplicação em um host e omesmo número de porta UDP a outra aplicação no mesmo host.

As classes DatagramPacket e DatagramSocket contém osmétodos necessários para realizar este tipo de comunicação. Para ilustrar o usodestas classes modificaremos os exemplos XI.1 e XI.2 para implementarmosuma aplicação Cliente/Servidor Daytime que usa o protocolo UDP. O ExemploXI.4 mostra o código fonte do cliente e o Exemplo XI.5 mostra o código doservidor.

Page 62: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

61

Analisando o código da aplicação cliente podemos notar que é necessáriocriar um objeto da classe DatagramPacket para representar os Datagrama ondeos dados são armazenados. No nosso exemplo colocamos também os dados doHost e a porta, porém estes dados poderiam ser omitidos na construção doDatagrama e serem passados somente no envio/recepção do pacote ou naconstrução do DatagramSocket. O Datagrama deverá armazenar os dadosenviados pelo servidor e foi dimensionado para conter 64 bytes. O métodoreceive() do objeto DatagramSocket aguarda o recebimento do pacote,tendo como argumento o objeto da classe DatagramPacket. Após o recebimentodo pacote os dados são convertidos para String e exibidos na saída padrão.

O servidor, diferentemente do servidor TCP, precisa saber a quem deveenviar os pacotes, uma vez que não é estabelecida uma conexão. Podíamossimplesmente passar o nome do host pela linha de comando, mas resolvemosadotar uma estratégia de Broadcasting. Nesta abordagem os datagramas sãoenviados a vários computadores e não apenas um. Isto é feito passando-se comoargumento para o método InetAddress.getByName() o endereço deBroadcast da rede.

import java.io.*;import java.net.*;

public class DataClienteUDP{ public static void main(String args[]) throws Exception { if (args.length != 1) { System.err.println("Uso: java DataClienteUDP host"); System.exit(1); } byte [] buff = new byte[64]; DatagramSocket ds = new DatagramSocket(); DatagramPacket dp = new DatagramPacket(buff, buff.length, InetAddress.getByName(args[0]),5013); ds.receive(dp); String s = new String(dp.getData()); System.out.println("Data e hora recebidade”+dp.getAddress()+ " : "+s); }}

Page 63: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

62

Exemplo XI.4 – Cliente Daytime UDP.

import java.io.*;import java.net.*;

public class DataServerUDP{ public static void main(String args[]) throws Exception { DatagramSocket ds; DatagramPacket dp; InetAddress addr = InetAddress.getByName(“255.255.255.0”); ds = new DatagramSocket();

byte [] buff; for (;;) { Thread.sleep(1000); String s = (new Date()).toString(); buff = s.getBytes(); dp = new DatagramPacket(buff, buff.length, addr, 5013); ds.send(dp); } }}

Exemplo XI.5 – Servidor Daytime UDP.

O tipo de endereço de Broadcast depende da classe de endereçamento IPda rede. O endereço usado no exemplo IX.5 funciona para a classe deendereçamento C. Para descobrir que tipo de classe pertence a rede onde está seucomputador olhe o primeiro byte do endereço IP de sua máquina e verifiquejunto a tabela XI.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 XI.3 – Classes para comunicação básica.

Page 64: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

63

O envio dos dados é feito pelo método send() do objetoDatagramSocket, tendo como argumento um objeto da classe DatagramPacket.Note que não é necessário um “Socket servidor” uma vez que o servidor enviaos pacotes independentemente de existir clientes solicitando-os.

Comunicação por meio de URL

URLs

Um URL (Uniform Resource Locator) é uma referência (um endereço) aum recurso na Internet. O URL é dividido em partes, sendo que apenas aprimeira parte é obrigatória. A maioria das URLs é dividida em três partes:

Informação sobre o Host

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

A parte do protocolo define o protocolo que deve ser usado para acessaro recurso. Os protocolos mais comuns são FTP, HTTP e file, este últimoindicando que o recurso se encontra no sistema de arquivos local. O protocolo éseguido do caractere “:”.

A parte com informação sobre o Host fornece a informação necessáriapara acessar o Host onde está localizado o recurso. Esta parte é omitida caso orecurso esteja no sistema de arquivos local. A informação sobre o Host éprecedida por duas barras (“//”), no caso de aplicação na Internet e apenas poruma barra (“/”), caso contrário. A informação sobre o Host também pode serdividida em três partes: a) o nome do domínio do Host; b) o nome e senha dousuário para login; e c) o número da porta caso 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

111122223333

666677778888123

endereço do recurso no HostProtocolo

Page 65: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

64

A última parte de uma URL representa o caminho até o recurso nosistema de arquivos do Host. Esta seção é separada da seção anterior por umabarra simples (“/”).

Manipulando URLs em Java

A linguagem Java fornece as seguintes classes para manipulação deURLs:

Classe DescriçãoURL Representa um URLURLConnection 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.

Tabela XI.3 – Classes para manipulação de URLs.

Um objeto URL é criado passando como parâmetro para o construtor oURL na forma de String:

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

Um objeto URL pode ser construído passando como parâmetro outroURL 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 URL comprimeiro construtor, passando como parâmetro o endereço:

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

Os construtores geram a exceção MalformedURLException, se oURL é inválido. Portanto, o programador deve providenciar o código para acaptura e tratamento desta exceção.

Page 66: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

65

De posse de um objeto URL é possível obter um objeto InputStream paraler os dados endereçados pelo URL, utilizando o método openStream() doobjeto URL. O Exemplo XI.4 mostra o código de um programa que pode serusado para listar na saída padrão o conteúdo de um URL passado pela linha decomando.

import java.net.*;import java.io.*;

public class LeURL{ public static void main(String[] args) throws Exception { if (args.length < 1) { System.err.println("uso: java LeURL <URL>..."); System.exit(1); } URL url = new URL(args[0]); BufferedReader in = new BufferedReader( new InputStreamReader(url.openStream()));

String linha;

while ((linha = in.readLine()) != null) System.out.println(linha);

in.close(); }}

Exemplo XI.4 – Leitor de URL.

Comunicando por meio de URLConnection

O programador pode utilizar o método openConnection() do objetoURL para obter uma conexão entre a aplicação é o recurso referenciado pelo oURL. O método openConnection() retorna um objeto URLConnection,que permite que a aplicação escreva e leia através da conexão. Alguns URLs,como os conectados à scripts CGI2 (Common-Gateway Interface), permitem que

2 Common-Gateway Interface (CGI) é um mecanismo para gerar páginas Web dinamicamente.Os dados são obtidos de fórmulários HTML e submetidos a um programa binário no servidor

Page 67: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

66

aplicação cliente escreva informação no URL. A saída do programa CGI podeser interceptada pelo programa Java de modo que possa ser exibida para ousuário. Esta forma de comunicação com scripts CGI é melhor do que por meiode formulários HTML, uma vez que o usuário não precisa navegar entre páginaspara visualizar os formulários e a resposta retornada. O Applet Java se encarregade enviar os dados e exibir o resultado na mesma página. Para ilustrar esta formade comunicação mostraremos um programa que submete uma cadeia decaracteres a um URL para que seja invertido e enviado de volta. O script CGIutilizado, escrito em Perl é exibido no exemplo XI.5 e foi escrito por HassanSchroeder, um membro da equipe de desenvolvimento da linguagem Java. EsteCGI pode ser acessado no URL http://java.sun.com/cgi-bin/backwards.

#!/opt/internet/bin/perlread(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});@pairs = split(/&/, $buffer);foreach $pair (@pairs){ ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; # Stop people from using subshells to execute commands $value =~ s/~!/ ~!/g; $FORM{$name} = $value;}

print "Content-type: text/plain\n\n";print "$FORM{'string'} reversed is: ";$foo=reverse($FORM{'string'});print "$foo\n";exit 0;

Exemplo XI.5 – Script backwards para inverter uma cadeia de caracteres.

O exemplo XI.6 contém o programa que envia uma cadeia de caracteresao URL e recebe de volta outra cadeia de caracteres que é a inversão daprimeira. A ação necessária é codificar a cadeia de caracteres por meio dométodo estático encode() da classe URLEncoder:

que gera a resposta na forma de uma página Web. O programa pode ser escrito em umavariadade delinguagens, como Perl e C.

Page 68: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

67

String string = URLEncoder.encode(args[0]);

Isto é necessário porque a string enviada a um URL necessita de umacodificação particular, como por exemplo, os espaços em branco sãosubstituídos pelo caractere “+”, os campos são separados pelo caractere “&”evalor do campo é separado do nome do campo pelo caracteres “=”.

Em seguida o programa cria um objeto URL relacionado com o endereçoonde se encontra o script, abre uma conexão e define que será usada para entradae saída.

URL url = new URL("http://java.sun.com/cgi-bin/backwards");URLConnection c = url.openConnection();c.setDoOutput(true);

Neste momento o programa está preparado para trabalhar com o URLcomo se fosse um Stream. O Stream para escrever no URL é obtido do objetoURLConnection por meio do método getOutputStream() e o Streampara ler do URL é obtido do objeto URLConnection por meio do métodogetInputStream(). Primeiro o programa submete a cadeia de caracteres aser invertida precedida pelo nome do campo e pelo caractere “=”:

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

import java.io.*;import java.net.*;

public class Inverte{ public static void main(String[] args) throws Exception {

if (args.length != 1) { System.err.println("Uso: java Inverte string"); System.exit(1); }

String string = URLEncoder.encode(args[0]);

URL url = new URL("http://java.sun.com/cgi-

Page 69: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

68bin/backwards"); URLConnection c = url.openConnection(); c.setDoOutput(true);

PrintWriter out = new PrintWriter(c.getOutputStream()); out.println("string=" + string); out.close();

BufferedReader in = new BufferedReader(newInputStreamReader( c.getInputStream())); String retorno;

while ((retorno = in.readLine()) != null) System.out.println(retorno);

in.close(); }}

Exemplo XI.6 – Programa que escreve em um URL.

Após isso o Stream de saída é fechado e o Stream de entrada é aberto. Acadeia de caracteres invertida é lida e exibida no dispositivo de saída padrão.

No exemplo apresentado o script CGI usa o POST METHOD para lerdados enviados pelo cliente. Alguns scripts CGI usam o GET METHOD para lerdados do cliente, no entanto, este último está ficando rapidamente obsoletodevido a maior versatilidade do primeiro.

Page 70: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

69

Capítulo IV – Computaç ãoDistribuída (RMI)

A RMI (Invocação de métodos remotos) é uma tecnologia que coloca aprogramação com rede em um nível mais alto. RMI torna possível que objetosdistribuídos em uma rede se comuniquem de forma transparente para oprogramador utilizando chamadas de procedimentos remotos.

O principal objetivo da tecnologia RMI é permitir que programadoresdesenvolvam programas distribuídos utilizando a mesma sintaxe e semântica deprogramas Java convencionais. Antes da introdução da RMI no mundo Java, noJDK 1.1, para fazer com que dois objetos em máquinas diferentes secomunicassem o programador deveria definir um protocolo de comunicação eescrever código utilizando socket para implementar este protocolo. Com RMI amaior parte do trabalho quem realiza é a máquina virtual Java.

Existem outras tecnologias, como CORBA (Common Object RequestBrocker Architecture), que também tem como objetivo fazer com que objetosdistribuídos em uma rede se comuniquem. Java também tem suporte a CORBA,mas para projetos em um ambiente Java puro, a RMI é consideravelmente maissimples que a CORBA.

Criando nossa agenda distribuída

De modo exemplificar o uso de RMI modificaremos a agenda distribuídafazendo uso desta tecnologia. Os passos a serem seguidos são:

1. Escrever e compilar a interface que descreve a como serão as chamadasdo cliente ao servidor;

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

3. Gerar Stubs e Skeleton do objeto distribuído;4. Desenvolver o código que disponibiliza o objeto;5. Escrever e compilar o código para o cliente RMI; e6. Testar.

Implementar interface do objeto remoto

Page 71: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

70

O primeiro passo quando se deseja criar um objeto Remoto com RMI éimplementar uma interface para este Objeto. Essa interface deve herdar ainterface Remote. É através dessa interface Remote, que não tem métodos, que amáquina virtual Java sabe qual objeto pode ser disponibilizado para acessoremoto. Abaixo temos o exemplo da interface do nosso objeto:

12345678

public interface Agenda extends java.rmi.Remote{ public void inserir(Pessoa p) throws java.rmi.RemoteException; public Pessoa getPessoa(String nome) throws java.rmi.RemoteException; public java.util.Enumeration getPessoas() throws java.rmi.RemoteException;}

Exemplo XX.XX – Interface do objeto remoto.

A interface Remote deve ser herdada pela nossa interface Agenda.

Page 72: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

71

Capítulo V - Acesso a B anco deDados

No dias de hoje, uma linguagem sem recursos para acesso a sistemas deBanco de Dados está fadada ao fracasso. Pensando nisso a Sun incluiu comoparte do núcleo de bibliotecas de classes da linguagem Java uma API com oobjetivo de preencher esta função, chamada de JDBC (segundo a Sun JDBC éapenas um acronismo, no entanto, muitas pessoas acreditam que é uma siglapara Java Database Connectivity). JDBC é uma API baseada no X/Open SQLCall Level Interface, tendo sido desenvolvida originalmente como um pacoteseparado, 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 programa Java comservidores de Banco de Dados e executar comandos SQL (Structure QueryLanguage). Sendo uma API independente do Sistema Gerenciador de Banco deDados, não é necessário escrever uma aplicação para acessar um Banco deDados Oracle, outra para uma Base de Dados Sybase, outra para o DB2, e assimpor diante.

A idéia de se usar uma camada intermediária entre o Banco de Dados e aaplicação, com o objetivo de isolá-la das particularidades do SGBD, não é nova.O exemplo mais popular deste enfoque é a API ODBC (Open DataBaseConnectivity), proposta pela Microsoft. O leitor pode estar se perguntandoporque a Sun resolveu propor mais uma API em vez de adotar a ODBC. Existemvários motivos, porém o principal é que a API ODBC simplesmente não éadequada para a linguagem Java. Isto ocorre porque ODBC foi desenvolvidapara ser usada na linguagem C e é baseada fortemente no uso de ponteiros,estrutura que não existe em Java.

Modelos de Acesso a Servidores

O modelo mais simples de aplicação Cliente/Servidor é o chamado demodelo de duas camadas, onde a aplicação acessa diretamente o Banco deDados. A figura XIII.1 mostra o esquema para uma aplicação que acessa umBanco de Dados usando o modelo de duas camadas.

Page 73: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

72

Figura XIII.1 – Modelo de acesso a Banco de Dados em duas camadas.

Figura XIII.2 – Modelo de acesso a Banco de Dados em três camadas.

Tipos de Drivers JDBC

Os drivers JDBC devem suportar o nível de entrada do padrão ANSISQL-2. No momento, os drivers JDBC existentes se encaixam em um dos quatrotipos abaixo:

1. Ponte JDBC-ODBC com driver ODBC – o driver JDBC acessa o banco dedados via drivers ODBC. Como ODBC é um código binário e, em algunscasos, compõe o código do cliente, é necessário instalar em cada máquinacliente que usa o driver. Essa é uma solução adequada somente para uma

AplicaçãoJava

DBMS

Aplicação Javaou Applet

DBMS

Aplicação ServidoraJava

JDBC

HHTTTTPP,, RRMMII,, CCOORRBBAA

Page 74: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

73

interna corporativa, ou em aplicações que adotam o modelo de três camadassendo a camada intermediária um servidor Java.

SGBD1

Cliente Ponte SGBD2Java JDBC-ODBC SGBD3

2. Driver Java parcial e Api Nativa – neste caso as chamadas JDBC sãoconvertidas para as chamadas às APIs nativas do SGBD. Como o driverpossui uma parte em código binário é necessário instalar algum código namáquina cliente, como é feito nos drivers do tipo 1.

Protocolo do SGBDCliente JDBC Java & SGBDJava Código Binário

3. Driver puro Java e protocolo de rede – neste caso as chamadas JDBC sãoconvertidas para um protocolo de rede independente do SGBD que é depoistraduzido para as chamadas às APIs nativas do SGBD por um servidor. Estaé uma arquitetura em três camadas, onde o servidor middleware é capaz deconectar seus clientes Java puros com vários SGBDs. Esta solução permite odesenvolvimento de clientes 100% Java, tendo como consequência a nãonecessidade de instalação de qualquer código na máquina cliente.

SGBD1Cliente Servidor de SGBD2Java acesso SGBD3

4. Driver Java Puro e protocolo nativo - neste caso as chamadas JDBC sãoconvertidas para as chamadas às APIs nativas do SGBD pelo driver, que foiescrito totalmente em Java.

Protocolo do SGBDCliente JDBC Java SGBDJava (100% Java)

JDBC DRIVER (100% Java)

Page 75: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

74

Atualmente existe uma maior disponibilidade dos drivers tipo 1 e 2, mas atendência é que estes desapareçam, sendo substituídos pelos drivers do tipo 3 e4.

Obtendo os Drivers JDBC

Informações sobre como obter drivers JDBC podem ser obtidas no sitehttp://www.javasoft.com/products/jdbc. Outra alternativa éacessar as páginas dos fabricantes de SGBD, para verificar se existe driverdisponível.

Preparando um Banco de Dados

Os exemplos deste livro usam a ponte JDBC-ODBC para conectar com oBanco de Dados. Isto facilita para os usuários que possuem um gerenciadorBanco de Dados pessoal como o Access, Paradox, e outros semelhantes. Alémdisso, como driver JDBC-ODBC está incorporado ao SDK, o usuário nãonecessita procurar um driver para testar os exemplos. O lado negativo destaabordagem está na necessidade de configurar o ODBC e no fato de que asaplicações remotas deverão ser desenvolvidas em três camadas. No entanto,nada impede que o leitor use outro driver para rodar os exemplos, bastando paraisso alterar a chamada da carga do driver.

Primeiramente é necessário criar uma base de dados em algum SGBD.Nos exemplos deste livro será usada uma base contendo dados sobre livros,alunos e empréstimos de livros aos alunos. Não trataremos neste livro dosconceitos relacionados com banco de dados relacionais nem sobre a linguagemde consulta SQL. Existem vários textos sobre o assunto onde o leitor podebuscar informação.

As figuras XIII.3 a XII.5 mostram as tabelas que formam a base de dadosusada nos exemplos. O banco de dados é formado por três tabelas. Uma paraarmazenar os dados dos alunos, outra para receber os dados dos livros e umaterceira para conter os dados dos empréstimos.

alunosmatricula nome

1 Railer Costa Freire2 Alexandre Altoé

Page 76: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

75

3 André M. A. Landro4 Ana Maria Freitas5 Claudia Maria6 Alexandra Moreira

Figura XIII.3 – Tabela de alunos

livroscodlivro titulo volume

1 Curso Pratico de Java 12 Curso Pratico de Java 23 Introdução a Compiladores 14 Fundamentos de Banco de Dados 15 Redes de Computadores 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 1

10 Aprenda Inteligência Artificial em 5 1

Figura XIII.4 – Tabela de livros.

emprestimoscodlivr matricu data_empresti data_devoluc

1 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/99

10 2 12/02/99 22/02/99

Figura XIII.5 – Tabela de empréstimos.

Em um Banco de Dados Relacional cada tabela representa um conjuntode entidades ou relacionamentos entre entidades, e cada linha da tabelarepresenta uma entidade particular ou um relacionamento entre entidades.Assim, cada linha das tabelas alunos e livros representa um aluno e um livrorespectivamente. Já na tabela empréstimos cada linha representa orelacionamento por empréstimo de um livro a um aluno. Para estabelecer este

Page 77: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

76

tipo de relacionamento em um Banco de Dados Relacional é preciso colocar natabela que representa o relacionamento os atributos chaves de cada entidade queparticipa da relação. Atributos chaves são os atributos que identificam cadaentidade. No caso dos alunos é seu número de matrícula, uma vez que podeexistir dois alunos com o mesmo nome. Já no caso de um livro o seu atributochave é o código do livro. Um Banco de Dados Relacional pode ser representadopelo diagrama de classes da UML, como mostrado pela figura XIII.6, onde cadatabela é vista como uma classe.

Figura XIII.6 – Diagrama de Classes do Banco de Dados.

Para criação das tabelas em banco de dados relacional deve-se usarcomandos DDL (Data Definition Language). O exemplo XX.XX mostra oscomandos em DDL para a criação das tabelas do exemplo:

CREATE TABLE ALUNOS (MATRICULA INT PRIMARY KEY, NOME VARCHAR(50) NOT NULL);

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

CREATE TABLE EMPRESTIMOS ( CODLIVRO INT NOT NULL, MATRICULA INT NOT NULL, DATAEMP DATE NOT NULL, DATADEV DATE NOT NULL, CONSTRAINT PK_EMP PRIMARY KEY (CODLIVRO, MATRICULA, DATAEMP), CONSTRAINT FK_EMP1 FOREIGN KEY (CODLIVRO ) REFERENCES LIVROS (CODLIVRO ),

Alunos

matriculanome

livros

codlivrotitulovolume

emprestimos

data_emprestimodata_devolução

Page 78: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

77

CONSTRAINT FK_EMP2 FOREIGN KEY (MATRICULA) REFERENCES ALUNOS (MATRICULA ));

Exemplo XX.XX – Comandos em DDL para criação das tabelas em um SGBDrelacional.

Configurando o ODBC

Como utilizamos nos exemplos a ponte JDBC-ODBC é necessárioconfigurar o ODBC para acessar a base de dados acima. Na plataformaWindows 98 isso é feito da seguinte forma:

1. Execute o programa de configuraçã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ão de“adicionar...”. O NFD do sistema é escolhido no lugar da opção“NFD do usuário” porque permite o compartilhado à base de dados.

3. Selecione o driver do banco de dados e pressione o botão de concluir.Por exemplo, se você estiver usando o Access selecione a opção“Driver para o Microsoft Access (*.mdb)”.

Page 79: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

78

Figura XIII.7 – Seleção do driver ODBC no Windows.

4. Ao surgir a tela para a entrada seleção do Banco de Dados, utilize atecla “Selecionar...” localizar o arquivo onde está a base de dados epreencha a janela de texto “Nome da fonte de dados” com o nomeque será usado para referenciar a base.

5. Feche o programa de configuração.

A configuração de uma base de dados para ser acessada via ODBCpossui vários outros detalhes que consideramos não ser relevantes para ospropósitos deste livro. A configuração como apresentada acima é a suficientepara se executar os exemplos deste livro.

Exemplo Inicial

O pacote java.sql fornece as classes e interfaces necessárias para aconexão com uma base de dados e a posterior manipulação dos dados. As etapaspara se criar uma aplicação cliente de um SGBD em 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 objeto

Statement.5. Receber o resultado, se for o caso.

Para comentar cada uma destas etapas utilizaremos o exemplo XII.1 quemostra o código de uma aplicação que lista no dispositivo de saída padrão onome de todos os alunos.

import java.sql.*;import java.net.URL;

class jdbc{ public static void main(String a[])

Page 80: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

79 { try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); Connection con = DriverManager.getConnection("jdbc:odbc:biblioteca"); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT NOME FROM alunos"); System.out.println("Nome"); while(rs.next()) System.out.println(rs.getString("nome")); stmt.close(); con.close(); } catch(Exception e) {System.out.println(e.getMessage()); e.printStackTrace();} }}

Exemplo XX.XX – Código para listar o nome dos alunos.

Carregando o Driver

A primeira etapa é carregar o driver JDBC. Para isso é usado o métodoestático forName() da classe Class. Em caso de erro este método lança aexceção ClassNotFoundException. O método cria uma instância dodriver e o registra junto ao DriverManager. No exemplo XIII.1 é carregado odriver JDBC-ODBC que vem junto com o SDK.

Estabelecendo a conexão

A segunda etapa é realizada por meio do método estáticogetConnection() da classe DriverManager. Este método, na sua forma maissimples, recebe como parâmetro um URL que faz referência a base de dados eretorna um objeto da classe Connection, que representa a conexão com a base dedados. Já discutimos sobre URLs no capítulo XI. No entanto, existem algumasparticularidades no que refere a URLs que fazem referência à Banco de Dados.O formato padrão deste tipo de URL é o seguinte:

jdbc:<subprotocolo>:<identificador>

onde:

Page 81: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

80

1. jdbc representa o protocolo;

2. <subprotocolo> se refere ao driver ou ao mecanismo de conexão como Banco de Dados, que pode ser suportado por um ou mais drivers.No exemplo XII.1 o nome é utilizada a ponte JDBC-ODBC,representada pela palavra odbc no subprotocolo.

3. <identificador> é a parte onde se identifica o Banco de Dados. Aforma de identificação varia de acordo com o subprotocolo. No nossoexemplo é colocado o mesmo nome usado para identificar a fonte dedados na configuração do ODBC. A sintaxe para o subprotocolo odbcé a seguinte:

jdbc:odbc:<fonte de dados>[;<atributo>=<valor>]*

onde <atributo> e <valor> representam parâmetros a serem passadospara o gerente de conexão do Banco de Dados.

Um banco de dados acessado remotamente requer maiores informações,como o nome do host e a porta. Tanto o uso local como remoto pode requerer aidentificação do usuário, assim como uma senha. Estes dados podem serpassados como parâmetro no método getConnection():

getConnection("jdbc:odbc:contas",”ana”,”sght”);

ou como parte do URL. Alguns exemplos de URLs estão descritos na tabelaXIII.1

URL Descriçãojdbc:odbc:biblioteca Referencia fonte de dados biblioteca via

ponte JDBC-ODBC.jdbc:odbc:bd1;CacheSize=20 Referencia fonte de dados bd1 via ponte

JDBC-ODBC. É definido o tamanho docache.

jdbc:odbc:contas;UID=ana;PWD=sght Referencia fonte de dados contas viaponte JDBC-ODBC. É passado também onome do usuário e a senha.

jdbc:oracle:thin:@sap.dpi.ufv.br:1521:agenda Referencia fonte de dados agenda no hostremoto sap.dpi.ufv.br via subprotocolooracle. É passado também o número daporta usada no acesso.

Page 82: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

81

Tabela.1 – Exemplos de URLs JDBC.

Criando e Executando Comandos

É necessário cria um ou mais objetos da classe Statement, que possui osmétodos necessários para manipular a base de dados. Este objeto é criado pormeio do método createStatement() do objeto da classe Connection.

Statement stmt = con.createStatement();

Podemos então usar o objeto Statement para executar comandos demanipulação do Banco de Dados. No exemplo XIII.1 o objetivo é recuperar snomes dos alunos. Este objetivo é atingido por meio da execução do comandoSQL

SELECT nome FROM alunos

passado como parâmetro para o método executeQuery() do objetoStatement. Este método retorna um objeto que implementa a interfaceResultSet, e que fornece os meios de acesso ao resultado da consulta. Muitasvezes, como no exemplo, o resultado de uma consulta é uma tabela com váriaslinhas. O programador pode utilizar o objeto ResultSet para acessar cada linhada tabela resultante em sequência. Para isso o objeto mantém um apontador paraa linha corrente, chamado de cursor. Inicialmente o cursor é posicionado antesda primeira linha, movimentado para próxima linha por meio de chamadas aométodo next() do objeto ResultSet.

O método executeQuery() é usado apenas para consultas. Alémdesse método, a classe Statement possui o método execute() que retornamúltiplos ResultSets e o método executeUpdate(), para atualização(comandos INSERT, DELETE e UPDATE da linguagem SQL), criação detabelas (comandos CREATE TABLE) e remoção (DROP TABLE). O valor deretorno do método executeUpdate() é um valor inteiro indicando onúmero de linhas afetadas ou zero no caso do DROP TABLE. Um exemplo deum comando para inserir um uma novo aluno na tabela seria:

stmt.executeUpdate("INSERT INTO alunos VALUES(7, 'Anamia')");

Page 83: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

82

Recuperando Valores

O objeto ResultSet possui os métodos necessários para recuperar osvalores de cada coluna da tabela, bastando passar o nome da coluna comoparâmetro. No exemplo XIII.1 é utilizado método getString() pararecuperar o valor da coluna “nome”. Os métodos para recuperação possuem oformato geral getXXX(), onde XXX é o nome de um tipo. A tabela XIII.2mostra qual o método mais indicado para cada tipo SQL.

TINYINT

SMALLINT

INTEGER

BIGINT

REAL

FLOAT

DOUBLE

DECIMAL

NUMERIC

BIT

CHAR

VARCHAR

LONGVARCHAR

BINARY

VARBINARY

LONGVARBINARY

DATE

TIME

TIMESTAMP

getByte X x x x x x x x x x x x xgetShort x X x x x x x x x x x x xgetInt x x X x x x x x x x x x xgetLong x x x X x x x x x x x x xgetFloat x x x x X x x x x x x x xgetDouble x x x x x X X x x x x x xgetBigDecimal x x x x x x x X X x x x xgetBoolean x x x x x x x x x X x x xgetString x x x x x x x x x x X X x x x x x x xgetBytes X X xgetDate x x x X xgetTime x x x X xgetTimestamp x x x x XgetAsciiStream x x X x x xgetUnicodeStream

x x X x x x

Page 84: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

83

getBinaryStream

x x X

getObject x x x x x x x x x x x x x x x x x x x“x” indica que o método pode ser usado para recuperar o valor no tipo SQL especificado.“X” indica que o método é recomendado para ser usado na recuperação do valor no tipo SQL especificado

Tabela XIII.2 – Tabelas com os métodos indicados para recuperação devalores.

É possível recuperar o valor da coluna passando como parâmetro onúmero da coluna no lugar de seu nome. Neste caso a recuperação no nome doaluno no exemplo XIII.1 ficaria na seguinte forma:

rs.getString(1);

Transações e Nível de Isolamento

Transação

Uma Transação é um conjunto de operações realizadas sobre um bancode dados tratadas atomicamente, em outras palavras, ou todas operações sãorealizadas e o seu resultado registrado permanentemente na base de dados ounenhuma operação é realizada. Por default, o banco de dados trata cada operaçãocomo uma transação, realizando implicitamente uma operação de commit ao fimde cada uma delas. A operação de commit registra permanentemente o resultadoda transação na tabela.

No entanto, existem situações onde é necessário tratar como umatransação um conjunto de operações, e não apenas uma transação. Por exemplo,suponha que em um Banco de Dados de uma agência bancária exista uma tabelacom informações sobre a conta de corrente e outra com informações sobrecontas de poupança. Suponha também que um cliente deseje transferir o dinheiroda conta corrente para uma conta de poupança. Essa transação é constituídapelas seguintes operações:

1. Caso exista saldo suficiente, subtração do montante da transferênciado saldo da conta corrente.

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

Page 85: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

84

As operações acima precisam ocorrer totalmente ou o efeito de nenhumadelas deve ser registrado na base de dados. Caso contrário podemos ter umasituação onde o dinheiro sai da conta corrente mas não entra na conta dapoupança. Este estado, onde as informações do banco de dados não reflete arealidade, é chamado 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 final da transação, por meio daexecução do método commit() do objeto Connection.

con.commit();

Se alguma exceção for levantada durante a execução de qualqueroperação da transação, o programador pode usar o método rollback() paradesfazer as operações já realizadas após o último commit().

con.setAutoCommit(false);try{ Statement stmt = con.createStatement(); stmt.executeUpdate(“UPDATE ...” ); stmt.executeUpdate(“UPDATE ...” ); con.commit(); stmt.close();}catch(Exception e){con.rollback();}finally{ try{ con.setAutoCommit(true);} catch(SQLException sqle) {System.out.prinln(sql.getMessage());}}

Exemplo XX.X – Uso dos métodos commit() e rollback().

Page 86: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

85

Níveis de isolamento

Além da atomicidade outra propriedade desejável em uma transação é oisolamento. A propriedade de isolamento implica que uma transação não éafetada pelas operações realizadas por outras transações que estão sendorealizadas concorrentemente.

O isolamento completo entre transações prejudica muito a execuçãoconcorrente de transações e pode ser desnecessário em determinados tipos deaplicações. Por isso os SGBDs permitem que o programador defina o nível deisolamento entre as transações. De acordo com o relaxamento do isolamentocertos problemas devido a interferência entre as transações podem ocorrer e oprogramador deve estar ciente disso.

O número de níveis de isolamento, sua nomenclatura e característicasdepende do SGBD utilizado. Descreveremos os níveis de isolamento definidosno pacote java.sql. Para exemplificar os problemas que podem ocorrerdevido a interferência entre transações utilizaremos um banco de dados exemplocom a seguinte tabela:

NumCC Saldo10189-920645-7

200,00300,00

• Read uncommitted - É o nível menos restritivo. Pode ocorrer leituras deregistros não committed (Dirty reads). Usados em onde não existeconcorrência ou não existem alterações em registros ou quando essasalterações não são relevantes. Exemplo de problema: Uma transação devetransferir R$50,00 da conta 10189-9 para a conta 20645-7 e uma segundatransação deve somar R$70,00 à conta 10189-9. A figura abaixo mostra oestado inicial e o estado final desejado da tabela:

NumCC Saldo NumCC Saldo10189-920645-7

200,00300,00

10189-920645-7

220,00350,00

Estado desejado após astransações

Estado antes dastransações

Page 87: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

86

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

Transação 1 Transação 2leitura do saldo 10189Escrita do Saldo-50,00

leitura do saldo 20645(falha na transação,realizado rollback)

leitura do saldo 10189

Escrita do Saldo+70,00

Como a transação 1 falhou o valor lido pela transação 2 é um valor que nãofoi tornado permanente na tabela. Isto faz com que a transação 2 opere sobreum resultado desfeito. A tabela resultante, mostrada abaixo estará em umestado inconsistente.

NumCC Saldo10189-920645-7

220,00300,00

• Read committed - Somente registros committed podem ser lidos. Evita oproblema de Dirty reads, no entanto duas leituras de um mesmo item emuma mesma transação podem possuir valores diferentes, uma vez que o valorpode ser mudado por outra transação entre duas leituras.

• Repeatable Read - Somente registros committed podem ser lidos, alémdisso impede a alteração de um item lido pela transação. Evita o problema deDirty reads e o problema do non-repeatable Read .

• Serializable - É o nível mais restritivo. Impede Dirty reads e non-repeatablereads. Além disso impede o problema de phantom reads onde um conjuntode registros satisfazendo a condição WHERE é lido enquanto outra transaçãoinsere novos registros que satisfazem a condição.

Para se definir o nível de isolamento na linguagem Java usa-se um objetoDatabaseMetaData que é obtido por meio do objeto getMetaData() doConnection. Primeiro é preciso saber se o SGBD suporta o nível de

Page 88: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

87

isolamento desejado para depois definir o nível. O exemplo XX.X mostra umasequência típica comandos.

DatabaseMetaData meta=con.getMetaData();

if(meta.supportsTransactionIsolationLevel( con.TRANSACTION_READ_COMMITTED)) { con.setTransactionIsolation( con.TRANSACTION_READ_COMMITTED);}else return;

Exemplo XX.X – Exemplo do estabelecimento do nível de isolamento.

A tabela abaixo mostra as constantes relacionadas com os níveis deisolamento da linguagem Java:

ConstanteTRANSACTION_NONETRANSACTION_READ_UNCOMMITTEDTRANSACTION_READ_COMMITTEDTRANSACTION_REPEATABLE_READTRANSACTION_SERIALIZABLE

Tabela XX.X – Tabela com as constantes dos níveis de isolamento.

Prepared Statements

Cada vez que se executa um comando SQL passado por meio de umaString. Este String deve ser analisado pelo processador de SQL do SGBD queirá, no caso da String estar sintaticamente correta, gerar um código binário queserá executado para atender à solicitação. Todo esse processo é caro e suaexecução repetidas vezes terá um impacto significativo sobre o desempenho daaplicação e do SGBD como um todo.

Existem duas abordagens para tentar solucionar esse problema:Comandos preparados (prepared statements) e procedimentos armazenados(stored procedures). Discutiremos primeiramente os prepared statements.

Prepared Statement é indicado nos casos onde um comando seráexecutado várias vezes em uma aplicação. Neste caso é melhor compilar ocomando uma única vez e toda vez que for necessário executá-lo basta enviar o

Page 89: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

88

comando compilado. Além disso, o comando pré-compilado pode serparametrizado, tornando-o mais genérico e, portanto, apto a expressar um maiornúmero de consultas.

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

PreparedStatement pstmt = con.prepareStatement( “INSERT INTO alunos(matricula,nome) VALUES(?, ? )”);

O comando anterior insere uma nova linha na tabela alunos com osvalores das colunas matricula e nome passados por parâmetro. O caractere ‘?’representa o parâmetro. Este tipo de comando só possui valor tendo parâmetros,caso contrário teria pouca chance de ser reutilizado. Para executar o comandodevemos especificar o valor dos parâmetros e executar o comando, comomostrado no exemplo abaixo:

pstmt.clearParameters();pstmt.setInt(1,8);pstmt.setString(2,”Clara Maria”);pstmt.executeUpdate();

Antes de especificar os parâmetros é necessário limpar qualquer outroparâmetro previamente especificado. Para especificar os parâmetros sãoutilizados um conjunto de métodos com o nome no formato setXXX(), ondeXXX é o tipo sendo passado. O primeiro parâmetro do método setXXX() é oíndice da ocorrência do caractere ‘?’ que será substituído pelo valor. O segundoparâmetro é o valor que será transmitido.

Procedimentos Armazenados (Stored Procedures)

A maioria dos SGBDs possuem algum tipo de linguagem deprogramação interna, como por exemplo a PL/SQL do Oracle ou mesmo Java eC/C++. Estas linguagens permitem que os desenvolvedores insiram parte docódigo da aplicação diretamente no banco de dados e invoquem este código apartir da aplicação. Esta abordagem possui as seguintes vantagens:

Page 90: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

89

• Reuso de código – o código precisa ser escrito apenas uma vez e usadoem várias aplicações, comunicando com várias linguagens.

• Independencia entre a aplicação e o esquema do BD – se o esquemamudar, provavelmente apenas os procedimentos armazenados.

• Desempenho – os procedimentos são previamente compilados,eliminando esta etapa.

• Segurança – as aplicações possuem privilégio apenas para execução deprocedimentos armazenados, evitando assim acessos não autorizados.

A sintaxe dos procedimentos armazenados depende do SGBD emquestão. Utilizaremos um exemplo em PL/SQL. No exemplo abaixo oprocedimento retorna o nome do aluno a partir de sua matricula.

CREATE OR REPLACE PROCEDURE sp_obtem_nome (id IN INTEGER, Nome_aluno out VARCHAR2)ISBEGIN SELECT nome INTO Nome_aluno FROM alunos WHERE matricula = id;END;

/

Para invocar o procedimento anterior de dentro de uma aplicação Java énecessário obter um objeto CallableStatement por meio do métodoprepareCall() do objeto Connection, passando como argumento umcomando SQL.

CallableStatement cstmt = con.prepareCall("{ CALL sp_obtem_nome(?,?)}");cstmt.registerOutParameter(2, Types.VARCHAR);cstmt.setInt(1, 3);cstmt.execute();System.out.prinln(“O nome do aluno numero 3 :” +cstmt.getString(2);

Page 91: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

90

Agenda Eletrônica versão JDBC

Pessoa

import java.io.*;

public class pessoa implements Serializable{

String Nome;String Tel;

// Construtorpublic pessoa(String n, String t) {Nome = n; Tel = t;}

public String getNome(){return Nome;}public String getTel(){return Tel;}

}

agendaimport java.util.*;import java.io.*;import java.sql.*;import java.net.URL;

public class agenda{ Connection con=null;

// Construtor public agenda()throws Exception { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); con=DriverManager.getConnection("jdbc:odbc:agenda"); }

/**CloseAgenda

*/ public void CloseAgenda() { if (con != null) try{con.close();}catch(Exception e){}; }

/**inserir

Page 92: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

91 */ public void inserir(pessoa p) { if (con == null) return;

try { Statement stmt = con.createStatement(); stmt.executeUpdate("INSERT INTO pessoas(nome,telefone)"+ "values('"+p.getNome()+"','"+p.getTel()+"')");

stmt.close(); }catch(Exception e) {System.err.println(e);} }

/**Consultar

*/

/**listar

*/ public Enumeration getLista() { if (con == null) return null;

Vector pessoas = new Vector();

try { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery ("SELECT Nome, Telefone FROM pessoas"); while(rs.next()) pessoas.addElement(newpessoa(rs.getString("Nome"), rs.getString("Telefone"))); stmt.close(); } catch(Exception e) {System.out.println(e.getMessage()); e.printStackTrace();}

return pessoas.elements(); }}

Servidorimport java.util.*;import java.net.*;import java.io.*;

Page 93: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

92

/**CLASS server*/class server{ public static void main(String args[]) { String oHost="localhost"; ServerSocket ssocket=null; serversec oServersec; int pt = 4444; agenda ag;

try {ag = new agenda();} catch(Exception e){System.err.println(e); return;}; if (args.length > 0) pt = Integer.parseInt(args[0]);

try{ ssocket = new ServerSocket(pt); pt = ssocket.getLocalPort(); oHost =

ssocket.getInetAddress().getHostName().trim();}catch(Exception e) {System.err.println(e);

System.exit(1);}

System.out.println("Porta:"+pt+" Host: "+oHost);

while(true){ try {

Socket clisocket = ssocket.accept();oServersec = new serversec(ag,clisocket);oServersec.start();

} catch(Exception e) {System.err.println(e);}}

}}

/** CLASS serversec*/class serversec extends Thread{ Socket oSocket; BufferedWriter soutput;

Page 94: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

93 BufferedReader cinput; agenda ag;

public serversec(agenda ag, Socket aoSocket) {

this.ag = ag;oSocket = aoSocket;

}

public void run() {

try{

soutput = new BufferedWriter(new

OutputStreamWriter(oSocket.getOutputStream())); cinput = new BufferedReader(new

InputStreamReader(oSocket.getInputStream()));

String losLinha = cinput.readLine(); switch(losLinha.charAt(0)) { case 'i': String Nome = cinput.readLine(); String Tel = cinput.readLine(); ag.inserir(new pessoa(Nome,Tel)); soutput.write("OK\n#\n"); break; case 'l': pessoa p; for (Enumeration e = ag.getLista();e.hasMoreElements() ;) { p = (pessoa) e.nextElement();

soutput.write(p.getNome()+"\n"+p.getTel()+"\n"); } soutput.write("#\n"); break; }

soutput.flush(); Thread.yield(); soutput.close(); oSocket.close();

}catch(Exception e) {System.err.println(e);}

}}

Page 95: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

94

Appletimport java.awt.*;import java.applet.*;import java.util.*;import java.net.*;import java.io.*;

public class agendaapplet extends Applet{ int port = 4444;

TextField txtNome = new TextField(); TextField txtTel = new TextField(); Label label1 = new Label(); Label label2 = new Label(); Button btIns = new Button(); Button btList = new Button(); Button btCons = new Button(); Button btSair = new Button(); TextArea Saida = new TextArea(); Button btLimpar = new Button();

public void init() {

setLayout(null);setSize(376,224);add(txtNome);txtNome.setBounds(108,48,232,24);add(txtTel);txtTel.setBounds(108,84,232,24);label1.setText("Nome");add(label1);label1.setBounds(24,48,60,26);label2.setText("Telefone");add(label2);label2.setBounds(24,84,60,26);btIns.setActionCommand("button");btIns.setLabel("Inserir");add(btIns);btIns.setBackground(java.awt.Color.lightGray);btIns.setBounds(12,12,49,23);btList.setActionCommand("button");btList.setLabel("Listar");add(btList);btList.setBackground(java.awt.Color.lightGray);btList.setBounds(149,12,60,23);btCons.setActionCommand("button");btCons.setLabel("Consultar");add(btCons);

Page 96: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

95btCons.setBackground(java.awt.Color.lightGray);btCons.setBounds(75,12,60,23);btSair.setActionCommand("button");btSair.setLabel("Sair");add(btSair);btSair.setBackground(java.awt.Color.lightGray);btSair.setBounds(297,12,60,23);add(Saida);Saida.setBounds(24,120,338,90);btLimpar.setActionCommand("button");btLimpar.setLabel("Limpar");add(btLimpar);btLimpar.setBackground(java.awt.Color.lightGray);btLimpar.setBounds(223,12,60,23);

SymMouse aSymMouse = new SymMouse();btSair.addMouseListener(aSymMouse);btIns.addMouseListener(aSymMouse);btList.addMouseListener(aSymMouse);btLimpar.addMouseListener(aSymMouse);

}

public void transmit(int port,String mensagem) { BufferedWriter soutput; BufferedReader cinput; Socket clisoc=null; try { if (clisoc != null) clisoc.close(); clisoc = new

Socket(InetAddress.getByName(getCodeBase().getHost()),port); soutput = new BufferedWriter (new OutputStreamWriter(clisoc.getOutputStream())); cinput = new BufferedReader(new InputStreamReader(clisoc.getInputStream()));

soutput.write(mensagem+"\n"); soutput.flush();

String losRet = cinput.readLine(); while (losRet.charAt(0)!='#') { Saida.setText(Saida.getText()+losRet+"\n"); losRet = cinput.readLine(); } Thread.sleep(500);

Page 97: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

96 soutput.close(); clisoc.close(); } catch(Exception e) {System.err.println(e);} }

class SymMouse extends java.awt.event.MouseAdapter {

public void mouseClicked(java.awt.event.MouseEvent event){ Object object = event.getSource(); if (object == btSair) btSair_MouseClick(event); else if (object == btIns) btIns_MouseClick(event); else if (object == btList) btList_MouseClick(event); else if (object == btLimpar)

btLimpar_MouseClick(event); }}

void btSair_MouseClick(java.awt.event.MouseEvent event) {

System.exit(0); }

void btIns_MouseClick(java.awt.event.MouseEvent event) {

String nome = txtNome.getText();String tel = txtTel.getText();

if (nome.length()>0 && tel.length()>0)transmit(port,"i\n"+nome+"\n"+tel+"\n");

}

void btList_MouseClick(java.awt.event.MouseEvent event) {

transmit(port,"l\n"); }

void btLimpar_MouseClick(java.awt.event.MouseEvent event) {

Saida.setText(""); }}

Page 98: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

97

Capítulo VI Servlets e JSPServlets e JSP são duas tecnologias desenvolvidas pela Sun para

desenvolvimento de aplicações na Web a partir de componentes Java queexecutem no lado servidor. Essas duas tecnologias fazem parte da plataformaJ2EE (Java 2 Platform Enterprise Edition) que fornece um conjunto detecnologias para o desenvolvimento de soluções escaláveis e robustas para aWeb. Neste livro abordaremos apenas as tecnologias Servlets e JSP, sendo osuficiente para o desenvolvimento de sites dinâmicos de razoável complexidade.Se a aplicação exigir uma grande robustez e escalabilidade o leitor deveconsiderar o uso em conjunto de outras tecnologias da plataforma J2EE.

Servlets

Servlets são classes Java que são instanciadas e executadas emassociação com servidores Web, atendendo requisições realizadas por meio doprotocolo HTTP. Ao serem acionados, os objetos Servlets podem enviar aresposta na forma de uma página HTML ou qualquer outro conteúdo MIME. Naverdade os Servlets podem trabalhar 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 independentes de protocolos e plataformas. Emoutras palavras Servlets é uma API para construção de componentes do ladoservidor com o objetivo de fornecer um padrão para comunicação entre clientese servidores. Os Servlets são tipicamente usados no desenvolvimento de sitesdinâmicos. Sites dinâmicos são sites onde algumas de suas páginas sãoconstruídas no momento do atendimento de uma requisição HTTP. Assim épossível criar páginas com conteúdo variável, de acordo com o usuário, tempo,ou informações armazenadas em um banco de dados.

Servlets não possuem interface gráfica e suas instâncias são executadasdentro de um ambiente Java denominado de Container. O container gerencia asinstâncias dos Servlets e provê os serviços de rede necessários para asrequisições e respostas. O container atua em associação com servidores Webrecebendo as requisições reencaminhada por eles. Tipicamente existe apenasuma instância de cada Servlet, no entanto, o container pode criar vários threadsde modo a permitir que uma única instância Servlet atenda mais de uma

Page 99: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

98

requisição simultaneamente A figura XX fornece uma visão do relacionamentodestes componentes.

Figura VI-1. Relacionamento entre Servlets, container e servidor Web

Servlets provêem uma solução interessante para o relacionamentocliente/servidor na Internet, tornando-se uma alternativa para a implantação desistemas para a Web. Antes de entrarmos em detalhes na construção de Servlets,compararemos esta solução com outras duas soluções possíveis para implantaçãode aplicações na Internet.

Applets X Servlets

Apesar de ser uma solução robusta existem problemas no uso de Applets paravalidação de dados e envio para o servidor. O programador precisa contar com ofato do usuário possuir um navegador com suporte a Java e na versãoapropriada. Você não pode contar com isso na Internet, principalmente se vocêdeseja estender a um grande número de usuário o acesso às suas páginas. Em setratando de Servlets, no lado do cliente pode existir apenas páginas HTML,evitando restrições de acesso às páginas. Em resumo, o uso de Applets não érecomendado para ambientes com múltiplos navegadores ou quando a semânticada aplicação possa ser expressa por componentes HTML.

ServidorWeb

Requisições

Máquina Virtual Java

Container

Instâncias de ServletsRespostas

Page 100: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

99

CGI X Servlets

Como visto no Erro! A origem da referência não foi encontrada.,scripts CGI (Common Gateway Interface), acionam programas no servidor. Ouso de CGI sobrecarrega o servidor uma vez cada requisição de serviço acarretaa execução de um programa executável (que pode ser escrito em com qualquerlinguagem que suporte o padrão CGI) no servidor, além disso, todo oprocessamento é realizado pelo CGI no servidor. Se houver algum erro naentrada de dados o CGI tem que produzir uma página HTML explicando oproblema. Já os Servlets são carregados apenas uma vez e como são executadosde forma multi-thread podem atender mais de uma mesma solicitação porsimultaneamente. Versões posteriores de CGI contornam este tipo de problema,mas permanecem outros como a falta de portabilidade e a insegurança naexecução de código escrito em uma linguagem como C/C++.

A API Servlet

A API Servlet é composta por um conjunto de interfaces e Classes. Ocomponente mais básico da API é interface Servlet. Ela define ocomportamento básico de um Servlet. A figura XX.XX mostra a interfaceServlet.

public interface Servlet { public void init(ServletConfig config) throws ServletException; public ServletConfig getServletConfig(); public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; public String getServletInfo(); public void destroy();}

Figura XV.XX. Interface Servlet.

O método service() é responsável pelo tratamento de todas dasrequisições dos clientes. Já os métodos init() e destroy() são chamadosquando o Servlet é carregado e descarregado do container, respectivamente. Ométodo getServletConfig() retorna um objeto ServletConfig que

Page 101: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

100

contém os parâmetros de inicialização do Servlet. O métodogetServletInfo() retorna um String contendo informações sobre oServlet, como versão e autor.

Tendo como base a interface Servlet o restante da API Servlet seorganiza hierarquicamente como mostra a figura XV.XX.

Figura XV.XX. Hierarquia de classes da API Servlet.

A classe GenericServlet implementa um servidor genérico egeralmente não é usada. A classe HttpServlet é a mais utilizada e foiespecialmente projetada para lidar com o protocolo HTTP. A figura XX.XXmostra a definição da classe interface HttpServlet.

HttpServlet

public abstract class HttpServletextends GenericServletimplements java.io.Serializable

Figura XV.XX. Definição da classe HttpServlet.

Note que a classe HttpServlet é uma classe abstrata. Para criar umServlet que atenda requisições HTTP o programador deve criar uma classederivada da HttpServlet e sobrescrever pelo menos um dos métodosabaixo:

Servlet

GenericServlet

HttpServlet

Page 102: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

101

doGet 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 XV.XX. Métodos da classe HttpServlet que devem sersobrescritos para tratar requisições HTTP.

Todos esses métodos são invocados pelo servidor por meio do métodoservice(). O método doGet() trata as requisições GET. Este tipo derequisição pode ser enviada várias vezes, permitindo que seja colocada em umbookmark. O método doPost() trata as requisições POST que permitem que ocliente envie dados de 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. Ométodo doPut() trata as requisições PUT. Este tipo de requisição permite queo cliente envie um arquivo para o servidor à semelhança de como é feito viaFTP. O método doPut() trata as requisições DELETE, permitindo que ocliente remova um documento ou uma página do servidor. O métodoservice(), que recebe todas as requisições, em geral não é sobrescrito, sendosua tarefa direcionar a requisição para o método adequado.

Exemplo de Servlet

Para entendermos o que é um Servlet nada melhor que um exemplosimples. O exemplo XV.XX gera uma página HTML em resposta a umarequisição GET. A página HTML gerada contém simplesmente a frase Olamundo!!!. Este é um Servlet bem simples que ilustra as funcionalidades básicasda classe.

import javax.servlet.*;import javax.servlet.http.*;

public class Ola extends HttpServlet{ public String getServletInfo() { return "Ola versão 0.1";}

public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException

Page 103: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

102

{ res.setContentType("text/html"); java.io.PrintWriter out = res.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>Servlet</title>"); out.println("</head>"); out.println("<body>Ola mundo!!!"); out.println("</body>"); out.println("</html>"); out.close(); }}

Exemplo XV.XX. Servlet Ola.

O método doGet() recebe dois objetos: um da classeHttpServletRequest e outro da classe HttpServletResponse. OHttpServletRequest é responsável pela comunicação do cliente para oservidor e o HttpServletResponse é responsável pela comunicação doservidor para o cliente. Sendo o exemplo XV.XX apenas um exemplo simplesele ignora o que foi enviado pelo cliente, tratando apenas de enviar uma páginaHTML como resposta. Para isso é utilizado o objeto da classeHttpServletResponse. Primeiramente é usado o métodosetContentType() para definir o tipo do conteúdo a ser enviado ao cliente.Esse método deve ser usado apenas uma vez e antes de se obter um objeto dotipo PrintWriter ou ServletOutputStream para a resposta. Após issoé usado o método getWriter() para se obter um objeto do tipoPrintWriter que é usado para escrever a resposta. Neste caso os dados daresposta são baseados em caracteres. Se o programador desejar enviar a respostaem bytes deve usar o método getOutputStream() para obter um objetoOutputStream. A partir de então o programa passa usar o objetoPrintWriter para enviar a página HTML.

Compilando o Servlet

A API Servlet ainda não foi incorporado ao SDK, portanto, para compilarum Servlet é preciso adicionar a API Servlet ao pacote SDK. Existem váriasformas de se fazer isso. A Sun fornece a especificação da API e diversos

Page 104: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

103

produtores de software executam a implementação. Atualmente, a especificaçãoda API Servlet está na versão 2.XX. Uma das implementações da API que podeser baixada gratuitamente pela Internet é a fornecida pelo projeto Jakarta(http://jakarta.apache.org) denominada de Tomcat. Aimplementação da API Servlet feita pelo projeto Jakarta é a implementação dereferência indicada pela Sun. Ou seja, é a implementação que os outrosfabricantes devem seguir para garantir a conformidade com a especificação daAPI. No entanto, uma vez que o Tomcat é a implementação mais atualizada daAPI, é também a menos testada e, por consequência, pode não ser a mais estávele com melhor desempenho.

Instalando o Tomcat

Assim como para se executar um Applet era preciso de um navegadorWeb com Java habilitado no caso de Servlets é preciso de servidor Web queexecute Java ou que passe as requisições feitas a Servlets para programas queexecutem os Servlets. O Tomcat é tanto a implementação da API Servlet como aimplementação de um container, que pode trabalhar em associação com umservidor Web como o Apache ou o IIS, ou pode também trabalhar isoladamente,desempenhando também o papel de um servidor Web. Nos exemplos aquimostrados usaremos o Tomcat isoladamente. Em um ambiente de produção estaconfiguração não é a mais adequada, uma vez que os servidores Web possuemum melhor desempenho no despacho de páginas estáticas. As instruções paraconfigurar o Tomcat para trabalhar em conjunto com um servidor Web podemser encontradas junto às instruções gerais do programa. As figuras XV.XXilustram essas duas situações.

Figura XV.XX. Servidor Web habilitado para Servlet.

Servidor Web

Servlet habilitadoInternet

Page 105: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

104

Figura XV.XX. Servidor Web reencaminhando as requisições para o Servletcontainer.

A versão estável do Tomcat é a 3.2.3.XX é após baixá-la do site doprojeto Jakarta o usuário deve descomprimir o arquivo. Por exemplo, noambiente Windows o usuário pode descomprimir o arquivo na raiz do disco C:,o que gerará a seguinte árvore de diretórios:

C:\ jakarta-tomcat-3.2.3 | |______bin |______conf |______doc |______lib |______logs |______src |______webapps

No diretório bin encontram-se os programas execução e interrupção docontainer Tomcat. No diretório conf encontram-se os arquivos de configuração.No diretório doc encontram-se os arquivos de documentação. No diretório libencontram-se os bytecodes do container e da implementação da API. Nodiretório logs são registradas as mensagens da geradas durante a execução dosistema. No diretório src encontram-se os arquivos fontes do container e daimplementação da API de configuração. Finalmente, No diretório webappsencontram-se as páginas e códigos das aplicações dos usuários.

No ambiente MS-Windows aconselhamos usar um nome dentro doformato 8.3 (oito caracteres para o nome e três para o tipo). Assim o diretóriojakarta-tomcat-3.2.3 poderia ser mudado para simplesmente tomcat.

Internet Servidor Web

Servlet Container

Page 106: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

105

Antes de executar o Tomcat é necessário definir duas variáveis deambiente. Por exemplo, supondo que no MS-Windows o Tomcat foi instalado nodiretório c:\tomcat e que o SDK está instalado no diretório c:\jdk1.3então as seguintes variáveis de ambiente devem ser definidas:

set JAVA_HOME=C:\jdk1.3set TOMCAT_HOME=C:\tomcat

Agora é possível executar o Tomcat por meio do seguinte comando:

C:\tomcat\bin\startup.bat

Para interromper a execução servidor basta executar o arquivo

c:\tomcat\bin\shutdown.bat

Falta de espaço para variáveis de ambienteCaso ao iniciar o servidor apareça a mensagem “sem espaço de ambiente”

clique com o botão direito do mouse no arquivo .bat e edite as propriedades definindo oambiente inicial com 4096. Feche o arquivo é execute novamente.

Ao entrar em execução o servidor lê as configurações constantes noarquivo server.xml e, por default, se anexa à porta 8080. Para verificar se oprograma está funcionando corretamente execute um navegador como oNetscape ou o Internet Explorer e digite a seguinte URL:

http://127.0.0.1:8080/index.html

A figura XX.XX mostra a tela principal do Tomcat.

Page 107: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

106

Figura XV.XX. Tela inicial do Tomcat.

A número porta default para recebimento das requisições HTTP pode seralterada por meio da edição do arquivo server.xml do diretório conf comomostrado abaixo:

<Connector className="org.apache.tomcat.service.PoolTcpConnector"> <Parameter name="handler"

value="org.apache.tomcat.service.http.HttpConnectionHandler"/> <Parameter name="port" value="Número da porta"/></Connector>

No entanto, caso o Tomcat esteja operando em conjunto com umservidor, o ideal é que o Tomcat não responda requisições diretamente.

Preparando para executar o Servlet

Compilando o Servlet

Page 108: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

107

Antes de executar o Servlet e preciso compilá-lo. Para compilá-lo épreciso que as classes que implementam a API Servlet estejam no classpath.Para isso é preciso definir a variável de ambiente. No ambiente MS-Windowsseria

set CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\servlet.jar

e no ambiente Unix seria

CLASSPATH=${CLASSPATH}:${TOMCAT_HOME}/lib/servlet.jar

Alternativamente, é possível indicar o classpath na própria linha deexecução do compilador Java. Por exemplo, No ambiente MS-Windows ficariana seguinte forma:

javac -classpath "%CLASSPATH%;c:\tomcat\lib\servlet.jar Ola.java

Criando uma aplicação no Tomcat

Agora é preciso definir onde deve ser colocado o arquivo compilado.Para isso é preciso criar uma aplicação no Tomcat ou usar uma das aplicações jáexistentes. Vamos aprender como criar uma aplicação no Tomcat. Para isso épreciso cria a seguinte estrutura de diretórios abaixo do diretório webapps doTomcat:

webapps |_____ Nome aplicação |_____ Web-inf |_____classes

Diretório de AplicaçõesNa verdade é possível definir outro diretório para colocar as aplicações do Tomcat. Para

indicar outro diretório é preciso editar o arquivo server.xml e indicar o diretório por meio dadiretiva home do tag ContextManager.

O diretório de uma aplicação é denominado de contexto da aplicação. Épreciso também editar o arquivo server.xml do diretório conf, incluindo aslinhas:

Page 109: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

108<Context path="/nome aplicação" docBase="webapps/ nome aplicação" debug="0” reloadable="true" ></Context>

Finalmente, é preciso criar (ou copiar de outra aplicação) um arquivoweb.xml no diretório Web-inf com o seguinte conteúdo:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd"><web-app></web-app>

Copie então o arquivo compilado Ola.class para o subdiretório/webapps/nome aplicação/Web-inf/classes do Tomcat.

Executando o Servlet

Invocando diretamente pelo Navegador

Podemos executar um Servlet diretamente digitando a URL do Servlet nonavegador. A URL em geral possui o seguinte formato:

http://máquina:porta/nome aplicação/servlet/nome servlet

A palavra servlet que aparece na URL não indica um subdiretório noservidor. Ela indica que esta é uma requisição para um Servlet. Por exemplo,suponha que o nome da aplicação criada no Tomcat seja teste. Então a URLpara a invocação do Servlet do exemplo XX.XX teria a seguinte forma:

http://localhost:8080/teste/servlet/Ola

A URL para a chamada do Servlet pode ser alterada de modo a ocultarqualquer referência à diretórios ou a tecnologias de implementação. No caso doTomcat essa configuração é no arquivo web.xml do diretório Web-inf daaplicação. Por exemplo, para eliminar a palavra servlet da URL poderíamos

Page 110: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

109

inserir as seguintes linhas no arquivo web.xml entre os tags <web-app> e</web-app>:

<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>

Invocando em uma página HTML

No caso de uma página HTML basta colocar a URL na forma de link.Por exemplo,

<a href="http://localhost:8080/teste/servlet/Ola>Servlet Ola</a>

Neste caso o Servlet Ola será solicitado quando o link associado ao texto“Servlet Ola” for acionado.

Diferenças entre as requisições GET e POST

Os dois métodos mais comuns, definidos pelo protocolo HTTP, de seenviar uma requisições a um servidor Web são os métodos POST e GET. Apesarde aparentemente cumprirem a mesma função, existem diferenças importantesentre estes dois métodos. O método GET tem por objetivo enviar uma requisiçãopor um recurso. As informações necessárias para a obtenção do recurso (comoinformações digitadas em formulários HTML) são adicionadas à URL e, porconsequência, não são permitidos caracteres inválidos na formação de URLs,

Page 111: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

110

como por espaços em branco e caracteres especiais. Já na requisição POST osdados são enviados no corpo da mensagem.

O método GET possui a vantagem de ser idempotente, ou seja, osservidores Web podem assumir que a requisição pode ser repetida, sendopossível adicionar à URL ao bookmark. Isto é muito útil quando o usuário desejamanter a URL resultante de uma pesquisa. Como desvantagem as informaçõespassadas via GET não podem ser muito longas, um vez o número de caracterespermitidos é por volta de 2K.

Já as requisições POST a princípio podem ter tamanho ilimitado. Noentanto, elas não são idempotente, o que as tornam ideais para formulários ondeos usuários precisam digitar informações confidenciais, como número de cartãode crédito. Desta forma o usuário é obrigado a digitar a informação toda vez quefor enviar a requisição, não sendo possível registrar a requisição em umbookmark.

Concorrência

Uma vez carregado o Servlet não é mais descarregado, a não ser que oservidor Web tenha sua execução interrompida. De modo geral, cada requisiçãoque deve ser direcionada a determinada instância de Servlet é tratada por umthread sobre a instância de Servlet. Isto significa que se existirem duasrequisições simultâneas que devem ser direcionadas para um mesmo objeto ocontainer criará dois threads sobre o mesmo objeto Servlet para tratar asrequisições. A figura XX.XX ilustra esta situação.

Figura XV.XX. Relacionamento entre as instâncias dos Servlets e os threads.

Servlet2

thread1Servlet1

thread2

thread1

usuário1

usuário2

usuário3

Page 112: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

111

Em conseqüência disto temos o benefícios de uma sobrecarga paraservidor, uma vez que a criação de threads é menos onerosa do que a criação deprocessos, e uma aparente melhora no tempo de resposta.

Por outro lado, o fato dos Servlets operarem em modo multi-threadaumenta a complexidade das aplicações e cuidados especiais, como visto nocapítulo sobre concorrência, devem tomados para evitar comportamentoserráticos. Por exemplo, suponha um Servlet que receba um conjunto de númerosinteiros e retorne uma página contendo a soma dos números. A exemplo XX.XXmostra o código do Servlet. O leitor pode imaginar um código muito maiseficiente para computar a soma de números, mas o objetivo do código doexemplo é ilustrar o problema da concorrência em Servlets. O exemplo contémtambém um trecho de código para recebimento de valores de formulários, o queserá discutido mais adiante.

import java.util.*;import javax.servlet.*;import javax.servlet.http.*;

public class Soma extends HttpServlet {

Vector v = new Vector(5); protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, java.io.IOException { v.clear(); Enumeration e = req.getParameterNames();

while (e.hasMoreElements()) { String name = (String)e.nextElement(); String value = req.getParameter(name); if (value != null) v.add(value); }

res.setContentType("text/html"); java.io.PrintWriter out = res.getWriter(); out.println("<html>"); out.println("<head><title>Servlet</title></head>"); out.println("<body>"); out.println("<h1> A soma e'"); int soma =0; for(int i =0; i< v.size() ; i++) { soma += Integer.parseInt((String)v.get(i)); } out.println(soma); out.println("<h1>"); out.println("</body>");

Page 113: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

112

out.println("</html>"); out.close(); }}

Exemplo XX.XX- Servlet com problemas de concorrência.

Note que o Servlet utiliza uma variável de instância para referenciar oVector que armazena os valores. Se não forem usadas primitivas desincronização (como no código do exemplo) e duas requisições simultâneaschegarem ao Servlet o resultado pode ser inconsistente, uma vez que o Vectorpoderá conter parte dos valores de uma requisição e parte dos valores de outrarequisição. Neste caso, para corrigir esse problema basta declarar a variávelcomo local ao método doPost() ou usar primitivas de sincronização.

Obtendo Informações sobre a Requisição

O objeto HttpServletRequest passado para o Servlet contémvárias informações importantes relacionadas com a requisição, como porexemplo o método empregado (POST ou GET), o protocolo utilizado, oendereço remoto, informações contidas no cabeçalho e muitas outras. O Servletdo exemplo XX.XX retorna uma página contendo informações sobre arequisição e sobre o cabeçalho da requisição.

import java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;

public class RequestInfo extends HttpServlet{ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); out.println("<html><head>"); out.println("<title>Exemplo sobre Requisicao de Info </title>"); out.println("</head><body>"); out.println("<h3> Exemplo sobre Requisicao de Info </h3>"); out.println("Metodo: " + req.getMethod()+”<br>”); out.println("Request URI: " + req.getRequestURI()+”<br>”); out.println("Protocolo: " + req.getProtocol()+”<br>”);

Page 114: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

113

out.println("PathInfo: " + req.getPathInfo()+”<br>”); out.println("Endereco remoto: " + req.getRemoteAddr()+”<br><br>”); Enumeration e = req.getHeaderNames(); while (e.hasMoreElements()) { String name = (String)e.nextElement(); String value = req.getHeader(name); out.println(name + " = " + value+"<br>"); } out.println("</body></html>"); }

public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doGet(req, res); }}

Exemplo XX.XX- Servlet que retorna as informações sobre a requisição.

Note que o método doPost() chama o método doGet(), de modoque o Servlet pode receber os dois tipos de requisição. A figura XX.XX mostra oresultado de uma execução do Servlet do exemplo XX.XX.

Exemplo sobre Requisicao de Info

Metodo: GETRequest URI: /servlet/RequestInfoProtocolo: HTTP/1.0PathInfo: nullEndereco remoto: 127.0.0.1

Connection = Keep-AliveUser-Agent = Mozilla/4.7 [en] (Win95; I)Pragma = no-cacheHost = localhost:8080Accept = image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*Accept-Encoding = gzipAccept-Language = enAccept-Charset = iso-8859-1,*,utf-8

Figura XX.XX- Saída da execução do Servlet que exibe as informações sobre arequisição.

Page 115: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

114

Lidando com Formulários

Ser capaz de lidar com as informações contidas em formulários HTML éfundamental para qualquer tecnologia de desenvolvimento de aplicações paraWeb. É por meio de formulários que os usuários fornecem dados, preenchempedidos de compra e (ainda mais importante) digitam o número do cartão decrédito. As informações digitadas no formulário chegam até o Servlet por meiodo objeto HttpServletRequest e são recuperadas por meio do métodogetParameter() deste objeto. Todo item de formulário HTML possui umnome e esse nome é passado como argumento para o métodogetParameter() que retorna na forma de String o valor do item deformulário.

O Servlet do exemplo XX.XX exibe o valor de dois itens de formuláriosdo tipo text. Um denominado nome e o outro denominado de sobrenome.Em seguida o Servlet cria um formulário contendo os mesmos itens deformulário. Note que um formulário é criado por meio do tag <form>. Comoparâmetros opcionais deste tag temos método da requisição (method), é a URLpara onde será submetida a requisição (action). No caso do exemplo, ométodo adotado é o POST e a requisição será submetida ao próprio ServletForm.

import java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;

public class Form extends HttpServlet{ public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { res.setContentType("text/html");

PrintWriter out = res.getWriter(); out.println("<html>"); out.println("<head><title>Trata formulario</title></head>"); out.println("<body bgcolor=\"white\">");

out.println("<h3>Trata formulario</h3>"); String nome = req.getParameter("nome");

Page 116: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

115

String sobreNome = req.getParameter("sobrenome"); if (nome != null || sobreNome != null) { out.println("Nome = " + nome + "<br>"); out.println("Sobrenome = " + sobreNome); } out.println("<P>"); out.print("<form action=\"Form\" method=POST>"); out.println("Nome : <input type=text size=20 name=nome><br>"); out.println("Sobrenome: <input type=text size=20 name=sobrenome><br>"); out.println("<input type=submit>"); out.println("</form>"); out.println("</body></html>"); }

public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doGet(req, res); }}

Exemplo XX.XX- Servlet para lidar com um formulário simples.

Lidando com Cookies

Um cookie nada mais é que um bloco de informação que é enviado doservidor para o navegador no cabeçalho página. A partir de então, dependendodo tempo de validade do cookie, o navegador reenvia essa informação para oservidor a cada nova requisição. Dependo do caso o cookie é tambémarmazenado no disco da máquina cliente e quando o site é novamente visitado ocookie enviado novamente para o servidor, fornecendo a informação desejada.

Os cookies foram a solução adotada pelos desenvolvedores do Netscapepara implementar a identificação de clientes sobre um protocolo HTTP que nãoé orientado à conexão. Esta solução, apesar das controvérsias sobre apossibilidade de quebra de privacidade, passou ser amplamente adotada e hojeos cookies são parte integrante do padrão Internet, normalizados pela normaRFC 2109.

A necessidade da identificação do cliente de onde partiu a requisição e omonitoramento de sua interação com o site (denominada de sessão) é importantepara o desenvolvimento de sistemas para a Web pelas seguintes razões:

Page 117: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

116

• É necessário associar os itens selecionados para compra com ousuário que deseja adquiri-los. Na maioria da vezes a seleção dositens e compra é feita por meio da navegação de várias páginas dosite e a todo instante é necessário distinguir os usuários que estãorealizando as requisições.

• É necessário acompanhar as interação do usuário com o site paraobservar seu comportamento e, a partir dessas informações, realizaradaptações no site para atrair um maior número de usuários ourealizar campanhas de marketing.

• É necessário saber que usuário está acessando o site para, de acordocom o seu perfil, fornecer uma visualização e um conjunto defuncionalidades adequadas às suas preferências.

Todas essas necessidades não podem ser atendidas com o uso básico doprotocolo HTTP, uma vez que ele não é orientado à sessão ou conexão. Com oscookies é possível contornar essa deficiência, uma vez que as informações quesão neles armazenadas podem ser usadas para identificar os clientes. Existemoutras formas de contornar a deficiência do protocolo de HTTP, como acodificação de URL e o uso de campos escondidos nas páginas HTML, mas ouso de cookies é a técnica mais utiliza, por ser mais simples e padronizada. Noentanto, o usuário pode impedir que o navegador aceite cookies, o que torna oato de navegar pela Web muito desagradável. Neste caso, é necessário utilizar asoutras técnicas para controle de sessão.

A API Servlet permite a manipulação explicita de cookies. Para controlede sessão o programador pode manipular diretamente os cookies, ou usar umaabstração de nível mais alto, implementada por meio do objeto HttpSession.Se o cliente não permitir o uso de cookies a API Servlet fornece métodos para acodificação de URL. O exemplo XX.XX mostra o uso de cookies paraarmazenar as informações digitadas em um formulário.

import java.io.*;import javax.servlet.*;import javax.servlet.http.*;

public class CookieTeste extends HttpServlet{ public void doGet(HttpServletRequest req, HttpServletResponseres) throws IOException, ServletException {

Page 118: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

117 res.setContentType("text/html");

PrintWriter out = res.getWriter(); out.println("<html>"); out.println("<body bgcolor=\"white\">"); out.println("<head><title>Teste de Cookies</title></head>"); out.println("<body>");

out.println("<h3>Teste de Cookies</h3>");

Cookie[] cookies = req.getCookies(); if (cookies.length > 0) { for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; out.print("Cookie Nome: " + cookie.getName() +"<br>"); out.println(" Cookie Valor: " + cookie.getValue() +"<br><br>"); } }

String cName = req.getParameter("cookienome"); String cValor = req.getParameter("cookievalor"); if (cName != null && cValor != null) { Cookie cookie = new Cookie(cName ,cValor); res.addCookie(cookie); out.println("<P>"); out.println("<br>"); out.print("Nome : "+cName +"<br>"); out.print("Valor : "+cValor); }

out.println("<P>"); out.print("<form action=\"CookieTeste\" method=POST>"); out.println("Nome : <input type=text length=20 name=cookienome><br>"); out.println("Valor : <input type=text length=20 name=cookievalor><br>"); out.println("<input type=submit></form>"); out.println("</body>"); out.println("</html>"); }

public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doGet(req, res); }}

Page 119: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

118

Exemplo XX.XX- Servlet para lidar com Cookies.

Para se criar um cookie é necessário criar um objeto Cookie, passandopara o construtor um nome e um valor, sendo ambos instâncias de String. Ocookie é enviado para o navegador por meio do método addCookie() doobjeto HttpServletResponse. Um vez que os cookies são enviados nocabeçalho da página, o método addCookie() deve ser chamado antes doenvio de qualquer conteúdo para o navegador. Para recuperar os cookiesenviados pelo navegador usa-se o método getCookies() do objetoHttpServletRequest que retorna um array de Cookie. Os métodosgetName() e getvalue() do objeto Cookie são utilizados para recuperaro nome o valor da informação associada ao cookie.

Os objetos da classe Cookie possuem vários métodos para controle douso de cookies. É possível definir tempo de vida máximo do cookie, os domíniosque devem receber o cookie (por default o domínio que deve receber o cookie éo que o criou), o diretório da página que deve receber o cookie, se o cookie deveser enviado somente sob um protocolo seguro e etc. Por exemplo, para definir aidade máxima de um cookie devemos utilizar o método setMaxAge(),passando um inteiro como parâmetro. Se o inteiro for positivo indicará emsegundos o tempo máximo de vida do cookie. Um valor negativo indica que ocookie deve apagado quando o navegador terminar. O valor zero indica que ocookie deve ser apagado imediatamente. O trecho de código exemplo XX.XXmostra algumas alterações no comportamento default de um cookie.

...Cookie cookie = new Cookie(cName ,cValor);cookie.setDomain(“*.uvf.br”); // todos os domínios como dpi.ufv.br mas não *.dpi.ufv.brcookie.setMaxAge (3600); // uma hora de tempo de vida...

Exemplo XX.XX- Mudanças no comportamento default do cookie.

Lidando com Sessões

A manipulação direta de cookies para controle de sessão é um tanto baixonível, uma vez que o usuário deve se preocupar com a identificação, tempo devida e outros detalhes. Por isso a API Servlet fornece um objeto com controles

Page 120: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

119

de nível mais alto para monitorar a sessão, o HttpSession. O objetoHttpSession monitora a sessão utilizando cookies de forma transparente. Noentanto, se o cliente não aceitar o uso de cookies é possível utilizar comoalternativa a codificação de URL para adicionar o identificador da sessão. Essaopção, apesar de ser mais genérica, não á primeira opção devido a possibilidadede criação de gargalos pela necessidade da análise prévia de todas requisiçõesque chegam ao servidor. O exemplo XX.XX mostra o uso de um objetoHttpSession para armazenar as informações digitadas em um formulário.

import java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;

public class SessionTeste extends HttpServlet{ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { resp.setContentType("text/html");

PrintWriter out = resp.getWriter(); out.println("<html><head>"); out.println("<title>Teste de Sessao</title>"); out.println("</head>"); out.println("<body>"); out.println("<h3>Teste de Sessao</h3>"); HttpSession session = req.getSession(true); out.println("Identificador: " + session.getId()); out.println("<br>"); out.println("Data: "); out.println(new Date(session.getCreationTime()) + "<br>"); out.println("Ultimo acesso: "); out.println(new Date(session.getLastAccessedTime()));

String nomedado = req.getParameter("nomedado"); String valordado = req.getParameter("valordado"); if (nomedado != null && valordado != null) { session.setAttribute(nomedado, valordado); }

out.println("<P>"); out.println("Dados da Sessao:" + "<br>"); Enumeration valueNames = session.getAttributeNames();

Page 121: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

120

while (valueNames.hasMoreElements()) { String name = (String)valueNames.nextElement(); String value = (String) session.getAttribute(name); out.println(name + " = " + value+"<br>"); }

out.println("<P>"); out.print("<form action=\"SessionTeste\" method=POST>"); out.println("Nome: <input type=text size=20 name=nomedado><br>"); out.println("Valor: <input type=text size=20 name=valordado><br>"); out.println("<input type=submit>"); out.println("</form>"); out.println("</body></html>"); }

public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { doGet(req, resp); }}

Exemplo XX.XX- Servlet para lidar com Sessões.

Para controlar a sessão é necessário obter um objeto HttpSession pormeio do método getSession() do objeto HttpServletRequest. Opcionalmente, ométodo getSession() recebe como argumento um valor booleano que indica se épara criar o objeto HttpSession se ele não existir (argumento true) ou se é pararetorna null caso ele não exista (argumento false). Para se associar um objeto ouinformação à sessão usa-se o método setAttribute() do objeto HttpSession,passando para o método um String e um objeto que será identificado pelo String.Note que o método aceita qualquer objeto e, portanto, qualquer objeto pode serassociado à sessão. Os objetos associados a uma sessão são recuperados com ouso método getAttribute() do objeto HttpSession, que recebe como argumento onome associado ao objeto. Para se obter uma enumeração do nomes associados àsessão usa-se o método getAttributeNames() do objeto HttpSession.

A figura XX.XX mostra o resultado da execução do exemplo XX.XX.

Page 122: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

121

Teste de Sessao

Identificador: session3Data: Sun May 28 15:19:15 GMT-03:00 2000Ultimo acesso: Sun May 28 15:19:43 GMT-03:00 2000

Dados da Sessao:Alcione = 4Alexandra = 6NomeValor

Figura VI-1 Saída resultante da execução do Servlet que lida com Sessões.

Enviar Consulta

Page 123: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

122

JSP

Servlets é uma boa idéia, mas você se imaginou montando uma páginacomplexa usando println()? Muitas vezes o desenvolvimento de um site éuma tarefa complexa que envolve vários profissionais. A tarefa de projeto dolayout da página fica a cargo do Web Designer, incluindo a diagramação dostextos e imagens, aplicação de cores, tratamento das imagens, definição daestrutura da informação apresentada no site e dos links para navegação pelamesma. Já o Desenvolvedor Web é responsável pela criação das aplicações quevão executar em um site. O trabalho destes dois profissionais é somado nacriação de um único produto, mas durante o desenvolvimento a interferênciamutua deve ser a mínima possível. Ou seja, um profissional não deve precisaralterar o que é foi feito pelo outro profissional para cumprir sua tarefa. Atecnologia Servlet não nos permite atingir esse ideal. Por exemplo, suponha queum Web Designer terminou o desenvolvimento de uma página e a entregou parao Desenvolvedor Web codificar em um Servlet. Se após a codificação o WebDesigner desejar realizar uma alteração na página será necessário que ele altere ocódigo do Servlet (do qual ele nada entende) ou entregar uma nova página para oDesenvolvedor Web para que ele a codifique totalmente mais uma vez. Qualqueruma dessas alternativas são indesejáveis e foi devido a esse problema a Sundesenvolveu uma tecnologia baseada em Servlets chamada de JSP.

Java Server Pages (JSP) são páginas HTML que incluem código Java eoutros tags especiais. Desta forma as partes estáticas da página não precisam sergeradas por println(). Elas são fixadas na própria página. A parte dinâmicaé gerada pelo código JSP. Assim a parte estática da página pode ser projetadapor um Web Designer que nada sabe de Java.

A primeira vez que uma página JSP é carregada pelo container JSP ocódigo Java é compilado gerando um Servlet que é executado, gerando umapágina HTML que é enviada para o navegador. As chamadas subsequentes sãoenviadas diretamente ao Servlet gerado na primeira requisição, não ocorrendomais as etapas de geração e compilação do Servlet.

A figura XX.XX mostra um esquema das etapas de execução de umapágina JSP na primeira vez que é requisitada. Na etapa (1) a requisição é enviadapara um servidor Web que reencaminha a requisição (etapa 2) para o containerServlet/JSP. Na etapa (3) o container verifica que não existe nenhuma instânciade Servlet correspondente à 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

Page 124: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

123

instância da classe. Finalmente, na etapa (6) é invocado o método service()da instância Servlet para gerar a resposta à requisição.

Figura VI-1 Etapas da primeira execução de uma página JSP.

A idéia de se usar scripts de linguagens de programação em páginasHTML que são processados no lado servidor para gerar conteúdo dinâmico nãoé restrita à linguagem Java. Existem várias soluções desse tipo fornecida poroutros fabricantes. Abaixo segue uma comparação de duas das tecnologias maispopulares com JSP.

PHP X JSP

PHP (Personal Home Pages) é uma linguagem script para ser executadano lado servidor criada em 1994 como um projeto pessoal de Rasmus Lerdorf.Atualmente encontra-se na versão 4. A sintaxe é fortemente baseada em C maspossui elementos de C++, Java e Perl. Possui suporte à programação OO pormeio de classes e objetos. Possui também suporte extensivo à Banco de dadosODBC, MySql, Sybase, Oracle e outros. PHP é uma linguagem mais fácil nodesenvolvimento de pequenas aplicações para Web em relação à JSP, uma vez

ServidorHttp

(1)Requisição de

página JSP

ContainerServlet/JSP Página

jsp

FonteServlet

(3)Traduz

(2)Encaminha a requisição

(5)Instancia e

executa

(6)Resposta àrequisição

Navegador

BytecodeServlet

(4)Compila

Page 125: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

124

que é uma linguagem mais simples e menos rígida do que JSP. No entanto, amedida que passamos para aplicações de maior porte, o uso de PHP não éindicado, uma vez que necessário o uso de linguagens com checagem maisrígidas e com maior suporte à escalabilidade, como é o caso de Java.

ASP X JSP

ASP (Active Server Pages) é a solução desenvolvida pela Microsoft®para atender as requisições feitas à servidores Web. Incorporada inicialmenteapenas ao Internet Information Server (IIS), no entanto, atualmente já ésuportada por outros servidores populares, como o Apache. O desenvolvimentode páginas que usam ASP envolve a produção de um script contendo HTMLmisturado com blocos de código de controle ASP. Este código de controle podeconter scripts em JavaScript ou VBScript. A primeira vantagem de JSP sobreASP é que a parte dinâmica é escrita em Java e não Visual Basic ou outralinguagem proprietária da Microsoft, portanto JSP é mais poderoso e fácil deusar. Em segundo lugar JSP é mais portável para outros sistemas operacionais eservidores WEB que não sejam Microsoft.

Primeiro exemplo em JSP

Para que o leitor possa ter uma idéia geral da tecnologia JSPapresentaremos agora a versão JSP do Olá mundo. O exemplo XX.XX mostra ocódigo da página.

<html> <head> <title>Exemplo JSP</title> </head> <body> <% String x = "Ol&aacute; Mundo!"; %> <%=x%> </body></html>

Exemplo XX.XX- Versão JSP do Olá mundo.

Page 126: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

125

Quem está habituado aos tags HTML notará que se trata basicamente deuma página HTML contendo código Java delimitado pelos símbolos “<%” e“%>”. Para facilitar a visualização destacamos os scripts Java com negrito. Noprimeiro trecho de script é declarada uma variável x com o valor “Olá mundo”(a seqüência &acute; é denota ‘á’ em HTML). No segundo trecho de script oconteúdo da variável x é extraído e colocado na página resultante da execuçãodo Servlet correspondente. Em seguida mostraremos como executar o exemploXX.XX.

Executando o arquivo JSP

Para executar o exemplo XX.XX salve-o com a extensão .jsp. Porexemplo ola.jsp. Se você estiver usando o servidor Tomcat, coloque-oarquivo no subdiretório /webapps/examples/jsp do Tomcat. Por exemploexamples/jsp/teste. Para invocar o arquivo JSP basta embutir a URL emuma página ou digitar diretamente a seguinte URL no navegador.

http://localhost:8080/examples/jsp/ola.jsp

Usamos o diretório /webapps/examples/jsp para testar rapidamenteo exemplo. Para desenvolver uma aplicação é aconselhável criar um diretórioapropriado como mostrado na seção que tratou de Servlets.

O Servlet criado a partir da página JSP é colocado em um diretório detrabalho. No caso do Tomcat o Servlet é colocado em subdiretório associado àaplicação subordinado ao diretório /work do Tomcat. O exemplo XX.XXmostra os principais trechos do Servlet criado a partir da tradução do arquivoola.jsp pelo tradutor do Tomcat. Note que o Servlet é subclasse de umaclasse HttpJspBase e não da HttpServlet. Além disso, o método queexecutado em resposta à requisição é o método _jspService() e não ométodo service(). Note também que todas as partes estáticas da página JSPsão colocadas como argumentos do método write() do objeto referenciadoout.

Page 127: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

126

public class _0002fjsp_0002fola_00032_0002ejspola_jsp_0 extends HttpJspBase { ....public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { .... PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null; ... try { ... out.write("<html>\r\n <head>\r\n <title>Exemplo JSP</title>\r\n</head>\r\n <body>\r\n"); String x = "Ol&aacute; Mundo!";

out.write("\r\n"); out.print(x); out.write("\r\n </body>\r\n</html>\r\n"); ... } catch (Exception ex) { ... } }}

Exemplo XX.XX- Servlet correspondente à página JSP do Olá mundo.

Objetos implícitos

No exemplo XX.XX pode-se ver a declaração de variáveis quereferenciam a alguns objetos importantes. Estas variáveis estão disponíveis parao projetista da página JSP. As variáveis mais importantes são:

Classe VariávelHttpServletRequestHttpServletResponsePageContextServletContextHttpSessionJspWriter

requestresponsepageContextapplicationsessionout

Page 128: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

127

Os objetos referenciados pelas variáveis request e response játiveram seu uso esclarecido na seção sobre Servlets. O objeto do tipoJspWriter tem a mesma função do PrinterWriter do Servlet. Os outrosobjetos terão sua função esclarecida mais adiante.

Tags JSP

Os tags JSP possuem a seguinte forma geral:

<% Código JSP %>

O primeiro caractere % pode ser seguido de outros caracteres quedeterminam o significado preciso do código dentro do tag. Os tags JSP possuemcorrespondência com os tags XML. Existem cinco categorias de tags JSP:

ExpressõesScriptletsDeclaraçõesDiretivasComentários

Em seguida comentaremos cada uma dessas categorias.

Expressões

<%= expressões %>

Expressões são avaliadas, convertidas para String e colocadas napágina enviada. A avaliação é realizada em tempo de execução, quando a páginaé requisitada.

Exemplos:<%= new java.util.Date() %><%= request.getMethod() %>

No primeiro exemplo será colocado na página a data corrente emmilésimo de segundos e no segundo será colocado o método usado na

Page 129: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

128

requisição. Note que cada expressão contém apenas um comando Java. Notetambé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 é preciso usar outracategoria de tags JSP: os Scriptlets . Os Scriptlets permitem inserir trechos decódigo em Java na página JSP. O exemplo XX.XX mostra uma página JSPcontendo um Scriptlet que transforma a temperatura digitada em celcius para oequivalente em Fahrenheit.

<html><head><title>Conversao Celcius Fahrenheit </title></head><body>

<% String valor = request.getParameter("celcius"); if (valor != null ) { double f = Double.parseDouble(valor)*9/5 +32; out.println("<P>"); out.println("<h2>Valor em Fahrenheit:" +f +"<h2><br>"); }%><form action=conversao.jsp method=POST> Celcius: <input type=text size=20 name=celcius><br> <input type=submit></form>

</body></html>

Exemplo XX.XX- Página JSP que converte graus Celcius para Fahrenheit.

Note o uso das variáveis request e out sem a necessidade dedeclaração. Todo o código digitado é inserido no método _jspService(). Afigura XX.XX mostra o resultado da requisição após a digitação do valor 30 nacaixa de texto do formulário.

Page 130: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

129

Figura VI-XX Resultado da conversão de 30 graus celcius.

O código dentro do scriptlet é inserido da mesma forma que é escrito etodo o texto HTML estático antes e após ou um scriptlet é convertido paracomandos print(). Desta forma o scriptlets não precisa conter comandos paracódigo estático e blocos de controle abertos afetam o código HTML envolvidospor scriptlets. O exemplo XX.XX mostra dois formas de se produzir o mesmoefeito. No código da esquerda os Scriplets se intercalam com código HTML. Ocódigo HTML, quando da tradução da página JSP para Servlet é inserido comoargumentos de métodos println() gerando o código da direita. Ambas asformas podem ser usadas em páginas JSP e produzem o mesmo 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 XX.XX- Dois códigos equivalentes.

Declarações

<%! Código Java %>

Valor em Fahrenheit:86.0

Celcius:

Enviar Consulta

Page 131: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

130

Uma declaração JSP permite definir variáveis ou métodos que sãoinseridos no corpo do Servlet. Como as declarações não geram saída, elas sãonormalmente usadas em combinação com expressões e scriptlets. O ExemploXX.XX mostra a declaração de uma variável que é usada para contar o númerode vezes que a página corrente foi requisitada desde que foi carregada.

<%! Private int numAcesso = 0; %>Acessos desde carregada:<%= ++ numAcesso %>

Exemplo XX.XX- Declaração de uma variável usando o tag de declaração.

As variáveis declaradas desta forma serão variáveis de instância. Já asvariáveis declaradas em Scriptlets são variáveis locais ao método_jspService(). Por isso é possível contar o número de requisições com oexemplo XX.XX. Se variável fosse declarada em um Scriptlet a variável serialocal ao método _jspService() e, portanto, teria seu valor reinicializado acada chamada.

Como já foi dito, os tags de declarações permitem a declaração demétodos. O Exemplo XX.XX mostra a declaração de um método que convertecelcius para Fahrenheit.

<%! private double converte(double c) { return c*9/5 +32; } %>

Exemplo XX.XX- Declaração de um método para a conversão de celcius paraFahrenheit.

Comentários

Page 132: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

131

Existem dois tipos de comentários utilizados em páginas JSP. O primeiroexclui todo o bloco comentado da saída gerada pelo processamento da página. Aforma geral deste tipo de comentário é a seguinte:

<%--comentário --%>

O segundo tipo de comentário é o utilizado em páginas HTML. Nestecaso o comentário é enviado dentro da página de resposta. A forma geral destetipo de comentário é a seguinte:

<!—comentário -->

Diretivas

Diretivas são mensagens para JSP container. Elas não enviam nada para apágina mas são importantes para definir atributos JSP e dependências com o JSPcontainer. A forma geral da diretivas é a seguinte:

<%@ Diretiva atributo="valor" %>

ou<%@ Diretiva atributo1 ="valor1" atributo2 ="valor2" ... atributoN =" valorN " %>

Em seguida comentaremos as principais diretivas.

Diretiva page

<%@ page atributo1 ="valor1" ... atributoN =" valorN " %>

A diretiva page permite a definição dos seguintes atributos:

importcontentTypeisThreadSafe

Page 133: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

132

sessionbufferautoflushinfoerrorPageisErrorPagelanguage

Segue a descrição de cada um desses atributos.

Atributo e Forma Geral Descriçãoimport="package.class"

ou

import="package.class1,...,package.classN"

Permite especificar os pacotes que devem serimportados para serem usados na página JSP.

Exemplo:

<%@ page import="java.util.*" %>

contentType="MIME-Type" Especifica o tipo MIME da saída. O default é 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 processamentonormal do Servlet, onde múltiplas requisições sãoprocessadas simultaneamente. Um valor false indica queo processamento deve ser feito por instancias separadasdo Servlet ou serialmente.

session="true|false” Um valor true (default) indica que a variávelpredefinida session (HttpSession) deve serassociada à sessão, se existir, caso contrário uma novasessão deve ser criada e associada a ela. Um valor falseindica que nenhuma sessão será usada.

buffer="sizekb|none" Especifica o tamanho do buffer para escrita usado peloobjeto JspWriter. O tamanho default não é menor que8k..

autoflush="true|false” Um valor true (default) indica que o buffer deve ser

Page 134: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

133

esvaziado quando estiver cheio.info="mensagem" Define uma cadeia de caracteres que pode ser

recuperada via getServletInfo().errorPage="url” Especifica a página JSP que deve ser processada em

caso de exceções não capturadas.isErrorPage="true|false” Indica se a página corrente pode atuar como página de

erro para outra página JSP. O default é false.Language="java” Possibilita definir a linguagem que está sendo usada. No

momento a única possibilidade é Java.

Tabela VI.XX –Atributos da diretiva page.

Diretiva include

<%@ include file="relative url" %>

Permite incluir arquivos no momento em que a página JSP é traduzidaem um Servlet.

Exemplo:

<%@ include file="/meuarq.html" %>

Extraindo Valores de Formulários

Uma página JSP, da mesma forma que um Servlet, pode usar o objetoreferenciado pela variável request para obter os valores dos parâmetros de umformulário. O exemplo XX.XX usado para converter graus Celcius emFahrenheit fez uso deste recurso. O exemplo XX.XX mostra outra página JSPcom formulário. Note que o scriptlet é usado para obter o nome e os valores detodos os parâmetros contidos no formulário. Como o métodogetParameterNames() retorna uma referência a um objetoEnumeration é preciso importar o pacote java.util, por meio da diretivapage.

<%@ page import="java.util.*" %><html><body><H1>Formulário</H1><%

Page 135: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

134 Enumeration campos = request.getParameterNames(); While(campos.hasMoreElements()) { String campo = (String)campos.nextElement(); String valor = request.getParameter(campo); %> <li><%= campo %> = <%= valor %></li><% } %>

<form method="POST" action="form.jsp"> Nome: <input type="text" size="20" name="nome" ><br> Telefone: <input type="text" size="20" name="telefone"><br> <INPUT TYPE=submit name=submit value="envie"> </form></body></html>

Exemplo VI.XX – Página JSP com formulário.

A figura XX.XX mostra o resultado da requisição após a digitação dosvalores Alcione e 333-3333 nas caixas de texto do formulário.

Figura VI.XX- Saída do exemplo XX.XX.

Criando e Modificando Cookies

Formulário

• telefone = 333-3333• nome = Alcione• submit = envie

Nome:

Telefone:

envie

Page 136: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

135

Da mesma for que em Servlets os cookies em JSP são tratados por meioda classe Cookie. Para recuperar os cookies enviados pelo navegador usa-se ométodo getCookies() do objeto HttpServletRequest que retorna umarranjo de Cookie. Os métodos getName() e getvalue() do objetoCookie são utilizados para recuperar o nome o valor da informação associadaao cookie. O cookie é enviado para o navegador por meio do métodoaddCookie() do objeto HttpServletResponse. O exemplo XX.XXmostra uma página JSP que exibe todos os cookies recebidos em uma requisiçãoe adiciona mais um na resposta.

<html><body><H1>Session id: <%= session.getId() %></H1><% Cookie[] cookies = request.getCookies(); For(int i = 0; i < cookies.length; i++) { %> Cookie name: <%= cookies[i].getName() %> <br> Value: <%= cookies[i].getValue() %><br> antiga idade máxima em segundos: <%= cookies[i].getMaxAge() %><br> <% cookies[i].setMaxAge(5); %> nova idade máxima em segundos: <%= cookies[i].getMaxAge() %><br><% } %><%! Int count = 0; int dcount = 0; %><% response.addCookie(new Cookie(”Cookie " + count++, ”Valor " + dcount++)); %></body></html>

Exemplo VI.XX – Página JSP que exibe os cookies recebidos.

A figura XX.XX mostra o resultado após três acessos seguidos à páginaJSP. Note que existe um cookie a mais com o nome JSESSIONID e valor igualà sessão. Este cookie é o usado pelo container para controlar a sessão.

Session id: 9ppfv0lsl1Cookie name: Cookie 0value: Valor 0antiga idade máxima em segundos: -1nova idade máxima em segundos: 5Cookie name: Cookie 1

Page 137: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

136

value: Valor 1antiga idade máxima em segundos: -1nova idade máxima em segundos: 5Cookie name: JSESSIONIDvalue: 9ppfv0lsl1antiga idade máxima em segundos: -1nova idade máxima em segundos: 5

Figura VI.XX- Saída do exemplo XX.XX após três acessos.

Lidando com sessões

O atributos de uma sessão são mantidos em um objeto HttpSessionreferenciado pela variável session. Pode-se armazenar valores em uma sessãopor meio do método setAttribute() e recuperá-los por meio do métodogetAttribute(). O tempo de duração default de uma sessão inativa (sem orecebimento de requisições do usuário) é 30 minutos mas esse valor pode seralterado por meio do método setMaxInactiveInterval(). O exemploXX.XX mostra duas páginas JSP. A primeira apresenta um formulário ondepodem ser digitados dois valores recebe dois valores de digitados em umformulário e define o intervalo máximo de inatividade de uma sessão em 10segundos. A segunda página recebe a submissão do formulário, insere os valoresna sessão e apresenta os valores relacionados com a sessão assim como aidentificação da sessão.

<%@ page import="java.util.*" %><html><body><H1>Formulário</H1><H1>Id da sess&atilde;o: <%= session.getId() %></H1><H3><li>Essa sess&atilde;o foi criada em<%= session.getCreationTime() %></li></H3>

<H3><li>Antigo intervalo de inatividade = <%= session.getMaxInactiveInterval() %></li><% session.setMaxInactiveInterval(10); %><li>Novo intervalo de inatividade= <%= session.getMaxInactiveInterval() %></li></H3>

Page 138: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

137

<% Enumeration atribs = session.getAttributeNames(); while(atribs.hasMoreElements()) { String atrib = (String)atribs.nextElement(); String valor = (String)session.getAttribute(atrib); %> <li><%= atrib %> = <%= valor %></li><% } %>

<form method="POST" action="sessao2.jsp"> Nome: <input type="text" size="20" name="nome" ><br> Telefone: <input type="text" size="20" name="telefone" > <br> <INPUT TYPE=submit name=submit value="envie"> </form></body></html>

<html><body><H1>Id da sess&atilde;o: <%= session.getId() %></H1><% String nome = request.getParameter("nome"); String telefone = request.getParameter("telefone");

if (nome !=null && nome.length()>0) session.setAttribute("nome",nome); if (telefone !=null &&telefone.length()>0) session.setAttribute("telefone",telefone);%>

<FORM TYPE=POST ACTION=sessao1.jsp><INPUT TYPE=submit name=submit Value="Retorna"></FORM></body></html>

Exemplo VI.XX – Exemplo do uso de sessão.

O exemplo XX.XX mostra que a sessão é mantida mesmo quandoo usuário muda de página. As figura XX.XX e XX.XX mostram o resultado darequisição após a digitação dos valores Alcione e 333-3333 nas caixas detexto do formulário, à submissão para página sessao2.jsp e o retorno àpágina sessao1.jsp.

Page 139: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

138

Figura VI.XX- Tela da página sessao1.jsp.

Figura VI.XX- Tela da página sessao2.jsp.

O Uso de JavaBeans

FormulárioId da sessão: soo8utc4m1Essa sessão foi criada em 1002202317590Antigo intervalo de inatividade = 1800Novo intervalo de inatividade= 10

telefone = 333-3333nome = Alcione

Nome:

Telefone:

envie

Id da sessão: soo8utc4m1Retorna

Page 140: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

139

A medida que o código Java dentro do HTML torna-se cada vez maiscomplexo o desenvolvedor pode-se perguntar: Java em HTML não é o problemainvertido do HTML em Servlet? O resultado não será tão complexo quantoproduzir uma página usando println()? Em outras palavras, estounovamente misturando conteúdo com forma?

Para solucionar esse problema a especificação de JSP permite o uso deJavaBeans para manipular a parte dinâmica em Java. JavaBeans já foramdescritos detalhadamente em um capítulo anterior, mas podemos encarar umJavaBean como sendo apenas uma classe Java que obedece a uma certapadronização de nomeação de métodos, formando o que é denominado depropriedade. As propriedades de um bean são acessadas por meio de métodosque obedecem a convenção getXxxx e setXxxx. , onde Xxxx é o nome dapropriedade. Por exemplo, getItem() é o método usado para retornar o valorda propriedade item. A sintaxe para o uso de um bean em uma página JSP é:

<jsp:useBean id="nome" class="package.class" />

Onde nome é o identificador da variável que conterá uma referência parauma instância do JavaBean. Você também pode modificar o atributo scopepara estabelecer o escopo do bean além da página corrente.

<jsp:useBean id="nome" scope="session" class="package.class" />

Para modificar as propriedades de um JavaBean você pode usar ojsp:setProperty ou chamar um método explicitamente em umscriptlet. Para recuperar o valor de uma propriedade de um JavaBean vocêpode usar o jsp:getProperty ou chamar um método explicitamente emum scriptlet. Quando é dito que um bean tem uma propriedade prop dotipo T significa que o bean deve prover um método getProp() e um métododo tipo setProp(T). O exemplo XX.XX mostra uma página JSP e umJavaBean. A página instancia o JavaBean, altera a propriedade mensagem erecupera o valor da propriedade, colocando-o na página.

Página bean.jsp

<HTML> <HEAD><TITLE>Uso de beans</TITLE></HEAD> <BODY> <CENTER>

Page 141: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

140

<TABLE BORDER=5> <TR><TH CLASS="TITLE"> Uso de JavaBeans </TABLE></CENTER> <P><jsp:useBean id="teste" class=”curso.BeanSimples" /><jsp:setProperty name="teste" property="mensagem" value=”Ola mundo!" /><H1> Mensagem: <I><jsp:getProperty name="teste" property="mensagem" /> </I></H1></BODY> </HTML>

Arquivo Curso/BeanSimples.java

package curso;

public class BeanSimples { private String men = "Nenhuma mensagem";

public String getMensagem() { return(men); }

public void setMensagem(String men) { this.men = men; }}

Exemplo VI.XX – Exemplo do uso de JavaBean.

A figura XX.XX mostra o resultado da requisição dirigida à páginabean.jsp.

Figura VI.XX- Resultado da requisição à página bean.jsp.

Se no tag setProperty usarmos o valor “*” para o atributoproperty então todos os valores de elementos de formulários que possuíremnomes iguais à propriedades serão transferidos para as respectivas propriedades

Mensagem: Ola mundo!

Uso de JavaBeans

Page 142: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

141

no momento do processamento da requisição. Por exemplo, seja uma página jspcontendo um formulário com uma caixa de texto com nome mensagem, comomostrado no exemplo XX.XX. Note que, neste caso, a propriedade mensagemdo JavaBean tem seu valor atualizado para o valor digitado na caixa de texto,sem a necessidade de uma chamada explícita no tag setProperty. Osvalores são automaticamente convertidos para o tipo correto no bean.

<HTML> <HEAD><TITLE>Uso de beans</TITLE> </HEAD><BODY> <CENTER><TABLE BORDER=5> <TR><TH CLASS="TITLE"> Uso de JavaBeans </TABLE></CENTER> <P><jsp:useBean id="teste" class="curso.BeanSimples" /><jsp:setProperty name="teste" property="*" /><H1> Mensagem: <I><jsp:getProperty name="teste" property="mensagem" /></I></H1>

<form method="POST" action="bean2.jsp"> Texto: <input type="text" size="20" name="mensagem" ><br> <INPUT TYPE=submit name=submit value="envie"></form>

</BODY> </HTML>

Exemplo VI.XX – Exemplo de atualização automática da propriedade.

A figura XX.XX mostra o resultado da requisição dirigida à páginabean2.jsp após a digitação do texto Olá!

Figura VI.XX- Resultado da requisição à página bean2.jsp.

Mensagem: Ola!

Uso de JavaBeans

envie

Texto:

Page 143: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

142

Escopo

Existem quatro valores possíveis para o escopo de um objeto: page,request, session e application. O default é page. A tabelaXX.XX descreve cada tipo de escopo.

Escopo Descriçãopage Objetos declarados com nesse 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 foremdeclarados. Objetos declarados com escopo page sãoreferenciados pelo objeto pagecontext.

request Objetos declarados com nesse escopo são válidos durantea requisição e são acessíveis mesmo quando a requisiçãoé encaminhada para outro programa no mesmo ambiente.Objetos declarados com escopo request sãoreferenciados pelo objeto request.

session Objetos declarados com nesse escopo são válidos durantea sessão desde que a página seja definida para funcionarem uma sessão. Objetos declarados com escoposession são referenciados pelo objeto session.

application Objetos declarados com nesse escopo são acessíveis porpáginas no mesmo servidor de aplicação. Objetosdeclarados com escopo application sãoreferenciados pelo objeto application.

Tabela VI.XX –Escopo dos objetos nas páginas JSP.

Implementação de um Carrinho de compras

O exemplo abaixo ilustra o uso de JSP para implementar um carrinho decompras virtual. O carrinho de compras virtual simula um carrinho de comprasde supermercado, onde o cliente vai colocando os produtos selecionados paracompra até se dirigir para o caixa para fazer o pagamento. No carrinho decompras virtual os itens selecionados pelo usuário são armazenados em umaestrutura de dados até que o usuário efetue o pagamento. Esse tipo de exemplo

Page 144: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

143

exige que a página JSP funcione com o escopo session para manter ocarrinho de compras durante a sessão. O exemplo XX.XX mostra um exemplosimples de implementação de carrinho de compras. O exemplo é composto pordois arquivos: um para a página JSP e um para o JavaBean que armazena ositens selecionados.

Página compras.jsp

<html><jsp:useBean id="carrinho" scope="session" class="compra.Carrinho" /><jsp:setProperty name="carrinho" property="*" /><body bgcolor="#FFFFFF">

<% carrinho.processRequest(request); String[] items = carrinho.getItems(); if (items.length>0) {%> <font size=+2 color="#3333FF">Voc&ecirc; comprou os seguintes itens:</font> <ol> <% for (int i=0; i<items.length; i++) { out.println("<li>"+items[i]); } }%></ol><hr><form type=POST action= compras.jsp> <br><font color="#3333FF" size=+2>Entre um item para adicionar ou remover: </font><br> <select NAME="item"> <option>Televis&atilde;o <option>R&aacute;dio <option>Computador <option>V&iacute;deo Cassete </select> <p><input TYPE=submit name="submit" value="adicione"> <input TYPE=submit name="submit" value="remova"></form></body></html>

JavaBean compra/Carrinho.java

package compra;

Page 145: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

144

import javax.servlet.http.*;import java.util.Vector;import java.util.Enumeration;

public class Carrinho { Vector v = new Vector(); String submit = null; String item = null;

private void addItem(String name) {v.addElement(name); }

private void removeItem(String name) {v.removeElement(name); }

public void setItem(String name) {item = name; }

public void setSubmit(String s) { submit = s; }

public String[] getItems() { String[] s = new String[v.size()]; v.copyInto(s); return s; }

private void reset() { submit = null; item = null; }

public void processRequest(HttpServletRequest request) { if (submit == null) return;

if (submit.equals("adicione")) addItem(item); else if (submit.equals("remova")) removeItem(item); reset(); }}

Exemplo VI.XX – Implementação de um carrinho de compras Virtual.

O exemplo XX.XX implementa apenas o carrinho de compras, deixandode fora o pagamento dos itens, uma vez que esta etapa depende de cada sistema.Geralmente o que é feito é direcionar o usuário para outra página onde eledigitará o número do cartão de crédito que será transmitido por meio de umaconexão segura para o servidor. Existem outras formas de pagamento, como

Page 146: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

145

boleto bancário e dinheiro virtual. O próprio carrinho de compras geralmente émais complexo, uma vez que os para compra devem ser obtidos dinamicamentede um banco de dados. A figura XX.XX mostra a tela resultante de algumasinterações com o carrinho de compras.

Figura VI.XX- Carrinho de compras virtual.

Page 147: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

146

Reencaminhando ou Redirecionando requisições

Existem algumas situações onde pode ser desejável transferir umarequisição para outra URL. Isto é feito com frequência em sistemas quecombinam o uso de Servlets juntamente com JSP. No entanto, a transferênciapode ser para qualquer recurso. Assim, podemos transferir uma requisição de umServlet para uma página JSP, HTML ou um Servlet. Da mesma forma umapágina JSP pode transferir uma requisição para uma página JSP, HTML ou umServlet.

Existem dois tipos de transferência de requisição: o redirecionamento e oreencaminhamento. O redirecionamento é obtido usando o métodosendRedirect() de uma instância HttpServletResponse, passandocomo argumento a URL de destino. O exemplo XX.XX mostra o código de umServlet redirecionando para uma página HTML.

import javax.servlet.*;import javax.servlet.http.*;import java.io.*;

public class Redireciona extends HttpServlet{ public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.sendRedirect("/test/index.html"); }}

Exemplo VI.XX – Redirecionamento de requisição.

Note pelo exemplo que é preciso passar o contexto do recurso(/teste). No caso de redirecionamento o a requisição corrente é perdida e umanova requisição é feita para a URL de destino. Por isso não se deve associarnenhum objeto à requisição, uma vez que o objeto HttpServletRequestcorrente será perdido. O que ocorre na prática é que o servidor envia umamensagem HTTP 302 de volta para o cliente informando que o recurso foitransferido para outra URL e o cliente envia uma nova requisição para a URLinformada.

Já no caso de reencaminhamento a requisição é encaminhada diretamentepara a nova URL mantendo todos os objetos associados e evitando uma nova ida

Page 148: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

147

ao cliente. Portanto, o uso de reencaminhamento é mais eficiente do que o usode redirecionamento. O reencaminhamento é obtido usando o métodoforward() de uma instância RequestDispatcher, passando comoargumento os objetos HttpServletRequest e HttpServletResponsepara a URL de destino. Uma instância RequestDispatcher é obtida pormeio do método getRequestDispatcher()de uma instânciaServletContext , que é obtido, por sua vez, por meio do métodogetServletContext() do Servlet. O exemplo XX.XX mostra o código deum Servlet reencaminhando a requisição para uma página JSP.

import javax.servlet.*;import javax.servlet.http.*;

public class Reencaminha extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) { try { getServletContext().getRequestDispatcher("/index.html"). forward(request,response); }catch (Exception e) { System.out.println("Servlet falhou: "); e.printStackTrace(); } }}

Exemplo VI.XX – Reencaminhamento de requisição.

Note que não é necessário passar o contexto na URL, como é feito noredirecionamento, uma vez que a requisição é encaminhada no contextocorrente.

Page 149: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

148

Uma Arquitetura para comércio eletrônico

O projeto de uma solução para comércio eletrônico é uma tarefacomplexa e deve atender diversos requisitos. Nesta seção mostraremos umamodelo de arquitetura básico para comércio eletrônico que pode ser adaptadopara soluções mais específicas. Este modelo implementa o padrão de projetoMVC, procurando, desta forma, isolar esses aspectos de um sistema decomputação.

Tipos de aplicações na WEB

Podemos enquadra as aplicações na Web em um dos seguintes tipos:

• Business-to-consumer (B2C) – entre empresa e consumidor. Exemplo: umapessoa 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 de automóveisdetecta que um item de estoque precisa ser resposta e faz o pedidodiretamente ao sistema de produção do fornecedor de autopeças. Neste tipode aplicação a linguagem XML possui um papel muito importante, uma vezque existe a necessidade de uma padronização dos tags para comunicação deconteúdo.

• User-to-data – acesso à bases de informação. Exemplo: uma usuárioconsulta uma base de informação.

• User-to-user – chat, e troca de informações entre usuários (Morpheus).

O exemplo que mostraremos é tipicamente um caso de User-to-data,(agenda eletrônica na Web) mas possui a mesma estrutura de um B2C.

Arquitetura MVC para a Web

A figura XX.XX contém um diagrama de blocos que mostra aparticipação de Servlets, JSP e JavaBeans na arquitetura proposta. A idéia éisolar cada aspecto do modelo MVC com a tecnologia mais adequada. A página

Page 150: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

149

JSP é ótima para fazer o papel da visão, uma vez que possui facilidades para ainserção de componentes visuais e para a apresentação de informação. Noentanto, é um pouco estranho usar uma página JSP para receber e tratar umarequisição. Esta tarefa, que se enquadra no aspecto de controle do modelo MVCé mais adequada a um Servlet, uma vez que neste momento componentes deapresentação são indesejáveis. Finalmente, é desejável que a modelagem donegócio fique isolada dos aspectos de interação. A proposta é que a modelagemdo negócio fique contida em classes de JavaBeans. Em aplicações maissofisticadas a modelagem do negócio deve ser implementada por classes deEnterprise JavaBeans (EJB), no entanto esta forma de implementação foge aoescopos deste livro. Cada componente participa da seguinte forma:

• Servlets – Atuam como controladores, recebendo as requisições dosusuários. Após a realização das análises necessária sobre a requisição,instancia o JavaBean e o armazena no escopo adequado (ou não caso o beanjá tenha sido criado no escopo) e encaminha a requisição para a página JSP.

• JavaBeans – Atuam como o modelo da solução, independente da requisiçãoe da forma de apresentação. Comunicam-se com a camada intermediária queencapsula a lógica do problema.

• JSP – Atuam na camada de apresentação utilizando os JavaBeans paraobtenção dos dados a serem exibidos, isolando-se assim de como os dadossão obtidos. O objetivo é minimizar a quantidade de código colocado napágina.

• Camada Intermediária (Middleware) – Incorporam a lógica de acesso aosdados. Permitem isolar os outros módulos de problemas como estratégias deacesso aos dados e desempenho. O uso de EJB (Enterprise JavaBeans) érecomendado para a implementação do Middleware, uma vez que os EJBspossuem capacidades para gerência de transações e persistência. Isto implicana adoção de um servidor de aplicação habilitado para EJB.

A figura XX.XX mostra a interação entre os componentes.

Page 151: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

150

Figura XV.XX. Arquitetura de uma aplicação para Comércio Eletrônico.

Essa arquitetura possui as seguintes vantagens:

1. Facilidade de manutenção: a distribuição lógica das funções entre osmódulos do sistema isola o impacto das modificações.

2. Escalabilidade: Modificações necessária para acompanhar o aumento dademanda de serviços (database pooling, clustering, etc) ficam concentradasna camada intermediária.

A figura abaixo mostra a arquitetura física de uma aplicação de comércioeletrônico.

Navegador Web

JSP(Apresentação)

RequisiçãoServlet

(controlador)

Resposta

JavaBean(modelo)

Cria uma instância

Servidor de Aplicação1

54

3

2

SGBD

JDBC

MiddleWare

Page 152: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

151

Figura XV.XX. Arquitetura física de uma aplicação para Comércio Eletrônico.

Demilitarized Zone (DMZ) é onde os servidores HTTP são instalados. ADMZ é protegida da rede púbica por um firewall, também chamado de firewallde protocolo. O firewall de protocolo deve ser configurado para permitir tráfegoapenas através da porta 80. Um segundo firewall, também chamado de firewallde domínio separa a DMZ da rede interna. O firewall de domínio deve serconfigurado para permitir comunicação apenas por meio das portas do servidorde aplicação

Agenda Web: Um Exemplo de uma aplicação Web usando aarquitetura MVC

O exemplo a seguir mostra o desenvolvimento da agenda eletrônica parao funcionamento na Web. A arquitetura adotada é uma implementação domodelo MVC. Apenas, para simplificar a solução, a camada intermediária foisimplificada e é implementada por um JavaBean que tem a função de gerenciar aconexão com o banco de dados. O banco de dados será composto por duastabelas, uma para armazenar os usuários autorizados a usar a tabela e outra paraarmazenar os itens da agenda. A figura XX.XX mostra o esquema conceitual dobanco de dados e a figura XX.XX mostra o comando para a criação das tabelas.Note que existe um relacionamento entre a tabela USUARIO e a tabela PESSOA,mostrando que os dados pessoais sobre o usuário ficam armazenados na agenda.

USUARIO PESSOA

Page 153: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

152

Figura XV.XX. 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 PRIMARY KEY, NOME VARCHAR(50) NOT NULL, TELEFONE VARCHAR(50), ENDERECO VARCHAR(80), EMAIL VARCHAR(50), HP VARCHAR(50), CELULAR VARCHAR(20), DESCRICAO VARCHAR(80));

CREATE TABLE USUARIO (ID INT PRIMARY KEY, LOGIN VARCHAR(20) NOT NULL, SENHA VARCHAR(20) NOT NULL, CONSTRAINT FK_USU FOREIGN KEY (ID) REFERENCES PESSOA(ID));

Figura XV.XX. 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 para cadastro deusuários será preciso cadastrá-los por meio comandos SQL. Os comandos dafigura XX.XX mostram como cadastrar um usuário.

INSERT INTO PESSOA(ID,NOME,TELEFONE,ENDERECO,EMAIL)VALUES(0,'Alcione de Paiva Oliveira','3899-1769', 'PH Rolfs','[email protected]');

INSERT INTO USUARIO(ID,LOGIN,SENHA) VALUES(0,'Alcione','senha');

Figura XV.XX. Script para cadastra um usuário.

O sistema e-agenda é composta pelos seguintes arquivos:

1:1 1:1

Page 154: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

153

Arquivo Descriçãoagenda.html Página inicial do site, contendo o formulário para a

entrada do login e senha para entrar no restante dosite.

principal.jsp Página JSP contendo o formulário para entrada dedados para inserção, remoção ou consulta de itens daagenda.

LoginBean.java JavaBean responsável por verificar se o usuário estáautorizado a acessar a agenda.

AgendaServlet.java Servlet responsável pelo tratamento de requisiçõessobre alguma função da agenda (consulta, inserção eremoçã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 controledas conexões.

Tabela XV.XX. Arquivos do sistema e-agenda.

O diagrama de colaboração abaixo mostra as interação entre oscomponentes do sistema.

Figura XV.XX. Interação entre os componentes do sistema.

1 e 4 – Requisições2 e 6 – instanciações4 – reencaminhamento de

requisições3,5,7 e 8 – Chamadas de métodos

agenda.html AgendaServlet

ConnectionBeanLoginBean

AcaoBean

principal.jsp

1

24

5

78

36

Page 155: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

154

Descreveremos agora cada componente da aplicação. O exemplo XX.XXmostra código HTML da página agenda.html. Esta é a página inicial daaplicação. Ela contém o formulário para a entrada do login e senha para entrarno restante do site.

123456789101112131415161718192021222324252627282920313233343536373839

<HTML><HEAD> <TITLE>Agenda</TITLE></HEAD>

<BODY BGCOLOR="#FFFFFF"><P align="center"><IMG src="tit.gif" width="350" height="100" border="0"></P><BR>

<CENTER> <FORM method="POST" name="TesteSub" onsubmit="return TestaVal()"action="/agenda/agenda"><BR> Login:<INPUT size="20" type="text" name="login"><BR><BR> Senha:<INPUT size="20" type="password" name="senha"><BR><BR><BR> <INPUT type="submit" name="envia" value="Enviar"> <INPUT size="3" type="Hidden" name="corrente" value="0"><BR> </FORM></CENTER><SCRIPT language="JavaScript"><!--function TestaVal(){ if (document.TesteSub.login.value == "") { alert ("Campo Login nao Preenchido...Form nao Submetido") return false } else if (document.TesteSub.senha.value == "") { alert ("Campo Senha nao Preenchido...Form nao Submetido") return false } else { return true }}//--></SCRIPT></BODY></HTML>

Page 156: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

155

Exemplo VI.XX – agenda.html.

O formulário está definido nas linha 11 a 17. Na linha 12 o parâmetroaction indica a URL que dever receber a requisição. A URL é virtual e suaassociação com o Servlet AgendaServlet será definida no arquivoweb.xml. Na linha 16 é definido um campo oculto (Hidden) como o nome decorrente e valor 0. Ele será usado pelo AgendaServlet reconhecer apágina de onde saiu a requisição. As linha 19 a 31 definem uma função emJavaScript que será usada para verificar se o usuário digitou o nome e a senhaantes de enviar a requisição ao usuário. O uso de JavaScript no lado cliente paracriticar a entrada do usuário é muito comum pois diminui a sobrecarga doservidor.

O exemplo XX.XX mostra código da página principal.jsp. Estapágina contém o formulário para entrada de dados para inserção, remoção ouconsulta de itens da agenda. Na linha 4 a diretiva page define que o servidordeve acompanhar a sessão do usuário e importa o pacote agenda. Na linha 7um objeto da classe agenda.LoginBean é recuperado da sessão por meio dométodo getAttribute(). Para recuperar o objeto é preciso passar para ométodo o nome que está associado ao objeto na sessão. De forma semelhante, nalinha 8 um objeto da classe agenda.AcaoBean é recuperado da requisiçãopor meio do método getAttribute(). Este objeto é recuperado darequisição porque cada requisição possui uma ação diferente associada. Na linha9 é verificado se objeto agenda.LoginBean foi recuperado e se o retorno dométodo getStatus() é true. Se o objeto agenda.LoginBean não foirecuperado significa que existe uma tentativa de acesso direto à páginaprincipal.jsp sem passar primeiro pela página agenda.html ou que asessão se esgotou. Se o método getStatus() retornar false significa que ousuário não está autorizado a acessar essa página. Nestes casos é processado ocódigo associado ao comando else da linha 51 que apaga a sessão por meio dométodo invalidate() do objeto HttpSession (linha 53) e mostra amensagem “Usuário não autorizado” (linha 55). Caso o objeto indique que ousuário está autorizado os comandos internos ao if são executados. Na linha 11é mostrada uma mensagem com o nome do usuário obtido por meio do métodogetNome() do objeto agenda.LoginBean . Na linha 13 é mostrado oresultado da ação anterior por meio do método toString() do objetoagenda.AcaoBean. A ação pode ter sido de consulta, inserção de um novoitem na agenda e remoção de um item na agenda. No primeiro caso é mostradouma lista dos itens que satisfizeram a consulta. No segundo e terceiro casos éexibida uma mensagem indicado se a operação foi bem sucedida.

Page 157: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

156

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253

<HTML><HEAD><TITLE>Tela da Agenda </TITLE></HEAD><BODY bgcolor="#FFFFFF"><%@ page session="true" import="agenda.*" %>

<% agenda.LoginBean lb = (agenda.LoginBean) session.getAttribute("loginbean"); agenda.AcaoBean ab = (agenda.AcaoBean) request.getAttribute("acaobean"); if (lb != null && lb.getStatus()) { %> <H2>Sess&atilde;o do <%= lb.getNome() %></H2> <% if (ab!=null) out.println(ab.toString()); %>

<P><BR></P> <FORM method="POST" name="formprin" onsubmit="return TestaVal()"action="/agenda/agenda"> Nome: <INPUT size="50" type="text" name="nome"><BR> Telefone: <INPUT size="20" type="text" name="telefone"><BR> Endere&ccedil;o: <INPUT size="50" type="text" name="endereco"><BR> Email: <INPUT size="50" type="text" name="email"><BR><BR> P&aacute;gina: <INPUT size="50" type="text" name="pagina"><BR> Celular: <INPUT size="20" type="text" name="celular"><BR> Descri&ccedil;&atilde;o: <INPUT size="20" type="text" name="descricao"> <BR><CENTER> <INPUT type="submit" name="acao" value="Consulta"> <INPUT type="submit" name="acao" value="Insere"> <INPUT type="submit" name="acao" value="Apaga"></CENTER> <INPUT size="3" type="Hidden" name="corrente" value="1"></FORM>

<SCRIPT language="JavaScript"><!--function TestaVal(){ if (document.formprin.nome.value == "" && document.formprin.descricao.value== "") { alert ("Campo Nome ou Descricao devem ser Preenchidos!") return false } else { return true }}//--></SCRIPT>

<%}else{ session.invalidate();

Page 158: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

157545556575859

%><H1>Usu&aacute;rio n&atilde;o autorizado</H1><%}%></BODY></HTML>

Exemplo VI.XX – principal.jsp.

As linhas 17 a 31 definem o código do formulário de entrada. Nas linhas17 e 18 são definidos os atributos do formulário. O atributo method indica arequisição será enviada por meio do método POST. O atributo name define onome do formulário como sendo formprin. O atributo onsubmit define quea função javaSript TestaVal() deve ser executada quando o formulário forsubmetido. Finalmente, o atributo action define a URL para onde a requisiçãodeve ser enviada. Neste caso a URL é agenda/agenda que está mapeada parao Servlet AgendaServlet. O mapeamento é feito no arquivo web.xml dodiretório web-inf do contexto agenda, como mostrado na figura XX.XX. Aslinhas 19 a 25 definem os campos de texto para entrada dos valores. As linhas 27a 29 definem os botões de submit. Todos possuem o mesmo nome, de forma queo Servlet precisa apenas examinar o valor do parâmetro acao para determinarqual ação foi solicitada Na linha 30 é definido um campo oculto (Hidden)como o nome de corrente e valor 0. Ele será usado pelo AgendaServletreconhecer a página de onde saiu a requisição. As linha 33 a 47 definem umafunção em JavaScript que será usada para verificar se o usuário entrou comvalores nos campos de texto nome ou decricao. No mínimo um dessescampos deve ser preenchido para que uma consulta possa ser realizada.

O exemplo XX.XX mostra código do JavaBean usado para intermediar aconexão com o banco de dados. O JavaBean ConnectionBean tem aresponsabilidade de abrir uma conexão com o banco de dados, retornar umareferência desta conexão quando solicitado e registrar se a conexão esta livre ouocupada. Neste exemplo o se encarrega apenas de obter a conexão e fechá-la, noentanto, em aplicação com maior número de acessos ao banco de dados pode sernecessário um maior controle sobre o uso das conexões, mantendo-as em umaestrutura de dados denominada de pool de conexões. Na linha 12 podemosobservar que o construtor da classe foi declarado com o modificador de acessoprivate. Isto significa que não é possível invocar o construtor por meio de umobjeto de outra classe. Isto é feito para que se possa ter um controle sobre acriação de instâncias da classe. No nosso caso permitiremos apenas que umainstância da classe seja criada, de modo que todas as referências apontem para

Page 159: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

158

esse objeto. Esta técnica de programação, onde se permite uma única instânciade uma classe é denominada de padrão de projeto Singleton. O objetivo deutilizarmos este padrão é porque desejamos que apenas um objeto controle aconexão com o banco de dados. Ma se o construtor não pode ser chamadointernamente como uma instância da classe é criada e sua referência é passadapara outros objetos? Esta é a tarefa do método estático getInstance()(linhas 14 a 19). Este método verifica se já existe a instância e retorna areferência. Caso a instância não exista ela é criada antes de se retornar areferência. O método init() (linhas 21 a 27) é chamado pelo construtor paraestabelecer a conexão com o SGBD. Ele carrega o driver JDBC do tipo 4 paraHSQLDB e obtém uma conexão com o SGBD. O métododevolveConnection() (linhas 29 a 34) é chamado quando se desejadevolver a conexão. Finalmente, o método getConnection() (linhas 36 a46) é chamado quando se deseja obter a conexão.

1234567891011121314151617181920212223242526272829

package agenda;

import java.sql.*;import java.lang.*;import java.util.*;

public class ConnectionBean { private Connection con=null; private static int clients=0; static private ConnectionBean instance=null;

private ConnectionBean() { init(); }

static synchronized public ConnectionBean getInstance() { if (instance == null) { instance = new ConnectionBean(); } return instance; }

private void init() { try { Class.forName("org.hsqldb.jdbcDriver"); con= DriverManager.getConnection("jdbc:hsqldb:hsql://localhost","sa",""); } catch(Exception e){System.out.println(e.getMessage());}; }

public synchronized void devolveConnection(Connection con) {

Page 160: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

159

303132333435363738394041424344454647

if (this.con==con) { clients--; notify(); } }

public synchronized Connection getConnection() { if(clients>0) { try { wait(5000); } catch (InterruptedException e) {}; if(clients>0) return null; } clients ++; return con; }}

Exemplo VI.XX – ConnectionBean.java.

O exemplo XX.XX mostra código do JavaBean usado para verificar se ousuário está autorizado a usar a agenda. O JavaBean LoginBean recebe onome e a senha do usuário, obtém a conexão com o SGBD e verifica se ousuário está autorizado, registrando o resultado da consulta na variável status(linha 10). Tudo isso é feito no construtor da classe (linhas 12 a 35). Note que naconstrução do comando SQL (linhas 17 a 20) é inserido uma junção entre astabelas PESSOA e USUARIO de modo a ser possível recuperar os dadosrelacionados armazenados em ambas as tabelas. Os métodos getLogin(),getNome() e getStatus() (linhas 36 a 38) são responsáveis pelo retornodo login, nome e status da consulta respectivamente.

1234567891011

package agenda;

import java.sql.*;import java.lang.*;import java.util.*;

public class LoginBean { protected String nome = null; protected String login= null; protected boolean status= false;

Page 161: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

160

12131415161718192021222324252627282930313233343536373839

public LoginBean(String login, String senha) { this.login = login; Connection con=null; Statement stmt =null; String consulta = "SELECT NOME FROM PESSOA, USUARIO "+ "WHERE USUARIO.ID = PESSOA.ID AND "+ "USUARIO.SENHA ='"+senha+"' AND "+ "USUARIO.LOGIN ='"+login+"'"; try { con=ConnectionBean.getInstance().getConnection(); stmt = con.createStatement(); ResultSet rs =stmt.executeQuery(consulta); if(rs.next()) { status = true; nome = rs.getString("NOME"); } } catch(Exception e){System.out.println(e.getMessage());} finally { ConnectionBean.getInstance().devolveConnection(con); try{stmt.close();}catch(Exception ee){}; }

} public String getLogin(){return login;} public String getNome(){return nome;} public boolean getStatus(){return status;}}

Exemplo VI.XX – LoginBean.java.

O exemplo XX.XX mostra código do Servlet que implementa a camadade controle do modelo MVC. O Servlet AgendaServlet recebe asrequisições e, de acordo com os parâmetros, instância os JavaBeans apropriadose reencaminha as requisições para as páginas corretas. Tanto o métododoGet() (linhas 9 a 12) quanto o método doPost()(linhas 13 a 17) invocamo método performTask()(linhas 19 a 61) que realiza o tratamento darequisição. Na linhas 24 a 26 do método performTask() é obtido o valor doparâmetro corrente que determina a página que originou a requisição. Se ovalor for nulo é assumido o valor default zero. Na linha 30 é executado umcomando switch sobre esse valor, de modo a desviar para bloco de comandosadequado. O bloco que vai da linha 32 até a linha 43 trata a requisição originadaem uma página com a identificação 0 (página agenda.html). Nas linhas 32 e33 são recuperados o valor de login e senha digitados pelo usuário. Se algum

Page 162: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

161

desses valores for nulo então a requisição deve ser reencaminhada para a páginade login (agenda.html) novamente (linha 35). Caso contrário é instanciadoum objeto LoginBean, inserido na sessão corrente e definida a páginaprincipal.jsp como a página para o reencaminhamento da requisição(linhas 38 a 41). Já o bloco que vai da linha 44 até a linha 54 trata a requisiçãooriginada em uma página com a identificação 1 (página principal.jsp). Nalinha 44 é recuperado o objeto HttpSession corrente. O argumento false éutilizado para impedir a criação de um novo objeto HttpSession caso nãoexista um corrente. Se o valor do objeto for null, então a requisição deve serreencaminhada para a página de login (linha 47). Caso contrário é instanciadoum objeto AcaoBean, inserido na requisição corrente e definida a páginaprincipal.jsp como a página para o reencaminhamento da requisição(linhas 50 a 52). Na linha 56 a requisição é reencaminhada para a páginadefinida (página agenda.html ou principal.jsp).

12345678910111213141516171819202122232425262728

package agenda;

import javax.servlet.*;import javax.servlet.http.*;import agenda.*;

public class AgendaServlet extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response) { performTask(request,response); } public void doPost(HttpServletRequest request, HttpServletResponse response) { performTask(request,response); }

public void performTask(HttpServletRequest request, HttpServletResponse response) { String url; HttpSession sessao; String corrente = request.getParameter("corrente"); int icorr=0; if (corrente != null) icorr = Integer.parseInt(corrente);

try

Page 163: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

162

29303132333435363738394041424344454647484950515253545556575859606162

{ switch(icorr) { case 0: String login = request.getParameter("login"); String senha = request.getParameter("senha"); if (login == null||senha == null) url= "/agenda.html"; else { sessao = request.getSession(true); sessao.setAttribute("loginbean", new agenda.LoginBean(login,senha)); url= "/principal.jsp"; }; break; case 1: sessao = request.getSession(false); if (sessao == null) url= "/agenda.html"; else { request.setAttribute("acaobean", new agenda.AcaoBean(request)); url= "/principal.jsp"; }; break; } getServletContext().getRequestDispatcher(url).forward(request,response); }catch (Exception e) { System.out.println("AgendaServlet falhou: "); e.printStackTrace(); } }}

Exemplo VI.XX – AgendaServlet.java.

O exemplo XX.XX mostra código do JavaBean usado para realizar amanutenção da agenda. O JavaBean AcaoBean é responsável pela consulta,remoção e inserção de novos itens na agenda. Um objeto StringBufferreferenciado pela variável retorno é utilizado pelo JavaBean para montar oresultado da execução. O construtor (linhas 16 a 27) verifica o tipo de requisiçãoe invoca o método apropriado.

O método consulta() (linhas 29 a 77) é responsável pela realizaçãode consultas. As consultas podem ser realizadas sobre o campo nome ou

Page 164: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

163

descrição e os casamentos podem ser parciais, uma vez que é usado o operadorLIKE. A consulta SQL é montada nas linhas 40 a 47. Na linha 50 é obtida umaconexão com SGBD por meio do objeto ConnectionBean. Na linha 57 ocomando SQL é executado e as linhas 59 a 72 montam o resultado da consulta.

O método insere() (linhas 79 a 148) é responsável por inserir umitem na agenda. Na linha 95 é obtida uma conexão com SGBD por meio doobjeto ConnectionBean. Para inserir um novo item é preciso obter o númerodo último identificador usado, incrementar o identificador e inserir na base oitem com o identificador incrementado. Esta operação requer que não sejaacrescentado nenhum identificador entre a operação de leitura do últimoidentificador e a inserção de um novo item. Ou seja, é necessário que essasoperações sejam tratadas como uma única transação e o isolamento entre astransações sejam do nível Repeatable Read. A definição do inicio da transação éfeita no comando da linha 102. A mudança do nível de isolamento é feita peloscomandos codificados nas linha 103 a 109. Na linha 112 é invocado o métodoobtemUltimo() (linhas 150 a 171) para obter o último identificadorutilizado. As linhas 114 a 128 montam o comando SQL para a execução. Ocomando SQL é executado na linha 131. O fim da transação é definido pelocomando da linha 132. Ao fim da transação, de forma a não prejudicar aconcorrência, o nível de isolamento deve retornar para um valor mais baixo. Istoé feito pelos comandos das linhas 133 a 137.

O método apaga() (linhas 173 a 201) é responsável por remover umitem da agenda. As linhas 175 a 180 contém o código para verificar se o usuáriodigitou o nome associado ao item que deve ser removido. A linha 181 montam ocomando SQL para a execução. Na linha 184 é obtida uma conexão com SGBDpor meio do objeto ConnectionBean. O comando SQL é executado na linha191.

12345678910111213

package agenda;

import java.lang.*;import java.util.*;import java.sql.*;

public class AcaoBean{ private Connection con=null; private StringBuffer retorno = null; private Statement stmt=null; private String [] legenda= {"C&oacute;digo","Nome","Telefone", "Endere&ccedil;o", "email","hp",

Page 165: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

164

14151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162

"celular","Descri&ccedil;&atilde;o"};

public AcaoBean(javax.servlet.http.HttpServletRequest request) { String acao = request.getParameter("acao"); if (acao.equals("Consulta")) { String nome = request.getParameter("nome"); String descri = request.getParameter("descricao"); consulta(nome,descri); } else if (acao.equals("Insere")) insere(request); else if (acao.equals("Apaga")) apaga(request); }

private void consulta(String nome,String descri) { String consulta = null;

if ((nome == null||nome.length()<1) && (descri == null|| descri.length()<1)) { retorno = new StringBuffer("Digite o nome ou descricao!"); return; }

if (descri == null|| descri.length()<1) consulta = "SELECT * FROM PESSOA WHERE NOME LIKE '%"+ nome+"%'"+" ORDER BY NOME"; else if (nome == null|| nome.length()<1) consulta = "SELECT * FROM PESSOA WHERE DESCRICAO LIKE '%"+ descri+"%'"+" ORDER BY NOME"; else consulta="SELECT * FROM PESSOA WHERE DESCRICAO LIKE '%"+ descri+"%' AND NOME LIKE '%"+nome+"%' ORDER BY NOME"; try { con=ConnectionBean.getInstance().getConnection(); if (con == null) { retorno = new StringBuffer("Servidor ocupado. Tente mais tarde.!"); return; } stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(consulta);

retorno = new StringBuffer(); retorno.append("<br><h3>Resultado</h3><br>"); while(rs.next()) {

Page 166: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

165

63646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111

retorno.append("ID:").append(rs.getString("id")); retorno.append("<br>Nome:").append(rs.getString("Nome")); retorno.append("<br>Telefone:").append(rs.getString("Telefone")); retorno.append("<br>Endereco:").append(rs.getString("Endereco")); retorno.append("<br>email:").append(rs.getString("email")); retorno.append("<br>hp:").append(rs.getString("hp")); retorno.append("<br>celular:").append(rs.getString("celular")); retorno.append("<br>descricao:").append(rs.getString("descricao")); retorno.append("<br><br>"); } } catch(Exception e){System.out.println(e.getMessage());} finally {ConnectionBean.getInstance().devolveConnection(con); try{stmt.close();}catch(Exception ee){}; } }

private void insere(javax.servlet.http.HttpServletRequest request) { String[] par = {"telefone","endereco","email","hp","celular","descricao"};

StringBuffer comando = new StringBuffer("INSERT INTO PESSOA("); StringBuffer values = new StringBuffer(" VALUES(");

String aux = request.getParameter("nome"); if (aux == null || aux.length()<1) { retorno = new StringBuffer("<br><h3>Digite o nome!</h3><br>"); return; }

try { con=ConnectionBean.getInstance().getConnection(); if (con == null) { retorno = new StringBuffer("Servidor ocupado. Tente mais tarde!"); return; }

con.setAutoCommit(false); DatabaseMetaData meta=con.getMetaData();

if(meta.supportsTransactionIsolationLevel( con.TRANSACTION_REPEATABLE_READ)) { con.setTransactionIsolation( con.TRANSACTION_REPEATABLE_READ); }

Page 167: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

166

112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160

int ultimo = obtemUltimo(con); if (ultimo==-1) return; ultimo++; comando.append("id,nome"); values.append(ultimo+",'").append(aux).append("'");

for(int i=0;i<par.length;i++) { aux = request.getParameter(par[i]); if (aux != null && aux.length()>0) { comando.append(",").append(par[i]); values.append(",'").append(aux).append("'"); } } comando.append(")"); values.append(")"); aux = comando.toString()+values.toString(); stmt = con.createStatement(); stmt.executeUpdate(aux); con.setAutoCommit(true); if(meta.supportsTransactionIsolationLevel( con.TRANSACTION_READ_COMMITTED)) { con.setTransactionIsolation( con.TRANSACTION_READ_COMMITTED); } retorno = new StringBuffer("<br><h3>Inserido!</h3><br>"); return; } catch(Exception e) {retorno = new StringBuffer("<br><h3>Erro:"+e.getMessage()+"!</h3><br>"); } finally { ConnectionBean.getInstance().devolveConnection(con); try{stmt.close();}catch(Exception ee){}; } }

private int obtemUltimo(Connection con) { String consulta = "SELECT MAX(ID) AS maior FROM PESSOA"; try { if (con == null) { retorno = new StringBuffer("Servidor ocupado. Tente mais tarde.!"); return -1; }

Page 168: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

167

161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205

stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(consulta); if(rs.next()) return Integer.parseInt(rs.getString("maior")); else return 0; } catch(Exception e) { retorno = new StringBuffer("<br><h3>Erro:"+e.getMessage()+"!</h3><br>"); return -1; } finally {try{stmt.close();}catch(Exception ee){};} }

private void apaga(javax.servlet.http.HttpServletRequest request) { String aux = request.getParameter("nome"); if (aux == null || aux.length()<1) { retorno = new StringBuffer("<br><h3>Digite o nome!</h3><br>"); return; } String consulta = "DELETE FROM PESSOA WHERE NOME ='"+aux+"'"; try { con=ConnectionBean.getInstance().getConnection(); if (con == null) { retorno = new StringBuffer("Servidor ocupado. Tente mais tarde.!"); return; } stmt = con.createStatement(); stmt.executeUpdate(consulta);

retorno = new StringBuffer("<br><h3>Removido!</h3><br>"); return; } catch(Exception e){ retorno = new StringBuffer("<br><h3>Erro:"+e.getMessage()+"!</h3><br>"); } finally { ConnectionBean.getInstance().devolveConnection(con); try{stmt.close();}catch(Exception ee){};} }

public String[] getLeg(){return legenda;} public String toString(){return retorno.toString();}}

Exemplo VI.XX – AcaoBean.java.

Page 169: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

168

Instalação do SGBD

O HSQLDB (www.hsqldb.org) é um SGBD de código abertodesenvolvido em Java. Ele obedece os padrões SQL e JDBC. Possui as seguintescaracterísticas:

• Tamanho pequeno (≅ 100KB).• Funciona como servidor, standalone e in-memory.• Suporta transação.• Integridade referencial.• Procedimentos Armazenados em Java.• Direitos de acessos

Para instalá-lo em no MS-Windows execute as seguintes etapas:

1) Descompacte o arquivo hsqldb_v.1.61.zip em um diretório qualquer. Porexemplo : c:\sgbd

2) Coloque o seguinte comando em seu autoexec.bat

SET CLASSPATH=%CLASSPATH%;c:\sgbd\hsqldb_v.1.61\lib\hsqldb.jar

Execução em modo servidor

c:\sgbd\hsqldb_v.1.61\demo\runServer.bat

Execução do gerenciador gráfico

c:\sgbd\hsqldb_v.1.61\demo\runManager.bat

Instalação da Aplicação

Para instalar crie a seguinte estrutura de diretório abaixo do diretóriowebapps do Tomcat:

Servlets eJavaBeans

páginasHTML e JSP

arquivoweb.xml

Page 170: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

169

webapps |_____ agenda |_____ Web-inf |_____classes |_______agenda

Figura XV.XX. Estrutura de diretórios para a aplicação agenda.

O arquivo web.xml deve ser alterado para conter mapeamento entre aURL agenda e o Servlet AgendaServlet.

...<web-app> <servlet> <servlet-name> agenda </servlet-name> <servlet-class> agenda.AgendaServlet </servlet-class> </servlet>

<servlet-mapping> <servlet-name> agenda </servlet-name> <url-pattern> /agenda </url-pattern> </servlet-mapping>...</web-app>

Figura XV.XX. Arquivo web.xml para a agenda.

Considerações sobre a solução

Page 171: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

170

A aplicação acima implementa uma agenda que pode ser acessada por meioda Internet, no entanto, devido à falta de espaço e à necessidade de destacarmosos pontos principais, alguns detalhes foram deixados de lado, como por exemplouma melhor interface com o usuário. Abaixo seguem alguns comentários sobrealgumas particularidades da aplicação:

1. O JavaBean da classe LoginBean é armazenado na sessão parapermitir a verificação se o acesso ao site é autorizado. Isto impede que osusuários tentem acessar diretamente a página principal.jsp daagenda. Caso tentem fazer isso, a sessão não conterá um objetoLoginBean associado e, portanto, o acesso será recusado.

2. O JavaBean da classe AcaoBean é armazenado no objeto requestuma vez que sua informações são alteradas a cada requisição. Uma formamais eficiente seria manter o objeto AcaoBean na sessão e cada novorequisição invocar um método do AcaoBean para gerar os resultados.No entanto, o objetivo da nossa implementação não é fazer a aplicaçãomais eficiente possível, e sim mostrar para o leitor uma aplicação comvariadas 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 apresentação(Visão) em relação à camada do modelo. Parte do código HTML davisão é inserido pelo AcaoBean no momento da construção da Stringcontendo o resultado da ação. Isto foi feito para minimizar a quantidadede código Java na página JSP. Pode-se argumentar que neste caso apromessa da separação entre as camadas não é cumprida totalmente.Uma solução para o problema seria gerar o conteúdo em XML e utilizarum analisador de XML para gerar a página de apresentação. No entanto,o uso da tecnologia XML foge ao escopo deste livro.

4. A solução apresenta código redundante para criticar as entradas dousuário. Existe código JavaScript nas páginas, e código Java no Servlet eJavaBeans. O uso de código JavaScript nas páginas para críticas deentrada é indispensável para aliviarmos a carga sobre o servidor. Já ocódigo para crítica no servidor não causa impacto perceptível e útil paraevitar tentativas de violação.

Page 172: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

171

Capítulo VII Perguntas FrequentesComo executar um programa a partir de uma aplicação em Java?

Resposta: isso pode ser feito com o método de instância exec da classeRuntime.

Como criar um TextField que não exiba o que está sendo digitado parase usado como campo de entrada de senhas?

Resposta: use o método setEchoChar() do TextField para definir qualcaractere que deve ser ecoado.

Como aumentar a área de ambiente do DOS para caber o CLASSPATH?

Resposta: coloque a seguinte linha no autoexec.bat

set shell=c:\command.com /e:4096

Page 173: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

172

BibliografiaEckel B. Thinking in Java. 2nd Ed. New Jersey : Prentice Hall, 2000.

Gosling J., Joy W., Steele G. The Java Language Specification. Massachusetts :Addison-Wesley, 1996.

Oaks S. Java Security. California : O’Reilly & Associates, Inc, 1998.

Oaks S., Wong H. Java Threads. 2ª Ed. California : O’Reilly & Associates, Inc,1999.

Watt D. A. Programming Language Concepts and Paradigms. Great Britain :Prentice Hall, 1990.

Ethan H., Lycklama E. How do you Plug Java Memory Leaks? Dr. Dobb´sJournal, San Francisco, CA, No. 309, February 2000.

Wahli U. e outros. Servlet and JSP Programming with IBM WebSphere Studioand VisualAge for Java, IBM RedBooks, California, May 2000.

Sadtler C. e outros. Patterns for e-business: User-to-Business Patterns for Topology1 and 2 using WebSphere Advanced Edition, IBM RedBooks, California, April2000.

Bagwel D. e outros. An Approach to Designing e-business Solutions, IBMRedBooks, California, December 1998.

Page 174: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

173

LinksRevistas

http://www.javaworld.com/Revista online sobre Java.

Livroshttp://www.eckelobjects.com/Página do autor do livro Thinking in Java, atualmente em segunda edição. Olivro pode ser baixado gratuitamente no site.

http://www.redbooks.ibm.com/booklist.htmlLivros da IBM

Servidoreshttp://jakarta.apache.orgPágina do projeto Jakarta que desenvolveu o Tomcat.

http://www.metronet.com/~wjm/tomcatLista Tomcat

http://www.jboss.orgServidor de aplicação gratuito habilitado para EJB

Dicas Java e recursoshttp://java.sun.com/Página da Sun com informações, tutoriais e produtos Java.

http://gamelan.earthweb.com/Página da com informações, Applets, Lista de discussão, tutoriais.

http://www.inquiry.com/techtips/java_proAsk the Java Pro

http://www.jguru.com/jGuru.com(Home): Your view of the Java universe

http://www.soujava.org.br

Page 175: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

174

Bem Vindo ao SouJava!

Servlets e JSPhttp://www.servlet.com/srvdev.jhtmlServlet Inc : Developers Forum

http://www.servlets.comServlets.com

http://www.jspin.com/homeJspin.com - The JSP Resource Index

http://www.burridge.net/jsp/jspinfo.htmlWeb Development with JSP: JSP, Java Servlet, and Java BeanInformation

http://www.apl.jhu.edu/~hall/java/Servlet-TutorialA Tutorial on Java Servlets and Java Server Pages (JSP)

Page 176: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

175

ÍndiceA

ASP ........................................................124

C

Calendar ................................................1CGI ..........................................................99contexto da aplicação.............................107Cookies ..................................................115CORBA....................................................69

D

Demilitarized Zone ................................151DMZ...............Consulte Demilitarized Zone

E

EJB.........................................................149Enterprise JavaBeans .............Consulte EJB

J

JSP ...................................................97, 122

M

Mudança de Contexto..............................5

MVC.............................................. 148, 151

N

Níveis de isolamento ............................... 85Nível de Isolamento................................. 83

P

PHP........................................................ 123pool de conexões.................................... 157Prepared Statements ................................ 87Procedimentos Armazenados................... 88

R

RMI ......................................................... 69

S

Servlets .................................................... 97Singleton ................................................ 158sites dinâmicos......................................... 97Stored Procedures.................................... 88

T

Transação................................................. 83

Page 177: Java na Pratica vol2

Java na Prática – Volume II

Alcione de P. Oliveira, Vinícius V. Maciel - UFV

1