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

Tamanho: px
Começar a partir da página:

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

Transcrição

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

2 Sumário 1 Capítulo I - Concorrência... 3 CRIANDO THREADS EM JAVA... 5 Criando threads por meio da interface Runnable... 8 A CLASSE THREAD... 9 Hierarquia... 9 Construtores... 9 Métodos Variáveis públicas...11 CICLO DE VIDA DOS THREADS sleep(), yield(), join(), destroy(), stop(), suspend() e resume() DAEMON THREADS INFLUÊNCIA DO SISTEMA OPERACIONAL NO COMPORTAMENTO DOS THREADS Forma de escalonamento de threads Relacionamento entre os níveis de prioridades definidas na linguagem Java e os níveis de prioridades definidas nos Sistemas Operacionais COMPARTILHAMENTO DE MEMÓRIA E SINCRONIZAÇÃO Atomicidade de Instruções e Sincronização do Acesso à Sessões Críticas Comunicação entre Threads: wait() e notify() Capítulo II - Animação Capítulo III - Programação em rede CONCEITOS SOBRE PROTOCOLOS USADOS NA INTERNET TCP UDP IDENTIFICAÇÃO DE HOSTS (Número IP) Identificação de Processos (Portas) PROGRAMAÇÃO EM REDE COM JAVA Comunicação Básica Entre Aplicações Comunicação Sem Conexão (UDP) Comunicação por meio de URL Capítulo IV Computação Distribuída (RMI) CRIANDO NOSSA AGENDA DISTRIBUÍDA Implementar interface do objeto remoto Capítulo V - Acesso a Banco de Dados MODELOS DE ACESSO A SERVIDORES TIPOS DE DRIVERS JDBC Obtendo os Drivers JDBC PREPARANDO UM BANCO DE DADOS Configurando o ODBC EXEMPLO INICIAL Carregando o Driver Estabelecendo a conexão Criando e Executando Comandos RECUPERANDO VALORES TRANSAÇÕES E NÍVEL DE ISOLAMENTO Transação... 83

3 Níveis de isolamento PREPARED STATEMENTS PROCEDIMENTOS ARMAZENADOS (STORED PROCEDURES) AGENDA ELETRÔNICA VERSÃO JDBC Capítulo VI Servlets e JSP SERVLETS...97 Applets X Servlets CGI X Servlets A API SERVLET Exemplo de Servlet COMPILANDO O SERVLET Instalando o Tomcat PREPARANDO PARA EXECUTAR O SERVLET Compilando o Servlet Criando uma aplicação no Tomcat EXECUTANDO O SERVLET Invocando diretamente pelo Navegador Invocando em uma página HTML Diferenças entre as requisições GET e POST CONCORRÊNCIA OBTENDO INFORMAÇÕES SOBRE A REQUISIÇÃO LIDANDO COM FORMULÁRIOS LIDANDO COM COOKIES LIDANDO COM SESSÕES JSP PHP X JSP ASP X JSP Primeiro exemplo em JSP Executando o arquivo JSP Objetos implícitos Tags JSP Comentários Diretivas Extraindo Valores de Formulários Criando e Modificando Cookies Lidando com sessões O Uso de JavaBeans REENCAMINHANDO OU REDIRECIONANDO REQUISIÇÕES UMA ARQUITETURA PARA COMÉRCIO ELETRÔNICO Tipos de aplicações na WEB Arquitetura MVC para a Web Agenda Web: Um Exemplo de uma aplicação Web usando a arquitetura MVC Capítulo VII Perguntas Frequentes Bibliografia Links Índice

4 Capítulo I - Concorrência 3 Um sistema operacional é dito concorrente se permite que mais que uma tarefa seja executada ao mesmo tempo. Na prática a concorrência real ou paralelismo só é possível se o hardware subjacente possui mais de um processador. No entanto, mesmo em computadores com apenas um processador é possível obter um certo tipo de concorrência fazendo com o processador central execute um pouco de cada tarefa por vez, dando a impressão de que as tarefas estão sendo executadas simultaneamente. Dentro da nomenclatura empregada, uma instância de um programa em execução é chamada de processo. Um processo ocupa um espaço em memória principal para o código e para as variáveis transientes (variáveis que são eliminadas ao término do processo). Cada processo possui pelo menos uma linha de execução (Thread). Para ilustrarmos o que é uma linha de execução suponha um determinado programa prog1. Ao ser posto em execução é criado um processo, digamos A, com uma área de código e uma área de dados e é iniciada a execução do processo a partir do ponto de entrada. A instrução inicial assim como 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á em execução de acordo com que foi determinado pelo programa. O estado corrente da linha de execução é representada pela instrução que está sendo executada. A figura IX.1 mostra a relação entre estes elementos. arquivo prog Área de código Área de dados Memória Principal Linha de execução (thread) Processo 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

5 diferença que enquanto cada processo possui sua área de código e dados separada de outros processos, os threads em um mesmo processo compartilham o código e a área de dados. O que distingue um thread de outro em um mesmo processo é a instrução corrente e uma área de pilha usada para armazenar o contexto da sequência de chamadas de cada thread. Por isso os threads também são chamados de processos leves (light process). A figura IX.2 mostra esquematicamente a diferença entre processos e threads. 4 A C Memória B Código Dados Processo Thread 1 Thread 2 Área de pilha do thread2 Área de pilha do thread1 Figura IX.2 (a) Processos; (b) Threads. Sistemas monotarefas e monothreads como o DOS possuem apenas um processo em execução em um determinado instante e apenas um thread no processo. Sistemas multitarefas e monothreads como o Windows 3.1 permitem vários processos em execução e apenas um thread por processo. Sistemas multitarefas e multithread como o Solaris, OS/2, Linux e Windows 95/98/NT permitem vários processos em execução e vários threads por processo. Como os threads em um mesmo processo possuem uma área de dados em comum, surge a necessidade de controlar o acesso a essa área de dados, de modo que thread não leia ou altere dados no momento que estão sendo alterados por outro thread. A inclusão de instruções para controlar o acesso a áreas compartilhadas torna o código mais complexo do que o código de processos monothreads. Uma pergunta pode surgir na mente do leitor: se a inclusão de mais de um thread torna o código mais complexo porque razão eu deveria projetar código multithread. Processos com vários threads podem realizar mais de uma

6 tarefa simultaneamente. São úteis na criação de processos servidores, criação de animações e no projeto de interfaces com o usuário que não ficam travadas durante a execução de alguma função. Por exemplo, imagine um processo servidor a espera de requisições de serviços, podemos projetá-lo de modo que ao surgir uma solicitação de um serviço por um processo cliente ele crie um thread para atender a solicitação enquanto volta a esperar a requisição de novos serviços. Com isto os processos clientes não precisam esperar o término do atendimento de alguma solicitação para ter sua requisição atendida. O mesmo pode ser dito em relação ao projeto de interfaces com o usuário. O processo pode criar threads para executar as funções solicitadas pelo usuário, enquanto aguarda novas interações. Caso contrário, a interface ficaria impedida 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 enquanto outros threads cuidam das outras tarefas como por exemplo entrada de dados. A rigor todas as aplicações acima como outras aplicações de processos multithread podem ser executados por meio de processos monothreads. No entanto, o tempo gasto na mudança de contexto entre processos na maioria dos sistemas operacionais é muito mais lenta que a simples alternância entre threads, uma vez que a maior parte das informações contextuais são compartilhadas pelos threads de um mesmo processo. 5 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 possui vários threads: thread para garbage collection, thread para monitoramento de eventos, thread para carga de imagens, etc. Criando threads em Java Processos Multithread não é uma invenção da linguagem Java. É possível criar processos multithread com quase todas as linguagens do mercado, como C++, e Object Pascal. No entanto Java incorporou threads ao núcleo básico da linguagem tornado desta forma mais natural o seu uso. Na verdade o uso de

7 threads está tão intimamente ligado a Java que é quase impossível escrever um programa útil que não seja multithread. A classe Thread agrupa os recursos necessários para a criação de um thread. A forma mais simples de se criar um thread é criar uma classe derivada da classe Thread. Por exemplo: class MeuThread extends Thread... É preciso também sobrescrever o método run() da classe Thread. O método run() é o ponto de entrada do thread, da mesma forma que o método main() é ponto de entrada de uma aplicação. O exemplo IX.1 mostra uma classe 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, apesar de 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 exemplo IX.1 o thread imprime cinco vezes o atributo String. Para iniciar a execução de um thread cria-se um objeto da classe e invoca-se o método start() do objeto. O método start() cria o thread e inicia sua execução pelo método run(). Se o método run() for chamado diretamente nenhum thread novo será criado e o método run() será executado no thread corrente. O exemplo IX.2 mostra uma forma de se criar um thread usando a classe definida no exemplo IX.1. 6

8 public class TesteThread1 public static void main (String[] args) new MeuThread("Linha1").start(); 7 Exemplo IX.2 Criação de um Thread. No exemplo acima apenas um thread, além do principal é criado. Nada impede que sejam criados mais objetos da mesma classe para disparar um número maior de threads. O exemplo IX.3 mostra a execução de dois threads sobre 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, por consequência, sobre uma instância do método run(). A saída gerada pela execução do exemplo IX.3 depende do sistema operacional subjacente. Uma saída possível é a seguinte: 0 Linha2 0 Linha1 1 Linha2 1 Linha1 2 Linha2 2 Linha1 3 Linha2 3 Linha1 4 Linha2 4 Linha1 FIM! Linha2 FIM! Linha1 A saída acima mostra que os threads executam intercaladamente. No entanto, em alguns sistemas operacionais os threads do exemplo IX.3 executariam um após o outro. A relação entre a sequência de execução e o sistema operacional e dicas de como escrever programas multithread com

9 sequência de execução independente de plataforma operacional serão em uma seção mais adiante neste mesmo capítulo. 8 Criando threads por meio da interface Runnable Algumas vezes não é possível criar uma subclasse da classe Thread porque a classe já deriva outra classe, por exemplo a classe Applet. Outras vezes, por questões de pureza de projeto o projetista não deseja derivar a classe Thread simplesmente para poder criar um thread uma vez que isto viola o significado da relação de classe-subclasse. Para esses casos existe a interface Runnable. A interface Runnable possui apenas um método para ser implementado: o método run(). Para criar um thread usando a interface Runnable é preciso criar um objeto da classe Thread, passando para o construtor uma instância da classe que implementa a interface. Ao invocar o método start() do objeto da classe Thread, o thread criado, inicia sua execuçã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 criado iniciará a execução sobre o método run() do objeto passado como parâmetro, e não sobre o método run() do objeto Thread.

10 Nada impede que seja criado mais de um thread executando sobre o mesmo objeto: Thread t1 = new Thread(ob1); Thread t2 = new Thread(ob1); Neste caso alguns cuidados devem ser tomados, uma vez que existe o compartilhamento das variáveis do objeto por dois threads. Os problemas que podem advir de uma situação como esta serão tratados mais adiante. 9 A classe Thread A classe Thread é extensa, possuindo vários construtores, métodos e variáveis públicas. Aqui mostraremos apenas os mais usados. Hierarquia A classe Thread deriva diretamente da classe Object. java.lang.object java.lang.thread Construtores Construtor Thread(ThreadGroup g, String nome) Thread(Runnable ob, String nome) Thread(ThreadGroup g, Runnable ob, String nome) Thread(String nome) Thread() Thread(Runnable ob) Descrição Cria um novo thread com o nome especificado dentro do grupo g. Cria um novo thread para executar sobre o objeto ob, com o nome especificado. Cria um novo thread para executar sobre o objeto ob, dentro do grupo g, com o nome especificado. Cria um novo thread com o nome especificado. Cria um novo thread com o nome default. Cria um novo thread para executar

11 10 Thread(ThreadGroup g, Runnable ob) sobre o objeto ob. Cria um novo thread para executar sobre o objeto ob, dentro do grupo g. 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 para obter a referência de threads por meio do seu nome. Métodos Método currentthread() destroy() dumpstack() enumerate(thread[] v) getname() getpriority() getthreadgroup() resume() run() setname(string name) setpriority(int newpriority) sleep(long millis) sleep(long millis, int nanos) start() stop() suspend() yield() Descrição Retorna uma referência para o thread corrente em execução. Destroi o thread sem liberar os recursos. Imprime a pilha de chamadas do thread corrente. Copia para o array todos os thread ativos no grupo do thread. Obtém o nome do thread. Obtém a prioridade do thread. Retorna o grupo do thread. Reassume a execução de um thread previamente suspenso. Se o thread foi construído usando um objeto Runnable separado então o método do objeto Runnable é chamado. Caso contrário nada ocorre. Muda o nome do thread. Muda a prioridade do thread. Suspende o thread em execução o número de milisegundos especificados. Suspende o thread em execução o número de milisegundos mais o número de nanosegundos especificados. Inicia a execução do thread. A máquina virtual chama o método run() do thread. Força o encerramento do thread. Suspende a execução de um thread. Faz com que o thread corrente interrompa permitindo que outro thread seja executado. Tabela IX.2 Métodos da classe Thread.

12 11 A tabela X.2 apresenta os principais métodos do classe Thread. Alguns métodos muito usados nas versões anteriores do SDK1.2 foram depreciados na versão atual por serem considerados inseguros ou com tendência a causarem deadlock. Os métodos depreciados são: stop(), suspend(), resume() e destroy(). Deadlock Travamento 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 thread B libere um recurso, enquanto que o thread B só libera o recurso esperado por A se obter um recurso mantido por A. Desta forma os dois threads são impedidos indefinidamente de prosseguir. Existem alguns métodos da classe Object que são importantes para o controle dos threads. O leitor pode estar se perguntando porque métodos relacionados threads estão na superclasse Object que é mãe de todas as classe em Java. A razão disso é que esses métodos lidam com um elemento associado a todo objeto e que é usado para promover o acesso exclusivo aos objetos. Esse elemento é chamado de monitor. Na seção que aborda a sincronização os monitores serão discutidos mais detalhadamente. Os métodos herdados relacionados com controle dos threads estão descritos na tabela IX.3. Método notify() notifyall() wait() wait(long timeout, int nanos) wait(long timeout) Descrição Notifica um thread que está esperando sobre um objeto. Notifica todos os threads que está esperando sobre um objeto. Espera para ser notificado por outro thread. Espera para ser notificado por outro thread. 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ínimo e default para a prioridade de execução dos threads. Java estabelece dez valores de prioridade. Como essas prioridades são relacionadas com as prioridades do

13 ambiente operacional depende da implementação máquina virtual e pode influenciar no resultado final da execução do programa. Mais adiante abordaremos a influência do ambiente operacional na execução de programas multithread. 12 Variável static final int MAX_PRIORITY static final int MIN_PRIORITY static final int NORM_PRIORITY Descrição A prioridade máxima que um thread pode ter. A prioridade mínima que um thread pode ter. 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 e suspenso até que passe para o estado morto. A transição de um estado para outro pode ser determinada por uma chamada explícita a um método ou devida a ocorrência de algum evento a nível de ambiente operacional ou de programa. Estados Ativos thread em Execução thread novo thread suspenso thread morto 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á as transições do estado em execução para o estado suspenso e vice-versa e desses para o estado morto podem ser disparadas tanto pela invocação de variados métodos como pela ocorrência de eventos. O exemplo IX.5 mostra as ocorrência de transição em um código.

14 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 de estados. sleep(), yield(), join(), destroy(), stop(), suspend() e resume(). Agora que vimos os estados que podem ser assumidos por um thread em seu ciclo de vida vamos examinar mais detalhadamente alguns dos métodos responsáveis pela mudança de estado de um thread.

15 sleep() Java na Prática Volume II 14 O método sleep()é um método estático e possui as seguintes interfaces: static void sleep(long ms) throws InterruptedException ou static 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 o método pode lançar a exceção InterruptedException, é preciso envolver a chamada em um bloco try/catch ou propagar a exceção. O exemplo IX.6 define uma espera mínima de 100 milisegundos entre cada volta do loop. Note que o tempo de suspensão do thread pode ser maior que o especificado, uma vez que outros threads de maior ou mesmo de igual prioridade podem estar sendo executados 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 Sistemas Operacionais não suportam resolução de nanosegundos. Mesmo a resolução a nível de unidade de milisegundo não é suportada pela maioria dos SOs. No caso do SO não suportar a resolução de tempo solicitada, o tempo será arredondado para a nível de resolução suportado pela plataforma operacional.

16 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 libere automaticamente a CPU para outro thread de mesma prioridade. Se não houver nenhum outro thread de mesma prioridade aguardando, então o thread corrente mantém a posse da CPU. O exemplo IX.7 altera o exemplo IX.1 de modo a permitir que outros threads de mesma prioridade sejam executados a cada volta do 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 da execuçã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);

17 Na primeira versão o thread corrente espera indefinidamente pelo encerramento da execução do segundo thread. Na segunda e terceira versão o thread corrente espera pelo término da execução do segundo thread até no máximo um período de tempo prefixado. O exemplo IX.8 mostra o como usar o método join(). 16 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 ativo t1.join(); // Espera pelo término do thread System.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(), and resume() tornaram-se deprecated uma vez que a utilização desses métodos tendia a gerar erros. No entanto, devido a grande quantidade de código que ainda utiliza estes método, acreditamos que seja importante mencioná-los. O método stop() é um método de instância que encerra a execução do thread ao qual pertence. Os recursos alocados ao thread são liberados. É recomendável substituir o método stop() pelo simples retorno do método run().

18 O método suspend() é um método de instância que suspende a execução do thread ao qual pertence. Nenhum recurso é liberado, inclusive os monitores que possuir no momento da suspensão (os monitores serão vistos mais adiante e servem para controlar o acesso à variáveis compartilhadas). Isto faz com que o método suspend() tenda a ocasionar deadlocks. O método resume() é um método de instância que reassume a execução do thread ao qual pertence. Os métodos suspend() e resume() devem ser substituídos respectivamente pelos métodos wait() e notify(), como veremos mais adiante. O método destroy() é um método de instância que encerra a execução do 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 simples retorno do método run(). 17 Daemon Threads Daemon threads são threads que rodam em background com a função de prover algum serviço mas não fazem parte do propósito principal do programa. Quando só existem threads do tipo daemon o programa é encerrado. Um exemplo de daemon é o thread para coleta de lixo. Um thread é definido como daemon por meio do método de instância setdaemon(). Para verificar se um thread é um daemon é usado o método de instâ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

19 Java na Prática Volume II 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. 18 No exemplo IX.9 o método main() da classe TestaDaemon cria um objeto da classe ThreadDaemon. O construtor da classe ThreadDaemon define o thread como daemon por meio do método setdaemon() e inicia a execuçã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 toda vez que a adquire. Após a criação da instância da classe ThreadDaemon no método main() é testado se o thread criado é um daemon, utilizando para esse fim o método isdaemon(). Depois disso o programa simplesmente espera o usuário pressionar a tecla <enter>. O programa termina logo em seguida ao acionamento da tecla, mostrando dessa forma que o programa permanece ativo apenas enquanto existem threads não daemons ativos. Influência do Sistema Operacional no Comportamento dos Threads Apesar da linguagem Java prometer a construção de programas independentes de plataforma operacional, o comportamento dos threads pode ser fortemente influenciado pelo sistema operacional subjacente. Portanto, o programador deve tomar alguns cuidados se deseja construir programas que funcionem da mesma forma, independente do ambiente onde está sendo executado.

20 Alguns sistemas operacionais não oferecem suporte a execução de threads. Neste caso, cada processo possui apenas um thread. Mesmo em sistemas operacionais que oferecem suporte a execução de múltiplos threads por processo o projetista da máquina virtual pode optar por não usar o suporte nativo a threads. Deste modo, é responsabilidade da máquina virtual criar um ambiente multithread. Threads implementados desta forma, a nível de usuário, são chamados de green-threads. As influências da plataforma operacional podem agrupadas em dois tipos: 1) Forma de escalonamento de threads. O ambiente pode adotar um escalonamento não preemptivo ou preemptivo. No escalonamento não preemptivo (também chamado de cooperativo) um thread em execução só perde o controle da CPU (Central Processing Unit) se a liberar voluntariamente ou se necessitar de algum recurso que ainda não está disponível. Já no escalonamento preemptivo, além das formas acima um thread pode perde o controle da CPU por eventos externos, como o fim do tempo máximo definido pelo ambiente para a execução contínua de um thread (fatia de tempo) ou porque um thread de mais alta prioridade está pronto para ser executado. Exemplos de sistemas operacionais não preemptivos são Windows 3.1 e IBM OS/2. Exemplos de sistemas operacionais preemptivos são Windows 95/98/NT e Linux, QNX, e muitos outros. Alguns sistemas operacionais adotam uma abordagem híbrida, suportando tanto o modelo cooperativo como o preemptivo, como o Solaris da Sun. 2) Relacionamento entre os níveis de prioridades definidas na linguagem Java e os níveis de prioridades definidas nos Sistemas Operacionais. Em um SO preemptivo um thread de uma determinada prioridade perde a posse da CPU para um thread de prioridade mais alta que esteja pronto para ser executado. A linguagem Java prevê dez níveis de prioridades que podem ser atribuídas aos threads. No entanto, cada SO possui um número de prioridades diferente e o mapeamento das prioridades da linguagem Java para as prioridades do SO subjacente pode influenciar o comportamento do programa. Forma de escalonamento de threads 19

21 A especificação da máquina virtual Java determina que a forma de escalonamento de threads seja preemptiva. Portanto, mesmo em ambiente operacionais cooperativos a máquina virtual deve garantir um escalonamento preemptivo. No entanto, um escalonamento preemptivo não obriga a preempção por fim de fatia de tempo. Podemos ter um escalonamento preemptivo onde um thread de mais alta prioridade interrompe o thread que tem a posse da CPU mas não existe preempção por fim de fatia de tempo. Um escalonamento onde threads de mesma prioridade intercalam a posse da CPU por força do fim da fatia de tempo é chamado de escalonamento Round-Robin. A especificação da máquina virtual Java não prevê o escalonamento Round-Robin, mas também não o descarta, abrindo uma possibilidade de implementações distintas de máquina virtual e introduzindo o não determinismo na execução de programas multithread. Por exemplo, o exemplo IX.3 poderia ter uma saída distinta da apresentada anteriormente caso seja executado por uma máquina virtual que não implementa o escalonamento Round-Robin. Nesse caso a saída seria a seguinte: 0 Linha2 1 Linha2 2 Linha2 3 Linha2 4 Linha2 FIM! Linha2 0 Linha1 1 Linha1 2 Linha1 3 Linha1 4 Linha1 FIM! Linha1 Neste caso, se o programador deseja que a execução de threads se processe de forma alternada, independentemente da implementação da máquina virtual, então é necessário que ele insira código para a liberação voluntária da CPU. Isso pode ser feito com o método yield() ou com o método sleep(). 20 Relacionamento entre os níveis de prioridades definidas na linguagem Java e os níveis de prioridades definidas nos Sistemas Operacionais. Como já dissemos a linguagem Java prevê dez níveis de prioridades que podem ser atribuídas aos threads. Na verdade são onze prioridades, mas a prioridade nível 0 é reservada para threads internos. As prioridades atribuídas

22 aos threads são estáticas, ou seja não se alteram ao longo da vida do thread, a não ser que por meio de chamadas a métodos definidos para esse propósito. A classe thread possui variáveis públicas finais com valores de prioridade predefinidos, como mostrado na tabela IX.4. No entanto, os sistemas operacionais podem possuir um número maior ou menor de níveis de prioridades. Vamos citar um exemplo: o MSWindows 95/98/NT. Este sistema possui apenas sete níveis de prioridades e estes sete níveis devem ser mapeados para os onze níveis de prioridades especificados em Java. Cada máquina virtual fará este mapeamento de modo diferente, porém a implementação comum é mostrada na tabela IX.5. Prioridades Java Prioridades MSWindows 0 THREAD_PRIORITY_IDLE 1(Thread.MIN_PRIORITY) THREAD_PRIORITY_LOWEST 2 THREAD_PRIORITY_LOWEST 3 THREAD_PRIORITY_BELOW_NORMAL 4 THREAD_PRIORITY_BELOW_NORMAL 5(Thread.NORM_PRIORITY) THREAD_PRIORITY_NORMAL 6 THREAD_PRIORITY_ABOVE_NORMAL 7 THREAD_PRIORITY_ABOVE_NORMAL 8 THREAD_PRIORITY_HIGHEST 9 THREAD_PRIORITY_HIGHEST 10(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 Java serão mapeados para um mesmo nível de prioridade em MSWindows. Isto pode levar a resultados inesperados caso o programador projete uma aplicação esperando, por exemplo, que um thread de prioridade 4 irá interromper um thread de prioridade 3. Para evitar este tipo de problema o programador pode adotar 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 prioridades aos threads; ou 2) não se basear em níveis de prioridades para definir o escalonamento de threads, utilizando, alternativamente, primitivas de sincronização que serão abordadas na próxima seção. 21

23 Compartilhamento de Memória e Sincronização 22 Como já foi dito, mais de um thread pode ser criado sobre um mesmo objeto. Neste caso cuidado especiais devem ser tomados, uma vez que os threads compartilham as mesmas variáveis e problemas podem surgir se um thread está atualizando uma variável enquanto outro thread está lendo ou atualizando a mesma variável. Este problema pode ocorrer mesmo em threads que executam sobre objetos distintos, já que os objetos podem possuir referências para um mesmo objeto. O exemplo IX.8 mostra a execução de dois threads sobre um mesmo objeto. O nome do thread é usado para que o thread decida que ação tomar. O thread de nome um ontem um número de 0 a 1000 gerado aleatoriamente e o coloca na posição inicial de um array de dez posições. As outras posições do array são preenchidas com os nove números inteiros seguintes ao número inicial. O thread de nome dois imprime o conteúdo do vetor. Obviamente o programa é apenas ilustrativo, não possuindo aplicação prática. A intenção inicial do projetista é obter na tela sequências de dez números inteiros consecutivos iniciados aleatoriamente. No entanto, como os dois threads compartilham o mesmo objeto e não existe qualquer sincronismo entre 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[])

24 Java na Prática Volume II CalcDez ob = new CalcDez(); Thread t1 = new Thread(ob, um ); Thread t2 = new Thread(ob, dois ); t1.start(); t2.start(); 23 Exemplo IX.8 Dois threads executando sobre o mesmo objeto. Se a máquina virtual não implementar um escalonamento Round-Robin apenas um thread será executado, visto que os dois threads possuem a mesma prioridade. 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 foi obtido em Pentium 100MHz executando a máquina virtual da Sun, versão 1.2, sob o sistema operacional MSWindows Serie iniciada por Serie iniciada por Figura IX.4 Saída do exemplo IX.8. Podemos notar as sequências estão misturadas, mostrando que cada thread interrompe o outro no meio da execução da tarefa especificada. O mesmo problema pode mesmo em threads que executam sobre objetos diferentes, bastando que cada thread possua referência para um mesmo objeto. O exemplo IX.9 mostra a execução de dois threads sobre objetos distintos.

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

26 25 É importante que o leitor não confunda o exemplo IX.9 com o exemplo IX.8 achando que nos dois exemplos os dois threads executam sobre o mesmo objeto, 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 uma classe que implementa a interface Runnable. Apesar de parecer que no exemplo IX.9 ambos os threads executaram sobre um mesmo objeto da classe Compartilhada que é passado como argumento, na verdade cada thread executará sobre sua própria instância da classe CalcDez2, sendo que o objeto da classe Compartilhada é referenciado pelos dois threads. O comportamento 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 pelo objeto da classe Compartilhada. Este tipo de situação, onde o resultado de uma computação depende da forma como os threads são escalonados, é chamada de condições de corrida (Race Conditions). É um problema a ser evitado uma vez que o programa passa a ter um comportamento não determinístico. Atomicidade de Instruções e Sincronização do Acesso à Sessões Críticas A condição de corrida ocorre porque os acesso à áreas de memória compartilhada não é feita de forma atômica, e nem de forma exclusiva. Por forma atômica queremos dizer que o acesso é feito por meio de várias instruções e pode ser interrompido por outro thread antes que toda as instruções que compõem o acesso sejam executadas. Por forma exclusiva queremos dizer que um thread podem consultar/atualizar um objeto durante a consulta/atualização do mesmo objeto por outros threads. Poucas operações são atômicas em Java. Em geral, as atribuições simples, com exceção dos tipos long e double, são atômicas, de forma que o programador não precisa se preocupar em ser interrompido no meio de uma operação de atribuição. No entanto, no caso de operações mais complexas sobre variáveis compartilhadas é preciso que o programador garanta o acesso exclusivo a essas variáveis. Os trechos de código onde é feito o acesso às variáveis compartilhadas são chamados de Seções Críticas ou Regiões Críticas. Uma vez determinada uma região crítica como garantir o acesso exclusivo? A linguagem Java permite que o programador garanta o acesso exclusivo por meio utilizando o conceito de monitor. O conceito de monitor foi proposto por C. A. R. Hoare em 1974 e pode ser encarado como um objeto que

27 garante a exclusão mútua na execução dos procedimentos a ele associados. Ou seja, apenas um procedimento associado ao monitor pode ser executado em um determinado momento. Por exemplo, suponha que dois procedimentos A e B estão associados a um monitor. Se no momento da invocação do procedimento A algum o procedimento B estiver sendo executando o processo ou thread que invocou o procedimento A fica suspenso até o término da execução do procedimento B. Ao término do procedimento B o processo que invocou o procedimento A é acordado e sua execução retomada. O uso de monitores em Java é uma variação do proposto por Hoare. na linguagem Java todo objeto possui um monitor associado. Para facilitar o entendimento podemos encarar o monitor como um detentor de um passe. Todo thread pode pedir emprestado o passe ao monitor de um objeto antes de realizar alguma computação. Como o monitor possui apenas um passe, apenas um thread pode adquirir o passe em um determinado instante. O passe tem que ser devolvido para o monitor para possibilitar o empréstimo do passe a outro thread. A figura IX.5 ilustra essa analogia. 26

28 27 passe Monitor de x! Monitor de x Objeto x Objeto x thread t1! thread t2 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. Monitor de x! Monitor de x Objeto x Objeto x 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 pela autorização de um monitor. Nos resta saber como solicitar o passe ao monitor. Isto é feito por meio da palavra chave synchronized. Existem duas formas de se usar a palavra chave synchronized: na declaração de métodos e no início de blocos. O exemplo IX.10 mostra duas versões da classe FilaCirc que implementa uma fila circular de valores inteiros: uma com métodos synchronized e outra com blocos synchronized. Um objeto desta classe pode ser compartilhado por dois ou mais threads para implementar o exemplo clássico de concorrência do tipo produtor/consumidor.!

29 28 a) Versão com métodos synchronized class FilaCirc private final int TAM = 10; private int vetint[]; private int inicio, total; b) Versão com blocos synchronized class FilaCirc private final int TAM = 10; private int vetint[]; private int inicio, total; public FilaCirc() vetint=new int[tam]; inicio=0; total =0; public 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; 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 circular de inteiros. A palavra chave synchronized na frente dos métodos de instância significa que o método será executado se puder adquirir o monitor do objeto a quem pertence o método 1. 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étodo de um objeto da classe FilaCirc nenhum outro thread que compartilha o mesmo 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 para facilitar o entendimento do leitor. Quando se trata de monitores os termos mais usados são: adquirir o monitor e liberar o monitor.

30 pelo primeiro thread termine. Caso outro thread invoque um método do mesmo objeto ficará bloqueado até que possa adquirir o monitor. O leitor pode estar se perguntando sobre a necessidade de sincronizar os métodos da classe FilaCirc uma vez que ocorrem apenas atribuições simples a elementos individuais de um vetor e as atribuições de inteiros são atômicas. De fato o problema ocorre não na atribuição dos elementos e sim na indexação do array. Por exemplo, a instrução inicio = (++inicio)%tam; do método getelement() não é atômica. Suponha que a os métodos da classe FilaCirc não são sincronizados e que as variáveis inicio e total possuem os valores 9 e 1 respectivamente. Suponha também que thread invocou o método getelement() e foi interrompido na linha de código mostrada acima após o incremento da variável inicio mas antes da conclusão da linha de código. Nesse caso o valor de inicio é 10. Se neste instante outro thread executar o método getelement() do mesmo objeto ocorrerá uma exceção IndexOutOfBoundsException 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 uma vez o mesmo elemento. Por exemplo, se um thread for interrompido no mesmo local do caso anterior, outro thread pode obter o mesmo elemento, uma vez que os valores de inicio e total não foram alterados. Na verdade o número de situações problemáticas, mesmo para esse exemplo pequeno, é enorme e perderíamos muito tempo se tentássemos descreve-las em sua totalidade. Em alguns casos pode ser indesejável sincronizar todo um método, ou pode-se desejar adquirir o monitor de outro objeto, diferente daquele a quem pertence o método. Isto pode ser feito usando a palavra chave synchronized na frente de blocos. Este forma de sincronização é mostrada no exemplo IX.10.b. Neste modo de usar a palavra-chave synchronized é necessário indicar o objeto do qual tentara-se adquirir o monitor. Caso o monitor seja adquirido o bloco é executado, caso contrário o thread é suspenso até que possa adquirir o monitor. O monitor é liberado no final do bloco. 29

31 No exemplo IX.10.b o monitor usado na sincronização é o do próprio objeto do método, indicado pela palavra chave this. Qualquer outro objeto referenciável no contexto poderia ser usado. O que importa que os grupos de threads que possuem áreas de código que necessitam de exclusão mútua usem o mesmo 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 principalmente quando os métodos são muito pequenos ou não realizam computações muito complexas. No entanto, se o método for muito longo ou levar muito tempo para ser executado, sincronizar todo o método pode travar em demasia a execução da aplicação. Nesses casos, a sincronização somente das seções críticas é mais indicada. Outra vantagem da segunda forma de sincronização é a liberdade no uso de monitores qualquer objeto referenciável. Isto permite a implementação sincronizações mais complexas como veremos mais adiante. O exemplo IX.11 mostra como pode ser usado um objeto da classe FilaCirc. public class TestaFilaCirc extends Thread private FilaCirc obj; private int tipo; 30 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 exemplo IX.11 seria o seguinte:... Elemento obtido:154

32 Elemento gerado:725 Fila vazia! Elemento gerado:801 Elemento obtido:725 Elemento gerado:204 Elemento obtido: É importante observar que o monitor em Java por si só não implementa a exclusão mútua. Ele é apenas um recurso que pode ser usado pelo programador para implementar o acesso exclusivo à variáveis compartilhadas. Cabe ao programador a responsabilidade pela uso adequado deste recurso. Por exemplo se o programador esquecer de sincronizar um bloco ou método que necessita de exclusã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 a palavra chave synchronized com muito cuidado. A sincronização custa muito caro em se tratando de ciclos de CPU. A chamada de um método sincronizado é por volta de 10 vezes mais lenta do que a chamada de um método não sincronizado. Por essa razão use sempre a seguinte regra: não sincronize o que não for preciso. Comunicação entre Threads: wait() e notify() O exemplo IX.10 não é um modelo de uma boa implementação de programa. 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, o thread que retira os elementos da fila tenta obter um elemento a cada volta do laço de iteração mesmo que a fila esteja vazia. Isto é um desperdício de tempo de processador e pode tornar o programa bastante ineficiente. Alguém poderia pensar em uma solução onde o thread testaria se a condição desejada para o processamento ocorre. Caso a condição não ocorra o thread poderia executar o método sleep() para ficar suspenso por algum tempo para depois testar novamente a condição. O thread procederia desta forma até que a condição fosse satisfeita. Este tipo de procedimento economizaria alguns ciclos de CPU, evitando que a tentativa incessante de executar o procedimento mesmo quando não há condições. O nome desta forma de ação,

33 onde o procedimento a cada intervalo de tempo pré-determinado testa se uma condiçã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 a possibilidade 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, o thread 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 o trecho 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 a computaçã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 que detém o monitor espera que a condição seja satisfeita e o thread que pode satisfazer a condição não pode prossegui porque não pode adquirir o monitor. O que precisamos é um tipo de comunicação entre threads que comunique que certas condições foram satisfeitas. Além disso, é preciso que, ao esperar por determinada condição, o thread libere o monitor. Esta forma de interação entre threads é obtido em Java com o uso dos métodos de instância wait(), notify() e notifyall(). Como vimos anteriormente, esses métodos pertencem à classe Object e não à classe Thread. Isto ocorre porque esses métodos atuam sobre os monitores, que são objetos relacionados a cada instância de uma classe Java e não sobre os threads. Ao invocar o método wait() de um objeto o thread é suspenso e inserido em uma fila do monitor do objeto, permanecendo na fila até receber uma notificação. Cada monitor possui sua própria fila. Ao invocar o método notify() 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 threads que estão na fila do monitor do objeto são notificados. A única exigência é que esses métodos sejam invocados em um thread que detenham a posse do monitor do objeto a que pertencem. Essa exigência faz sentido uma vez que eles sinalizam a threads que esperam na fila desses monitores. Devido a essa exigência a invocação desses métodos ocorre em métodos ou blocos sincronizados. O exemplo IX.12 mostra as formas mais comuns de chamadas desses métodos. Note que o thread deve possuir o monitor do objeto ao qual pertence o método. Por isso, nos exemplo IX.12 b e c, o objeto sincronizado no bloco é o mesmo que invoca os métodos notify() e notifyall(). a) b) class X class Y 32

34 ... public synchronized int ma()... // Espera uma condição while(!cond) wait(); // Prossegue com a // condição satisfeita Java na Prática Volume II 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() e notifyall(). Outra observação importante é que o thread que invoca o método wait() o faz dentro de um laço sobre a condição de espera. Isto ocorre porque apesar de ter sido notificado isto não assegura que a condição está satisfeita. O thread pode ter sido notificado por outra razão ou entre a notificação e a retomada 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ção pertence ao thread que notificou. Mesmo após a liberação do monitor nada

35 garante que o thread notificado ganhe a posse do monitor. Outros threads podem ter 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 para notificação. O exemplo IX.13 é uma versão do exemplo IX.10a que usa os métodos de notificação para evitar problemas como a espera ocupada. O exemplo 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; 34 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 com notificação. A necessidade de se testar a condição em loop pode ser observada na figura IX.6 que mostra a evolução da execução de três threads sobre objetos que compartilham uma instância da classe FilaCirc. O thread 3 executa o método addelement(), no entanto, em virtude da condição total==tam é obrigado a invocar o método wait() e esperar uma notificação. O próximo

36 thread a assumir a CPU é o thread 1 que executa o método getelement(), que estabelece a condição total<tam e executa um notify(). No entanto, o próximo thread a assumir a CPU é o thread 2 e não o thread 3. O thread 2 executa o método addelement(), o qual estabelece novamente a condição total==tam. Quando o thread 3 assumi novamente a CPU, uma vez que foi notificado, testa a condição e invoca novamente o método wait() para esperar a condição favorável à execução. Caso não testasse a condição em um loop o thread 3 tentaria inserir um elemento em uma fila cheia. O método notify() não indica que evento ocorreu. No caso do exemplo IX.13 existem dois tipos de eventos (a fila não está cheia e a fila não está vazia), no entanto, podemos observar que não existe a possibilidade de um thread ser notificado em decorrência de um evento diferente do que está aguardando. 35 thread 3 Método: addelement() condição: total == TAM thread 2 thread 1 Método: addelement() condição: total < TAM Método: getelement() Tempo Executando Esperando CPU Esperando notificação 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ários threads aguardando em um mesmo monitor mas esperando por evento diferentes. Neste caso podemos usar o notifyall() para notificar todos os threads que esperam em um único monitor que um evento ocorreu. Cada thread, a medida que fosse escalado, testaria se ocorreu condição para a execução e em caso positivo prosseguiria na execução e em caso contrário voltaria a aguardar no monitor. 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

37 de cada thread são colocadas em uma fila implementada por um objeto da classe Vector. 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ão criadas na primeira tentativa de acesso, tanto na leitura quanto no armazenamento. Não existe bloqueio devido à fila cheia, uma vez que as filas são implementadas por objetos da classe Vector que crescem conforme a necessidade. Portanto, o único evento que necessita ser notificado é a chegada de alguma mensagem. Como todos os threads aguardam sobre o mesmo monitor é usado o método notifyall() para notificar todos os threads. 36 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();

38 Java na Prática Volume II // A mensagem é removida da fila listamen.removeelementat(0); return temp; Exemplo IX.14 Gerenciador de mensagens. 37 O exemplo IX.15 mostra como pode ser usado o gerente de filas do exemplo IX.14. Devido o uso da classe ThreadGroup assim como vários de seus métodos, resolvemos numerar as linhas de código do exemplo IX.15 para melhor podermos explicar o seu funcionamento 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);

39 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 da ThreadGroup formando assim uma árvore onde todos os grupos, exceto o primeiro 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 esse agrupamento para poder acessar cada thread. As linhas 1 a 18 definem a classe que será usada para criação de objetos receptores de mensagens. Na linha 3 é declarada a variável que irá referenciar um objeto do tipo GerenteMen. As linhas 4 a 8 contém o código do único construtor da classe. Ele recebe uma referência para o grupo de threads ao qual deve se associar, o nome que deve ser atribuído ao thread e a referência ao gerente de filas. Na linha 6 os primeiros dois parâmetros são passados ao construtor 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 para referenciar a fila de mensagens do thread. Entre as linhas 11 e 16 é executado um laço infinito onde o thread recebe e imprime as mensagens recebidas. Na linha 14 o thread testa se a mensagem recebida é igual a fim. Neste caso o thread encerra sua execução.

40 As linhas 20 a 51 definem a classe que será usada para criação do objeto gerador de mensagens. Este exemplo foi projetado para lidar com apenas um thread gerador de mensagem. Modificações devem ser realizadas para tratar de aplicações com mais de um thread gerador de mensagens. Na linha 22 é declarada a variável que irá referenciar um objeto do tipo GerenteMen. As linhas 23 a 27 contém o código do único construtor da classe. Ele recebe uma referência para o grupo de threads ao qual deve se associar, o nome que deve ser atribuído ao thread e a referência ao gerente de filas. Na linha 25 os primeiros dois parâmetros são passados ao construtor da superclasse. Na linha 26 a referência ao gerente de filas é atribuída à variável da instância. As linhas 28 a 50 contém o código do método run() que é o método de entrada do thread. Na linha 29 é obtido o nome do thread corrente que será usado na impressão de mensagens. 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 contendo referê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. Na linha 34 é realizado um teste para a verificação da necessidade de criar o vetor que irá conter as referências para os threads ativos. Ele deve ser criado a primeira vez e toda vez que a capacidade do vetor for diferente do número de threads ativos do grupo. O tamanho do vetor é determinado pelo método de instância activecount() da classe ThreadGroup. A linha 36 contém o có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 do vetor de referências. O teste da linha 38 impede que seja enviada uma mensagem para o próprio gerador. Essas mensagens são descartadas. As linhas 40 a 42 tratam da impressão e envio da mensagem construída. Já fora da iteração, as linhas 45 a 49 tratam da do envio da mensagem fim para todos os threads receptores, o que fará com que encerrem sua execução. 39

41 As linhas 53 a 64 definem a classe que será usada como ponto de entrada da 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 as referências para os threads receptores. Elas são obtidas dinamicamente por meio do grupo de threads. Um trecho possível da saída obtida na execução do programa do exemplo IX.15 seria o seguinte:... g>mensagem enviada para r_dois:mensagem 88 r_um>mensagem recebida:mensagem 87 g>mensagem enviada para r_dois:mensagem 90 r_dois>mensagem recebida:mensagem 88 g>mensagem enviada para r_um:mensagem 91 r_dois>mensagem recebida:mensagem 90 g>mensagem enviada para r_um:mensagem 93 r_um>mensagem recebida:mensagem 91 g>mensagem enviada para r_um:mensagem 95 r_um>mensagem recebida:mensagem 93 g>mensagem enviada para r_um:mensagem 96 r_um>mensagem recebida:mensagem 95 g>mensagem enviada para r_um:mensagem 97 r_um>mensagem recebida:mensagem 96 g>mensagem enviada para r_dois:mensagem 99 r_um>mensagem recebida:mensagem 97 r_dois>mensagem recebida:mensagem 99 Pressione qualquer tecla para continuar Otimizando a Programação Multithread Existe um problema óbvio com a abordagem do exemplo IX.14: a mensagem é dirigida a apenas um thread mas todos serão notificados, sobrecarregando o sistema, uma vez que todos os threads precisaram testar se a mensagem é destinada a eles. Para contornar esses problema é necessário vislumbrar uma forma de notificar apenas o thread destinatário. Essa solução pode ser obtida se cada thread esperar em um monitor de um objeto diferente. Não importa o tipo do objeto desde que seja referenciável pelo thread receptor e pelo thread que irá armazenar a mensagem. Um candidato natural é a fila de mensagem de cada thread. Existe uma fila para cada thread e o

42 thread que armazena a mensagem tem acesso a todas a filas por meio da tabela hash. O exemplo IX.16 mostra uma versão do exemplo IX.14 que utiliza esta té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(); 41 // A mensagem é removida da fila listamen.removeelementat(0); return temp;

43 42 Exemplo IX.16 Gerenciador de mensagens otimizado. Note que os métodos wait() e notify() invocados pertencem à fila relacionada com cada thread. O exemplo IX.15 pode ser usado sem modificações para testar essa versão. Criando outros mecanismos de sincronização Existem várias propostas de primitivas de sincronização. Dentre as mais comuns podemos citar os semáforos, mutex, variáveis condicionais, monitores e encontros (rendevouz). Cada uma dessas primitivas é mais adequada a um determinado propósito. A implementação de monitores na linguagem Java, juntamente com os métodos wait() e notify() que formam um tipo de variáveis condicionais podem ser combinadas para implementar muitas dessas outras primitivas, de modo a atender objetivos específicos. Para exemplificar essa possibilidade mostraremos como implementar um semáforo usando as primitivas de sincronização da linguagem Java. Um semáforo é uma variável inteira sobre a qual pode-se realizar as seguintes operações: Operação inicializar P V Descrição Um valor inteiro maior ou igual a zero é atribuído ao semáforo. Se o semáforo é maior que zero, o semáforo é decrementado. Caso contrário, o thread é suspenso até que o semáforo contenha um valor maior que zero. Incrementa o semáforo e acorda os threads que estiverem bloqueados 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 quando existe a necessidade de comunicação entre dois ou mais processos, como no caso de sistemas do tipo produtor/consumidor. Por exemplo, suponha dois processos onde um coloca mensagens em buffer e outro retira as mensagens. Os processos podem 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 de mensagens. A figura IX.xx mostra os esquemas dos processos. A implementação dos semáforos em Java pode ser visto no exemplo IX.xx e o uso dos semáforos

44 em uma situação como a ilustrada pela figura IX.xx pode ser visto no exemplo IX.xx. 43 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.

45 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);

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

47 46 Capítulo II - Animação Animaçã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 de 1997 uma API que dê suporte avançado para animação. A animação pode ser controlado 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

48 47 Ele não permite interromper a animação. O repaint() chama o update() 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()

49 Java na Prática Volume II ind = ++ind % imgs.length; 48 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)

50 pcmessage = pcmsg; nappletwidth = nwide; nmessagewidth = -1; nypos = -1; nxpos = 0; Java na Prática Volume II 49 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)

51 50 Capítulo III - Programação em rede Diferentemente das linguagens mais populares atualmente, Java foi projetada na era da Internet, e por isso mesmo ferramentas para comunicação dentro da Grande Rede foram incorporadas à linguagem desde a sua concepção. Classes para manipulação de URLs e dos protocolos que constituem a Internet fazem parte do núcleo básico da linguagem. Isto facilita muito a tarefa de desenvolver aplicações que para a Internet ou outras redes que fazem uso do mesmo conjunto de protocolos. Esta é uma das principais forças da linguagem Java. De modo a entendermos como desenvolver aplicações em rede com Java é importante o compreensão de alguns conceitos básicos sobre protocolos de comunicação. Conceitos Sobre Protocolos Usados na Internet Um protocolo de comunicação é um conjunto de formatos e regras usadas para transmitir informação. Computadores distintos devem obedecer estas regras e formatos de modo que se possam comunicar. Podemos encarar o protocolo como a definição de uma linguagem comum de modo a possibilitar a comunicaçã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 camada relativamente inferior na pilha a outra estabelece as regras para a camada superior sobre a utilização de seus serviços. As camadas inferiores fornecem serviços mais básicos de transmissão de dados, enquanto que as camadas superiores oferecem serviços de mais alto nível. Esta forma de organização hierárquica de protocolos é também chamada de pilha de protocolos. A principal pilha de protocolo sobre o qual a Internet se organiza é o TCP/IP. Por simplicidade chamaremos a pilha de protocolos TCP/IP apenas como protocolo TCP/IP ou TCP/IP. A figura XI.1 mostra como se organizam alguns dos protocolos que fazem parte do TCP/IP. A camada física é a responsável pela transporte efetivo dos dados sobre o meio físico. A camada de rede é responsável pela interface lógica entre os computadores. A camada de transporte provê transferência de dados a nível de

52 serviço, e a camada de aplicação provê comunicação a nível de processos ou aplicações. Exemplos de protocolos a nível de aplicação são: FTP, usado para transferê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 de correio eletrônico; SNMP usado para gerência da rede; e MIME que é uma extensão do SMTP para lidar com mensagens com conteúdos diversos. 51 MIME Camada de Aplicação FTP HTTP SMTP TELNET SNMP Camada de Transporte TCP UDP Camada de rede IP Camada física Ethernet, X.25, Token Ring FTP - File Transfer Protocol SMTP - Simple Mail Transfer Protocol HTTP - Hypertext Transfer Protocol SNMP - Simple Network Management Protocol IP - Internet Protocol TCP - Transmission Control Protocol MIME - Multi-purpose Internet Mail Extensions UDP - User Datagram Protocol Figura XI.1 Alguns protocolos da pilha TCP/IP. No processo de transmissão de dados sobre uma rede TCP/IP os dados são divididos em grupos chamados de pacotes. Cada camada adiciona um alguns dados a mais no início de cada pacote para permitir que o pacote chegue ao destino. Os dados adicionados são chamados de headers.

53 52 Dados Aplicação TCP Header Dados Transporte IP Header TCP Header Dados Rede Ethernet Header IP Header TCP Header Dados Física Figura XI.2 Headers adicionados a cada camada de protocolo. Na camada de transporte existem dois protocolos que fazem uso do protocolo IP: o protocolo TCP/IP e o UDP. TCP O protocolo TCP é um protocolo orientado a conexão que provê um fluxo confiável de dados entre dois computadores. Por protocolo orientado a conexão queremos dizer que é estabelecido um canal de comunicação ponto-aponto onde os dados podem trafegar em ambas as direções. O TCP garante que os dados enviados em uma ponta cheguem ao destino, na mesma ordem que foram enviados. Caso contrário, um erro é reportado. Protocolos como HTTP, FTP e TELNET exigem um canal de comunicação confiável e a ordem de recebimento dos dados é fundamental para o sucesso dessas aplicações. UDP No entanto, nem todas as aplicações necessitam destas características do protocolo TCP e o processamento adicional exigido para garantir a confiabilidade e a ordenação dos dados podem inviabilizá-las. Para esses casos existe o protocolo de transporte UDP. UDP é um protocolo para envio de pacotes independentes de dados, chamados de datagramas, de um computador a outro, sem garantias sobre a chegada dos pacotes. O protocolo UDP não é orientado a conexão.

54 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. O número IP é representado por quatro grupos de 8 bits, limitando desta forma o valor numérico de cada grupo ao valor máximo de 255. Um exemplo de número IP é Uma vez que é muito difícil lembrar e atribuir significado a números existe uma forma alternativa de identificar os computadores da rede por meio de nomes. Um ou mais computadores da rede fazem o papel de resolvedores de nomes, mantendo bases de dados que associam o nomes do Hosts à seus números IP. Desta forma é possível um computador comunicar com outro computador por meio do nome lógico e não por meio do número IP. A figura XI.3 ilustra a comunicação entre dois computadores. A Internet é representada como uma nuvem devido a complexidade da rede. Host meucomp.com.br IP: Host outrocomp.edu IP: Figura XI.3 Representação da comunicação entre dois computadores. O representação dos nomes dos computadores na Internet é feita por substrings separadas por. e obedecem uma regra de nomeação que define que o primeiro substring representa o nome da máquina, e os restantes representa o domínio onde está inserida a máquina. Um domínio é um agrupamento de computadores que pertencem a uma instituição, órgão, empresa, ou uma organização qualquer. Assim, no exemplo da figura XI.3 o computador meucomp pertence ao domínio com.br. No Brasil a FAPESP (Fundação de Amparo à Pesquisa do Estado de São Paulo) responsável pela gerência dos nomes dos domínios na Internet.

55 Identificação de Processos (Portas) 54 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 outro faz o papel de cliente do serviço. Em um único Host vários processos podem estar fazendo o papel de servidor, oferecendo serviços através de um único meio físico. Portanto, é preciso uma forma de identificar as servidores em um mesmo Host. Isto é feito por meio da associação de um número inteiro, chamado de porta, 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 variar de 1 a 65535, no entanto os números de 1 a 1023 são reservados para serviços conhecidos como FTP e HTTP. O programador não deve usar estas portas a não ser que esteja implementando algum desses serviços. Nos ambientes Unix as portas que vão de 6000 a 6999 são usadas pelo gerenciador de Interfaces X Windows 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.1 mostra o número da porta de alguns dos serviços mais conhecidos. Protocolo Porta HTTP 80 echo 7 FTP 20,21 SMTP 25 Finger 79 Daytime 13 pop3 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 uma aplicação cliente, bastando para isso que ela indique o nome do Host e o número da porta ao se comunicar. Programação em Rede com Java O pacote java.net contém as classes e interfaces usadas para programação de sistemas em rede com Java. As classes podem ser enquadradas em três categorias:

56 55 1. Classes para comunicação básica em rede. Tratam da comunicação em baixo nível entre aplicações. Outros protocolos podem ser implementados usando como base esta comunicação básica. 2. Classes para comunicação dentro da Web. Estas classes provêem facilidades para acessar conteúdos por meio de URLs. 3. Classes para tratamento dos formatos estendidos da Web. Utilizadas para 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 para a comunicação básica entre dois processos. A tabela XI.2 descreve sucintamente cada uma das classes. Classe Socket ServerSocket DatagramSocket DatagramPacket InetAddress Descrição Provê um socket cliente para comunicação orientada à conexão via protocolo TCP. Provê um socket servidor para comunicação orientada à conexão via protocolo TCP. Provê um socket UDP para comunicação não orientada à conexão. Representa um datagrama que pode ser enviado usando DatagramSocket. 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ção orientada à conexão, enquanto que as classes DatagramSocket e DatagramPacket 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 crie um objeto Socket. É preciso passar o nome ou número IP do Host e o número da porta onde o servidor está esperando as solicitações de serviço. A Classe Socket possui os métodos getinputstream() e

57 getoutputstream(), que são usados para obter Streams associados ao Socket. Deste modo, a transmissão de dados via Socket é idêntica à leitura e escrita em arquivos via Streams. O exemplo XI.1 mostra o código de um cliente que acessa um servidor de Daytime. O serviço de Daytime é disponibilizado nas plataformas UNIX e é acessado via porta 13. Sua função enviar, aos processos clientes, 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()); 56 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 Host servidor pela linha de comando. Você pode testar este programa mesmo que seu computador não esteja conectado em uma rede, desde que o protocolo TCP/IP esteja instalado. É que você pode passar como parâmetro o nome do seu computador ou, alternativamente, o nome localhost, ainda o número IP O nome localhost e o número IP sempre identificam o computador local. Note que após a criação do objeto Socket, o método getinputstream() é chamado e o objeto retornado é envolvido por um

58 objeto BufferedReader de modo a comunicar dados via rede da mesma forma que é realizada uma operação de E/S. Ao se terminar a operação é preciso fechar tanto a instância BufferedReader quanto o objeto Socket. A instância da Classe de E/S sempre deve ser fechada primeiro. Os Hosts que utilizam as variações do sistema operacional UNIX possuem o serviço de Daytime, no entanto, outros sistemas operacionais podem não implementar este serviço. O programador pode resolver este problema implentando ele mesmo um servidor de Daytime. A próxima seção mostrará como isto pode ser feito. 57 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 um novo serviço é preciso associar a uma porta com valor maior que Se estamos implementando um serviço já estabelecido é preciso obedecer as especificações definidas para o serviço. O exemplo XI.2 mostra o código de um servidor do serviço Daytime. Como não queremos substituir o serviço padrão de Daytime utilizaremos o número de porta 5013 no lugar do número 13. Para se testar este servidor com o programa cliente do exemplo X.1 é preciso alterar o número da porta no código do cliente.

59 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 pode indicar o tamanho da fila de solicitações de conexão. As conexões são colocadas na fila até que o servidor possa atende-las. Se chegar alguma conexão e não houver espaço na fila a conexão será recusada. No nosso exemplo passamos como parâmetro o valor 5. Após criar o objeto ServerSocket o servidor deve indicar que está disposto a receber conexões. Isto é feito por meio da execução do método accept() do objeto ServerSocket. Ao executar este método o processo passa para o estado bloqueado até que alguma conexão seja solicitada. Quando a conexão é solicitada o método accept() retorna um objeto Socket igual ao do processo cliente, que será usado para obter os Streams onde será efetuada a comunicação. O Stream obtido do objeto Socket é encapsulado em objeto BufferedWriter que será encarregado de enviar a cadeia de caracteres contendo a data e a hora para o cliente. O Servidor em implementa um laço infinito, recebendo e tratando solicitações de serviços.

60 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ços ao 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. Isto pode ser remediado por meio da implementação de servidores Multithreaded. Apesar do serviço implementado pelo exemplo XI.2 não exigir um servidor Multithreaded, uma vez que o cliente é atendido rapidamente, o servidor Daytime foi alterado para exemplificar a implementação de um servidor Multithreaded. O exemplo XI.3 mostra o código da versão Multithreaded do servidor. 59 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()

61 try BufferedWriter out = new BufferedWriter (new OuputStreamWriter ( socket.getouputstream())); out.write((new Date()).toString()+ \n ); out.flush(); out.close(); socket.close(); catch(exception e) System.err.println(e); 60 Exemplo XI.3 Servidor de Daytime Multithreaded. No exemplo XI.3, ao receber uma conexão o servidor cria um Thread para atender o cliente e fica disponível para receber novas solicitações. Esse exemplo pode ser usado como esqueleto para desenvolvimento de servidores mais complexos, porém, neste caso é necessário limitar o número de Threads que podem ser criados. Comunicação Sem Conexão (UDP) Como já dissemos na seção anterior, nem sempre é necessário um canal de comunicação confiável entre duas aplicações. Para estes casos existe o protocolo UDP, que provê uma forma de comunicação onde a aplicação envia pacotes de dados, chamados de datagramas, para outra aplicação, sem garantias se e quando a mensagem vai chegar, nem se o conteúdo está preservado. As portas do protocolo UDP obedecem a mesma distribuição das TCP porém são distintas uma da outra, de modo que o programador pode associar uma porta TCP de um determinado número à uma aplicação em um host e o mesmo número de porta UDP a outra aplicação no mesmo host. As classes DatagramPacket e DatagramSocket contém os métodos necessários para realizar este tipo de comunicação. Para ilustrar o uso destas classes modificaremos os exemplos XI.1 e XI.2 para implementarmos uma aplicação Cliente/Servidor Daytime que usa o protocolo UDP. O Exemplo XI.4 mostra o código fonte do cliente e o Exemplo XI.5 mostra o código do servidor.

62 61 Analisando o código da aplicação cliente podemos notar que é necessário criar um objeto da classe DatagramPacket para representar os Datagrama onde os dados são armazenados. No nosso exemplo colocamos também os dados do Host e a porta, porém estes dados poderiam ser omitidos na construção do Datagrama e serem passados somente no envio/recepção do pacote ou na construção do DatagramSocket. O Datagrama deverá armazenar os dados enviados pelo servidor e foi dimensionado para conter 64 bytes. O método receive() do objeto DatagramSocket aguarda o recebimento do pacote, tendo como argumento o objeto da classe DatagramPacket. Após o recebimento do 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 deve enviar os pacotes, uma vez que não é estabelecida uma conexão. Podíamos simplesmente passar o nome do host pela linha de comando, mas resolvemos adotar uma estratégia de Broadcasting. Nesta abordagem os datagramas são enviados a vários computadores e não apenas um. Isto é feito passando-se como argumento para o método InetAddress.getByName() o endereço de Broadcast 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 recebida de +dp.getaddress()+ " : "+s);

63 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( ); 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 IP da rede. O endereço usado no exemplo IX.5 funciona para a classe de endereçamento C. Para descobrir que tipo de classe pertence a rede onde está seu computador olhe o primeiro byte do endereço IP de sua máquina e verifique junto a tabela XI.3. Primeiro byte do endereço IP Classe Endereço de Broadcast 0 a 126 A a 191 B a 223 C Tabela XI.3 Classes para comunicação básica.

64 O envio dos dados é feito pelo método send() do objeto DatagramSocket, tendo como argumento um objeto da classe DatagramPacket. Note que não é necessário um Socket servidor uma vez que o servidor envia os 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) a um recurso na Internet. O URL é dividido em partes, sendo que apenas a primeira parte é obrigatória. A maioria das URLs é dividida em três partes: 63 Informação sobre o Host 123 Protocolo endereço do recurso no Host A parte do protocolo define o protocolo que deve ser usado para acessar o recurso. Os protocolos mais comuns são FTP, HTTP e file, este último indicando 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ária para acessar o Host onde está localizado o recurso. Esta parte é omitida caso o recurso 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 por uma barra ( / ), caso contrário. A informação sobre o Host também pode ser dividida em três partes: a) o nome do domínio do Host; b) o nome e senha do usuário para login; e c) o número da porta caso necessário, após o nome do host, precedida pelo caractere :. Exemplos:

65 A última parte de uma URL representa o caminho até o recurso no sistema de arquivos do Host. Esta seção é separada da seção anterior por uma barra simples ( / ). 64 Manipulando URLs em Java A linguagem Java fornece as seguintes classes para manipulação de URLs: Classe URL URLConnection URLEncoder Descrição Representa um URL Classe abstrata que representa uma conexão entre uma aplicação e um URL. Instâncias desta classe podem ser usadas para ler e escrever no recurso referenciado pela URL. 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 o URL na forma de String: URL dpi = new URL("http://www.dpi.ufv.br"); Um objeto URL pode ser construído passando como parâmetro outro URL para servir de endereço base. Por exemplo URL profs = new URL (dpi, professores.html ); Isto tem o mesmo efeito que gerar uma instância da classe URL com primeiro construtor, passando como parâmetro o endereço: Os construtores geram a exceção MalformedURLException, se o URL é inválido. Portanto, o programador deve providenciar o código para a captura e tratamento desta exceção.

66 De posse de um objeto URL é possível obter um objeto InputStream para ler os dados endereçados pelo URL, utilizando o método openstream() do objeto URL. O Exemplo XI.4 mostra o código de um programa que pode ser usado para listar na saída padrão o conteúdo de um URL passado pela linha de comando. 65 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 objeto URL para obter uma conexão entre a aplicação é o recurso referenciado pelo o URL. 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 CGI 2 (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

67 aplicação cliente escreva informação no URL. A saída do programa CGI pode ser interceptada pelo programa Java de modo que possa ser exibida para o usuário. Esta forma de comunicação com scripts CGI é melhor do que por meio de formulários HTML, uma vez que o usuário não precisa navegar entre páginas para visualizar os formulários e a resposta retornada. O Applet Java se encarrega de enviar os dados e exibir o resultado na mesma página. Para ilustrar esta forma de comunicação mostraremos um programa que submete uma cadeia de caracteres a um URL para que seja invertido e enviado de volta. O script CGI utilizado, escrito em Perl é exibido no exemplo XI.5 e foi escrito por Hassan Schroeder, um membro da equipe de desenvolvimento da linguagem Java. Este CGI pode ser acessado no URL 66 #!/opt/internet/bin/perl read(stdin, $buffer, = split(/&/, $buffer); foreach $pair ($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 caracteres ao URL e recebe de volta outra cadeia de caracteres que é a inversão da primeira. A ação necessária é codificar a cadeia de caracteres por meio do método estático encode() da classe URLEncoder: que gera a resposta na forma de uma página Web. O programa pode ser escrito em uma variadade delinguagens, como Perl e C.

68 String string = URLEncoder.encode(args[0]); 67 Isto é necessário porque a string enviada a um URL necessita de uma codificação particular, como por exemplo, os espaços em branco são substituídos pelo caractere +, os campos são separados pelo caractere & e valor do campo é separado do nome do campo pelo caracteres =. Em seguida o programa cria um objeto URL relacionado com o endereço onde se encontra o script, abre uma conexão e define que será usada para entrada e saída. URL url = new URL("http://java.sun.com/cgibin/backwards"); URLConnection c = url.openconnection(); c.setdooutput(true); Neste momento o programa está preparado para trabalhar com o URL como se fosse um Stream. O Stream para escrever no URL é obtido do objeto URLConnection por meio do método getoutputstream() e o Stream para ler do URL é obtido do objeto URLConnection por meio do método getinputstream(). Primeiro o programa submete a cadeia de caracteres a ser 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-

69 bin/backwards"); URLConnection c = url.openconnection(); c.setdooutput(true); 68 PrintWriter out = new PrintWriter(c.getOutputStream()); out.println("string=" + string); out.close(); BufferedReader in = new BufferedReader(new InputStreamReader( 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. A cadeia de caracteres invertida é lida e exibida no dispositivo de saída padrão. No exemplo apresentado o script CGI usa o POST METHOD para ler dados enviados pelo cliente. Alguns scripts CGI usam o GET METHOD para ler dados do cliente, no entanto, este último está ficando rapidamente obsoleto devido a maior versatilidade do primeiro.

70 69 Capítulo IV Computaç ão Distribuída (RMI) A RMI (Invocação de métodos remotos) é uma tecnologia que coloca a programação com rede em um nível mais alto. RMI torna possível que objetos distribuídos em uma rede se comuniquem de forma transparente para o programador utilizando chamadas de procedimentos remotos. O principal objetivo da tecnologia RMI é permitir que programadores desenvolvam programas distribuídos utilizando a mesma sintaxe e semântica de programas Java convencionais. Antes da introdução da RMI no mundo Java, no JDK 1.1, para fazer com que dois objetos em máquinas diferentes se comunicassem o programador deveria definir um protocolo de comunicação e escrever código utilizando socket para implementar este protocolo. Com RMI a maior parte do trabalho quem realiza é a máquina virtual Java. Existem outras tecnologias, como CORBA (Common Object Request Brocker Architecture), que também tem como objetivo fazer com que objetos distribuídos em uma rede se comuniquem. Java também tem suporte a CORBA, mas para projetos em um ambiente Java puro, a RMI é consideravelmente mais simples que a CORBA. Criando nossa agenda distribuída De modo exemplificar o uso de RMI modificaremos a agenda distribuída fazendo uso desta tecnologia. Os passos a serem seguidos são: 1. Escrever e compilar a interface que descreve a como serão as chamadas do 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; e 6. Testar. Implementar interface do objeto remoto

71 O primeiro passo quando se deseja criar um objeto Remoto com RMI é implementar uma interface para este Objeto. Essa interface deve herdar a interface Remote. É através dessa interface Remote, que não tem métodos, que a máquina virtual Java sabe qual objeto pode ser disponibilizado para acesso remoto. Abaixo temos o exemplo da interface do nosso objeto: 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.

72 Capítulo V - Acesso a B anco de Dados 71 No dias de hoje, uma linguagem sem recursos para acesso a sistemas de Banco de Dados está fadada ao fracasso. Pensando nisso a Sun incluiu como parte do núcleo de bibliotecas de classes da linguagem Java uma API com o objetivo de preencher esta função, chamada de JDBC (segundo a Sun JDBC é apenas um acronismo, no entanto, muitas pessoas acreditam que é uma sigla para Java Database Connectivity). JDBC é uma API baseada no X/Open SQL Call Level Interface, tendo sido desenvolvida originalmente como um pacote separado, porém a partir do JDK1.1 passou a fazer parte do núcleo básico de pacotes. Utilizando a API JDBC é possível conectar um programa Java com servidores de Banco de Dados e executar comandos SQL (Structure Query Language). Sendo uma API independente do Sistema Gerenciador de Banco de Dados, não é necessário escrever uma aplicação para acessar um Banco de Dados Oracle, outra para uma Base de Dados Sybase, outra para o DB2, e assim por diante. A idéia de se usar uma camada intermediária entre o Banco de Dados e a aplicação, com o objetivo de isolá-la das particularidades do SGBD, não é nova. O exemplo mais popular deste enfoque é a API ODBC (Open DataBase Connectivity), proposta pela Microsoft. O leitor pode estar se perguntando porque a Sun resolveu propor mais uma API em vez de adotar a ODBC. Existem vários motivos, porém o principal é que a API ODBC simplesmente não é adequada para a linguagem Java. Isto ocorre porque ODBC foi desenvolvida para 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 de modelo de duas camadas, onde a aplicação acessa diretamente o Banco de Dados. A figura XIII.1 mostra o esquema para uma aplicação que acessa um Banco de Dados usando o modelo de duas camadas.

73 72 Aplicação Java DBMS Figura XIII.1 Modelo de acesso a Banco de Dados em duas camadas. Aplicação Java ou Applet Aplicação Servidora Java JDBC HTTP, RMI, CORBA DBMS 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 ANSI SQL-2. No momento, os drivers JDBC existentes se encaixam em um dos quatro tipos abaixo: 1. Ponte JDBC-ODBC com driver ODBC o driver JDBC acessa o banco de dados via drivers ODBC. Como ODBC é um código binário e, em alguns casos, compõe o código do cliente, é necessário instalar em cada máquina cliente que usa o driver. Essa é uma solução adequada somente para uma

74 interna corporativa, ou em aplicações que adotam o modelo de três camadas sendo a camada intermediária um servidor Java. SGBD1 Cliente Ponte SGBD2 Java JDBC-ODBC SGBD3 2. Driver Java parcial e Api Nativa neste caso as chamadas JDBC são convertidas para as chamadas às APIs nativas do SGBD. Como o driver possui uma parte em código binário é necessário instalar algum código na máquina cliente, como é feito nos drivers do tipo Protocolo do SGBD Cliente JDBC Java & SGBD Java Código Binário 3. Driver puro Java e protocolo de rede neste caso as chamadas JDBC são convertidas para um protocolo de rede independente do SGBD que é depois traduzido para as chamadas às APIs nativas do SGBD por um servidor. Esta é uma arquitetura em três camadas, onde o servidor middleware é capaz de conectar seus clientes Java puros com vários SGBDs. Esta solução permite o desenvolvimento de clientes 100% Java, tendo como consequência a não necessidade de instalação de qualquer código na máquina cliente. JDBC DRIVER (100% Java) SGBD1 Cliente Servidor de SGBD2 Java acesso SGBD3 4. Driver Java Puro e protocolo nativo - neste caso as chamadas JDBC são convertidas para as chamadas às APIs nativas do SGBD pelo driver, que foi escrito totalmente em Java. Protocolo do SGBD Cliente JDBC Java SGBD Java (100% Java)

75 Atualmente existe uma maior disponibilidade dos drivers tipo 1 e 2, mas a tendência é que estes desapareçam, sendo substituídos pelos drivers do tipo 3 e 4. Obtendo os Drivers JDBC Informações sobre como obter drivers JDBC podem ser obtidas no site Outra alternativa é acessar as páginas dos fabricantes de SGBD, para verificar se existe driver disponível. 74 Preparando um Banco de Dados Os exemplos deste livro usam a ponte JDBC-ODBC para conectar com o Banco de Dados. Isto facilita para os usuários que possuem um gerenciador Banco de Dados pessoal como o Access, Paradox, e outros semelhantes. Além disso, como driver JDBC-ODBC está incorporado ao SDK, o usuário não necessita procurar um driver para testar os exemplos. O lado negativo desta abordagem está na necessidade de configurar o ODBC e no fato de que as aplicaçõ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 para isso 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 dos conceitos relacionados com banco de dados relacionais nem sobre a linguagem de consulta SQL. Existem vários textos sobre o assunto onde o leitor pode buscar informação. As figuras XIII.3 a XII.5 mostram as tabelas que formam a base de dados usada nos exemplos. O banco de dados é formado por três tabelas. Uma para armazenar os dados dos alunos, outra para receber os dados dos livros e uma terceira para conter os dados dos empréstimos. alunos matricula nome 1 Railer Costa Freire 2 Alexandre Altoé

76 3 André M. A. Landro 4 Ana Maria Freitas 5 Claudia Maria 6 Alexandra Moreira 75 Figura XIII.3 Tabela de alunos livros codlivro titulo volume 1 Curso Pratico de Java 1 2 Curso Pratico de Java 2 3 Introdução a Compiladores 1 4 Fundamentos de Banco de Dados 1 5 Redes de Computadores 1 6 Redes de Computadores Fácil 2 7 Lógica matemática 1 8 Engenharia de Software para Leigos 1 9 Aprenda Computação Gráfica em duas 1 10 Aprenda Inteligência Artificial em 5 1 Figura XIII.4 Tabela de livros. emprestimos codlivr matricu data_empresti data_devoluc /01/99 10/01/ /01/99 13/01/ /01/99 22/01/ /01/99 30/01/ /02/99 13/02/ /02/99 22/02/99 Figura XIII.5 Tabela de empréstimos. Em um Banco de Dados Relacional cada tabela representa um conjunto de entidades ou relacionamentos entre entidades, e cada linha da tabela representa uma entidade particular ou um relacionamento entre entidades. Assim, cada linha das tabelas alunos e livros representa um aluno e um livro respectivamente. Já na tabela empréstimos cada linha representa o relacionamento por empréstimo de um livro a um aluno. Para estabelecer este

77 tipo de relacionamento em um Banco de Dados Relacional é preciso colocar na tabela que representa o relacionamento os atributos chaves de cada entidade que participa da relação. Atributos chaves são os atributos que identificam cada entidade. No caso dos alunos é seu número de matrícula, uma vez que pode existir dois alunos com o mesmo nome. Já no caso de um livro o seu atributo chave é o código do livro. Um Banco de Dados Relacional pode ser representado pelo diagrama de classes da UML, como mostrado pela figura XIII.6, onde cada tabela é vista como uma classe. 76 Alunos matricula nome emprestimos data_emprestimo data_devolução codlivro titulo volume livros Figura XIII.6 Diagrama de Classes do Banco de Dados. Para criação das tabelas em banco de dados relacional deve-se usar comandos DDL (Data Definition Language). O exemplo XX.XX mostra os comandos 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 ),

78 77 CONSTRAINT FK_EMP2 FOREIGN KEY (MATRICULA) REFERENCES ALUNOS (MATRICULA )); Exemplo XX.XX Comandos em DDL para criação das tabelas em um SGBD relacional. Configurando o ODBC Como utilizamos nos exemplos a ponte JDBC-ODBC é necessário configurar o ODBC para acessar a base de dados acima. Na plataforma Windows 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).

79 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 a tecla Selecionar... localizar o arquivo onde está a base de dados e preencha a janela de texto Nome da fonte de dados com o nome que 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 ODBC possui vários outros detalhes que consideramos não ser relevantes para os propósitos deste livro. A configuração como apresentada acima é a suficiente para se executar os exemplos deste livro. Exemplo Inicial O pacote java.sql fornece as classes e interfaces necessárias para a conexão com uma base de dados e a posterior manipulação dos dados. As etapas para 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 que mostra o código de uma aplicação que lista no dispositivo de saída padrão o nome de todos os alunos. import java.sql.*; import java.net.url; class jdbc public static void main(string a[])

80 Java na Prática Volume II 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. 79 Carregando o Driver A primeira etapa é carregar o driver JDBC. Para isso é usado o método estático forname() da classe Class. Em caso de erro este método lança a exceção ClassNotFoundException. O método cria uma instância do driver e o registra junto ao DriverManager. No exemplo XIII.1 é carregado o driver JDBC-ODBC que vem junto com o SDK. Estabelecendo a conexão A segunda etapa é realizada por meio do método estático getconnection() da classe DriverManager. Este método, na sua forma mais simples, recebe como parâmetro um URL que faz referência a base de dados e retorna um objeto da classe Connection, que representa a conexão com a base de dados. Já discutimos sobre URLs no capítulo XI. No entanto, existem algumas particularidades no que refere a URLs que fazem referência à Banco de Dados. O formato padrão deste tipo de URL é o seguinte: onde: jdbc:<subprotocolo>:<identificador>

81 80 1. jdbc representa o protocolo; 2. <subprotocolo> se refere ao driver ou ao mecanismo de conexão com o 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. A forma de identificação varia de acordo com o subprotocolo. No nosso exemplo é colocado o mesmo nome usado para identificar a fonte de dados 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 passados para 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 a identificação do usuário, assim como uma senha. Estes dados podem ser passados 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 tabela XIII.1 jdbc:odbc:biblioteca URL jdbc:odbc:bd1;cachesize=20 jdbc:odbc:contas;uid=ana;pwd=sght Descrição Referencia fonte de dados biblioteca via ponte JDBC-ODBC. Referencia fonte de dados bd1 via ponte JDBC-ODBC. É definido o tamanho do cache. Referencia fonte de dados contas via ponte JDBC-ODBC. É passado também o nome do usuário e a senha. Referencia fonte de dados agenda no host remoto sap.dpi.ufv.br via subprotocolo oracle. É passado também o número da porta usada no acesso.

82 81 Tabela.1 Exemplos de URLs JDBC. Criando e Executando Comandos É necessário cria um ou mais objetos da classe Statement, que possui os métodos necessários para manipular a base de dados. Este objeto é criado por meio do método createstatement() do objeto da classe Connection. Statement stmt = con.createstatement(); Podemos então usar o objeto Statement para executar comandos de manipulação do Banco de Dados. No exemplo XIII.1 o objetivo é recuperar s nomes dos alunos. Este objetivo é atingido por meio da execução do comando SQL SELECT nome FROM alunos passado como parâmetro para o método executequery() do objeto Statement. Este método retorna um objeto que implementa a interface ResultSet, e que fornece os meios de acesso ao resultado da consulta. Muitas vezes, como no exemplo, o resultado de uma consulta é uma tabela com várias linhas. O programador pode utilizar o objeto ResultSet para acessar cada linha da tabela resultante em sequência. Para isso o objeto mantém um apontador para a linha corrente, chamado de cursor. Inicialmente o cursor é posicionado antes da primeira linha, movimentado para próxima linha por meio de chamadas ao método next() do objeto ResultSet. O método executequery() é usado apenas para consultas. Além desse método, a classe Statement possui o método execute() que retorna múltiplos ResultSets e o método executeupdate(), para atualização (comandos INSERT, DELETE e UPDATE da linguagem SQL), criação de tabelas (comandos CREATE TABLE) e remoção (DROP TABLE). O valor de retorno do método executeupdate() é um valor inteiro indicando o número de linhas afetadas ou zero no caso do DROP TABLE. Um exemplo de um comando para inserir um uma novo aluno na tabela seria: stmt.executeupdate("insert INTO alunos VALUES(7, 'Ana mia')");

83 82 Recuperando Valores O objeto ResultSet possui os métodos necessários para recuperar os valores de cada coluna da tabela, bastando passar o nome da coluna como parâmetro. No exemplo XIII.1 é utilizado método getstring() para recuperar o valor da coluna nome. Os métodos para recuperação possuem o formato geral getxxx(), onde XXX é o nome de um tipo. A tabela XIII.2 mostra qual o método mais indicado para cada tipo SQL. T I N Y I N T S M A L L I N T I N T E G E R B I G I N T R E A L F L O A T D O U B L E D E C I M A L N U M E R I C B I T C H A R V A R C H A R L O N G V A R C H A R B I N A R Y V A R B I N A R Y L O N G V A R B I N A R Y D A T E T I M E T I M E S T A M P getbyte X x x x x x x x x x x x x getshort x X x x x x x x x x x x x getint x x X x x x x x x x x x x getlong x x x X x x x x x x x x x getfloat x x x x X x x x x x x x x getdouble x x x x x X X x x x x x x getbigdecimal x x x x x x x X X x x x x getboolean x x x x x x x x x X x x x getstring x x x x x x x x x x X X x x x x x x x getbytes X X x getdate x x x X x gettime x x x X x gettimestamp x x x x X getasciistream x x X x x x getunicodestre am x x X x x x

84 getbinarystrea x x X m 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 de valores. É possível recuperar o valor da coluna passando como parâmetro o número da coluna no lugar de seu nome. Neste caso a recuperação no nome do aluno no exemplo XIII.1 ficaria na seguinte forma: rs.getstring(1); 83 Transações e Nível de Isolamento Transação Uma Transação é um conjunto de operações realizadas sobre um banco de dados tratadas atomicamente, em outras palavras, ou todas operações são realizadas e o seu resultado registrado permanentemente na base de dados ou nenhuma operação é realizada. Por default, o banco de dados trata cada operação como uma transação, realizando implicitamente uma operação de commit ao fim de cada uma delas. A operação de commit registra permanentemente o resultado da transação na tabela. No entanto, existem situações onde é necessário tratar como uma transaçã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 tabela com informações sobre a conta de corrente e outra com informações sobre contas de poupança. Suponha também que um cliente deseje transferir o dinheiro da conta corrente para uma conta de poupança. Essa transação é constituída pelas seguintes operações: 1. Caso exista saldo suficiente, subtração do montante da transferência do saldo da conta corrente. 2. Adição do montante da transferência ao saldo da conta de poupança.

85 As operações acima precisam ocorrer totalmente ou o efeito de nenhuma delas deve ser registrado na base de dados. Caso contrário podemos ter uma situação onde o dinheiro sai da conta corrente mas não entra na conta da poupança. Este estado, onde as informações do banco de dados não reflete a realidade, é chamado de estado inconsistente. De modo a obter esse controle sobre as transações é necessário desabilitar o modo de auto-commit. Isto é feito por meio método setautocommit() do objeto Connection. con.setautocommit(false); A partir do momento em que é executado o comando acima, o programador é responsável pela indicação do final da transação, por meio da execução do método commit() do objeto Connection. con.commit(); Se alguma exceção for levantada durante a execução de qualquer operação da transação, o programador pode usar o método rollback() para desfazer as operações já realizadas após o último commit(). 84 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().

86 Níveis de isolamento Java na Prática Volume II 85 Além da atomicidade outra propriedade desejável em uma transação é o isolamento. A propriedade de isolamento implica que uma transação não é afetada pelas operações realizadas por outras transações que estão sendo realizadas concorrentemente. O isolamento completo entre transações prejudica muito a execução concorrente de transações e pode ser desnecessário em determinados tipos de aplicações. Por isso os SGBDs permitem que o programador defina o nível de isolamento entre as transações. De acordo com o relaxamento do isolamento certos problemas devido a interferência entre as transações podem ocorrer e o programador deve estar ciente disso. O número de níveis de isolamento, sua nomenclatura e características depende do SGBD utilizado. Descreveremos os níveis de isolamento definidos no pacote java.sql. Para exemplificar os problemas que podem ocorrer devido a interferência entre transações utilizaremos um banco de dados exemplo com a seguinte tabela: NumCC Saldo 200,00 300,00 Read uncommitted - É o nível menos restritivo. Pode ocorrer leituras de registros não committed (Dirty reads). Usados em onde não existe concorrência ou não existem alterações em registros ou quando essas alterações não são relevantes. Exemplo de problema: Uma transação deve transferir R$50,00 da conta para a conta e uma segunda transação deve somar R$70,00 à conta A figura abaixo mostra o estado inicial e o estado final desejado da tabela: Estado antes das transações Estado desejado após as transações NumCC Saldo NumCC Saldo ,00 300, ,00 350,00

87 Cada transação é divida em operações de leitura e escrita. Suponha que o intercalamento das operações seja feito como mostrado abaixo: Transação 1 Transação 2 leitura do saldo Escrita do Saldo-50,00 leitura do saldo (falha na transação, realizado rollback) leitura do saldo Escrita do Saldo+70,00 Como a transação 1 falhou o valor lido pela transação 2 é um valor que não foi tornado permanente na tabela. Isto faz com que a transação 2 opere sobre um resultado desfeito. A tabela resultante, mostrada abaixo estará em um estado inconsistente. 86 NumCC Saldo 220,00 300,00 Read committed - Somente registros committed podem ser lidos. Evita o problema de Dirty reads, no entanto duas leituras de um mesmo item em uma mesma transação podem possuir valores diferentes, uma vez que o valor pode ser mudado por outra transação entre duas leituras. Repeatable Read - Somente registros committed podem ser lidos, além disso impede a alteração de um item lido pela transação. Evita o problema de Dirty reads e o problema do non-repeatable Read. Serializable - É o nível mais restritivo. Impede Dirty reads e non-repeatable reads. Além disso impede o problema de phantom reads onde um conjunto de registros satisfazendo a condição WHERE é lido enquanto outra transação insere novos registros que satisfazem a condição. Para se definir o nível de isolamento na linguagem Java usa-se um objeto DatabaseMetaData que é obtido por meio do objeto getmetadata() do Connection. Primeiro é preciso saber se o SGBD suporta o nível de

88 isolamento desejado para depois definir o nível. O exemplo XX.X mostra uma sequê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. 87 A tabela abaixo mostra as constantes relacionadas com os níveis de isolamento da linguagem Java: Constante TRANSACTION_NONE TRANSACTION_READ_UNCOMMITTED TRANSACTION_READ_COMMITTED TRANSACTION_REPEATABLE_READ TRANSACTION_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 uma String. Este String deve ser analisado pelo processador de SQL do SGBD que irá, no caso da String estar sintaticamente correta, gerar um código binário que será executado para atender à solicitação. Todo esse processo é caro e sua execução repetidas vezes terá um impacto significativo sobre o desempenho da aplicaçã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 o comando uma única vez e toda vez que for necessário executá-lo basta enviar o

89 comando compilado. Além disso, o comando pré-compilado pode ser parametrizado, tornando-o mais genérico e, portanto, apto a expressar um maior número de consultas. Para criar um Prepared Statement é necessário obter um objeto PreparedStatement por meio do método preparestatement() do objeto Connection, passando como argumento um comando SQL. 88 PreparedStatement pstmt = con.preparestatement( INSERT INTO alunos(matricula,nome) VALUES(?,? ) ); O comando anterior insere uma nova linha na tabela alunos com os valores 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 comando devemos especificar o valor dos parâmetros e executar o comando, como mostrado 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 outro parâmetro previamente especificado. Para especificar os parâmetros são utilizados um conjunto de métodos com o nome no formato setxxx(), onde XXX é 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 segundo parâmetro é o valor que será transmitido. Procedimentos Armazenados (Stored Procedures) A maioria dos SGBDs possuem algum tipo de linguagem de programação interna, como por exemplo a PL/SQL do Oracle ou mesmo Java e C/C++. Estas linguagens permitem que os desenvolvedores insiram parte do código da aplicação diretamente no banco de dados e invoquem este código a partir da aplicação. Esta abordagem possui as seguintes vantagens:

90 89 Reuso de código o código precisa ser escrito apenas uma vez e usado em várias aplicações, comunicando com várias linguagens. Independencia entre a aplicação e o esquema do BD se o esquema mudar, 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 de procedimentos armazenados, evitando assim acessos não autorizados. A sintaxe dos procedimentos armazenados depende do SGBD em questão. Utilizaremos um exemplo em PL/SQL. No exemplo abaixo o procedimento retorna o nome do aluno a partir de sua matricula. CREATE OR REPLACE PROCEDURE sp_obtem_nome (id IN INTEGER, Nome_aluno out VARCHAR2)IS BEGIN 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étodo preparecall() do objeto Connection, passando como argumento um comando 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);

91 90 Agenda Eletrônica versão JDBC Pessoa import java.io.*; public class pessoa implements Serializable String Nome; String Tel; // Construtor public pessoa(string n, String t) Nome = n; Tel = t; public String getnome()return Nome; public String gettel()return Tel; agenda import 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) trycon.close();catch(exception e); /** inserir

92 */ public void inserir(pessoa p) if (con == null) return; Java na Prática Volume II 91 "+ 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(new pessoa(rs.getstring("nome"), rs.getstring("telefone"))); stmt.close(); catch(exception e) System.out.println(e.getMessage()); e.printstacktrace(); return pessoas.elements(); Servidor import java.util.*; import java.net.*; import java.io.*;

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

94 BufferedReader agenda Java na Prática Volume II cinput; ag; 93 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);

95 Applet import java.awt.*; import java.applet.*; import java.util.*; import java.net.*; import java.io.*; Java na Prática Volume II 94 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);

96 btcons.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); 95 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);

97 Java na Prática Volume II soutput.close(); clisoc.close(); catch(exception e) System.err.println(e); 96 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("");

98 97 Capítulo VI Servlets e JSP Servlets e JSP são duas tecnologias desenvolvidas pela Sun para desenvolvimento de aplicações na Web a partir de componentes Java que executem no lado servidor. Essas duas tecnologias fazem parte da plataforma J2EE (Java 2 Platform Enterprise Edition) que fornece um conjunto de tecnologias para o desenvolvimento de soluções escaláveis e robustas para a Web. Neste livro abordaremos apenas as tecnologias Servlets e JSP, sendo o suficiente para o desenvolvimento de sites dinâmicos de razoável complexidade. Se a aplicação exigir uma grande robustez e escalabilidade o leitor deve considerar o uso em conjunto de outras tecnologias da plataforma J2EE. Servlets Servlets são classes Java que são instanciadas e executadas em associação com servidores Web, atendendo requisições realizadas por meio do protocolo HTTP. Ao serem acionados, os objetos Servlets podem enviar a resposta na forma de uma página HTML ou qualquer outro conteúdo MIME. Na verdade 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 do ambiente do servidor, sendo independentes de protocolos e plataformas. Em outras palavras Servlets é uma API para construção de componentes do lado servidor com o objetivo de fornecer um padrão para comunicação entre clientes e servidores. Os Servlets são tipicamente usados no desenvolvimento de sites dinâmicos. Sites dinâmicos são sites onde algumas de suas páginas são construí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 executadas dentro de um ambiente Java denominado de Container. O container gerencia as instâncias dos Servlets e provê os serviços de rede necessários para as requisições e respostas. O container atua em associação com servidores Web recebendo as requisições reencaminhada por eles. Tipicamente existe apenas uma instância de cada Servlet, no entanto, o container pode criar vários threads de modo a permitir que uma única instância Servlet atenda mais de uma

99 requisição simultaneamente A figura XX fornece uma visão do relacionamento destes componentes. 98 Máquina Virtual Java Servidor Web Requisições Respostas Container Instâncias de Servlets Figura VI-1. Relacionamento entre Servlets, container e servidor Web Servlets provêem uma solução interessante para o relacionamento cliente/servidor na Internet, tornando-se uma alternativa para a implantação de sistemas 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ção de aplicações na Internet. Applets X Servlets Apesar de ser uma solução robusta existem problemas no uso de Applets para validação de dados e envio para o servidor. O programador precisa contar com o fato do usuário possuir um navegador com suporte a Java e na versão apropriada. 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 se tratando 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ântica da aplicação possa ser expressa por componentes HTML.

100 CGI X Servlets Java na Prática Volume II 99 Como visto no Erro! A origem da referência não foi encontrada., scripts CGI (Common Gateway Interface), acionam programas no servidor. O uso de CGI sobrecarrega o servidor uma vez cada requisição de serviço acarreta a execução de um programa executável (que pode ser escrito em com qualquer linguagem que suporte o padrão CGI) no servidor, além disso, todo o processamento é realizado pelo CGI no servidor. Se houver algum erro na entrada de dados o CGI tem que produzir uma página HTML explicando o problema. Já os Servlets são carregados apenas uma vez e como são executados de forma multi-thread podem atender mais de uma mesma solicitação por simultaneamente. Versões posteriores de CGI contornam este tipo de problema, mas permanecem outros como a falta de portabilidade e a insegurança na execuçã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. O componente mais básico da API é interface Servlet. Ela define o comportamento básico de um Servlet. A figura XX.XX mostra a interface Servlet. 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 das requisições dos clientes. Já os métodos init() e destroy() são chamados quando o Servlet é carregado e descarregado do container, respectivamente. O método getservletconfig() retorna um objeto ServletConfig que

101 contém os parâmetros de inicialização do Servlet. O método getservletinfo() retorna um String contendo informações sobre o Servlet, como versão e autor. Tendo como base a interface Servlet o restante da API Servlet se organiza hierarquicamente como mostra a figura XV.XX. 100 Servlet GenericServlet HttpServlet Figura XV.XX. Hierarquia de classes da API Servlet. A classe GenericServlet implementa um servidor genérico e geralmente não é usada. A classe HttpServlet é a mais utilizada e foi especialmente projetada para lidar com o protocolo HTTP. A figura XX.XX mostra a definição da classe interface HttpServlet. HttpServlet public abstract class HttpServlet extends GenericServlet implements java.io.serializable Figura XV.XX. Definição da classe HttpServlet. Note que a classe HttpServlet é uma classe abstrata. Para criar um Servlet que atenda requisições HTTP o programador deve criar uma classe derivada da HttpServlet e sobrescrever pelo menos um dos métodos abaixo:

102 101 doget dopost doput dodelete Trata as requisições HTTP GET. Trata as requisições HTTP POST. Trata as requisições HTTP PUT. Trata as requisições HTTP DELETE. Tabela XV.XX. Métodos da classe HttpServlet que devem ser sobrescritos para tratar requisições HTTP. Todos esses métodos são invocados pelo servidor por meio do método service(). O método doget() trata as requisições GET. Este tipo de requisição pode ser enviada várias vezes, permitindo que seja colocada em um bookmark. O método dopost() trata as requisições POST que permitem que o cliente 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. O método doput() trata as requisições PUT. Este tipo de requisição permite que o cliente envie um arquivo para o servidor à semelhança de como é feito via FTP. O método doput() trata as requisições DELETE, permitindo que o cliente remova um documento ou uma página do servidor. O método service(), que recebe todas as requisições, em geral não é sobrescrito, sendo sua tarefa direcionar a requisição para o método adequado. Exemplo de Servlet Para entendermos o que é um Servlet nada melhor que um exemplo simples. O exemplo XV.XX gera uma página HTML em resposta a uma requisição GET. A página HTML gerada contém simplesmente a frase Ola mundo!!!. Este é um Servlet bem simples que ilustra as funcionalidades básicas da 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

103 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 classe HttpServletRequest e outro da classe HttpServletResponse. O HttpServletRequest é responsável pela comunicação do cliente para o servidor e o HttpServletResponse é responsável pela comunicação do servidor para o cliente. Sendo o exemplo XV.XX apenas um exemplo simples ele ignora o que foi enviado pelo cliente, tratando apenas de enviar uma página HTML como resposta. Para isso é utilizado o objeto da classe HttpServletResponse. Primeiramente é usado o método setcontenttype() 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 do tipo PrintWriter ou ServletOutputStream para a resposta. Após isso é usado o método getwriter() para se obter um objeto do tipo PrintWriter que é usado para escrever a resposta. Neste caso os dados da resposta são baseados em caracteres. Se o programador desejar enviar a resposta em bytes deve usar o método getoutputstream() para obter um objeto OutputStream. A partir de então o programa passa usar o objeto PrintWriter para enviar a página HTML. Compilando o Servlet A API Servlet ainda não foi incorporado ao SDK, portanto, para compilar um Servlet é preciso adicionar a API Servlet ao pacote SDK. Existem várias formas de se fazer isso. A Sun fornece a especificação da API e diversos

104 produtores de software executam a implementação. Atualmente, a especificação da API Servlet está na versão 2.XX. Uma das implementações da API que pode ser baixada gratuitamente pela Internet é a fornecida pelo projeto Jakarta (http://jakarta.apache.org) denominada de Tomcat. A implementação da API Servlet feita pelo projeto Jakarta é a implementação de referência indicada pela Sun. Ou seja, é a implementação que os outros fabricantes devem seguir para garantir a conformidade com a especificação da API. No entanto, uma vez que o Tomcat é a implementação mais atualizada da API, é também a menos testada e, por consequência, pode não ser a mais estável e com melhor desempenho. 103 Instalando o Tomcat Assim como para se executar um Applet era preciso de um navegador Web com Java habilitado no caso de Servlets é preciso de servidor Web que execute Java ou que passe as requisições feitas a Servlets para programas que executem os Servlets. O Tomcat é tanto a implementação da API Servlet como a implementação de um container, que pode trabalhar em associação com um servidor Web como o Apache ou o IIS, ou pode também trabalhar isoladamente, desempenhando também o papel de um servidor Web. Nos exemplos aqui mostrados usaremos o Tomcat isoladamente. Em um ambiente de produção esta configuração não é a mais adequada, uma vez que os servidores Web possuem um melhor desempenho no despacho de páginas estáticas. As instruções para configurar o Tomcat para trabalhar em conjunto com um servidor Web podem ser encontradas junto às instruções gerais do programa. As figuras XV.XX ilustram essas duas situações. Internet Servidor Web Servlet habilitado Figura XV.XX. Servidor Web habilitado para Servlet.

105 104 Internet Servidor Web Servlet Container Figura XV.XX. Servidor Web reencaminhando as requisições para o Servlet container. A versão estável do Tomcat é a XX é após baixá-la do site do projeto Jakarta o usuário deve descomprimir o arquivo. Por exemplo, no ambiente 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 bin conf doc lib logs src webapps No diretório bin encontram-se os programas execução e interrupção do container 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 lib encontram-se os bytecodes do container e da implementação da API. No diretório logs são registradas as mensagens da geradas durante a execução do sistema. No diretório src encontram-se os arquivos fontes do container e da implementação da API de configuração. Finalmente, No diretório webapps encontram-se as páginas e códigos das aplicações dos usuários. No ambiente MS-Windows aconselhamos usar um nome dentro do formato 8.3 (oito caracteres para o nome e três para o tipo). Assim o diretório jakarta-tomcat poderia ser mudado para simplesmente tomcat.

106 Antes de executar o Tomcat é necessário definir duas variáveis de ambiente. Por exemplo, supondo que no MS-Windows o Tomcat foi instalado no diretório c:\tomcat e que o SDK está instalado no diretório c:\jdk1.3 então as seguintes variáveis de ambiente devem ser definidas: set JAVA_HOME=C:\jdk1.3 set 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 105 Falta de espaço para variáveis de ambiente Caso 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 o ambiente inicial com Feche o arquivo é execute novamente. Ao entrar em execução o servidor lê as configurações constantes no arquivo server.xml e, por default, se anexa à porta Para verificar se o programa está funcionando corretamente execute um navegador como o Netscape ou o Internet Explorer e digite a seguinte URL: A figura XX.XX mostra a tela principal do Tomcat.

107 106 Figura XV.XX. Tela inicial do Tomcat. A número porta default para recebimento das requisições HTTP pode ser alterada por meio da edição do arquivo server.xml do diretório conf como mostrado 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 um servidor, o ideal é que o Tomcat não responda requisições diretamente. Preparando para executar o Servlet Compilando o Servlet

108 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-Windows seria 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 de execução do compilador Java. Por exemplo, No ambiente MS-Windows ficaria na 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 do Tomcat: webapps Nome aplicação Web-inf classes 107 Diretório de Aplicações Na 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 da diretiva 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 as linhas:

109 <Context path="/nome aplicação" docbase="webapps/ nome aplicação" debug="0 reloadable="true" > </Context> 108 Finalmente, é preciso criar (ou copiar de outra aplicação) um arquivo web.xml no diretório Web-inf com o seguinte conteúdo: <?xml version="1.0" encoding="iso "?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.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 no navegador. A URL em geral possui o seguinte formato: aplicação/servlet/nome servlet A palavra servlet que aparece na URL não indica um subdiretório no servidor. 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 URL para a invocação do Servlet do exemplo XX.XX teria a seguinte forma: A URL para a chamada do Servlet pode ser alterada de modo a ocultar qualquer referência à diretórios ou a tecnologias de implementação. No caso do Tomcat essa configuração é no arquivo web.xml do diretório Web-inf da aplicação. Por exemplo, para eliminar a palavra servlet da URL poderíamos

110 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> 109 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 se enviar uma requisições a um servidor Web são os métodos POST e GET. Apesar de aparentemente cumprirem a mesma função, existem diferenças importantes entre estes dois métodos. O método GET tem por objetivo enviar uma requisição por um recurso. As informações necessárias para a obtenção do recurso (como informações digitadas em formulários HTML) são adicionadas à URL e, por consequência, não são permitidos caracteres inválidos na formação de URLs,

111 como por espaços em branco e caracteres especiais. Já na requisição POST os dados são enviados no corpo da mensagem. O método GET possui a vantagem de ser idempotente, ou seja, os servidores Web podem assumir que a requisição pode ser repetida, sendo possível adicionar à URL ao bookmark. Isto é muito útil quando o usuário deseja manter a URL resultante de uma pesquisa. Como desvantagem as informações passadas via GET não podem ser muito longas, um vez o número de caracteres permitidos é por volta de 2K. Já as requisições POST a princípio podem ter tamanho ilimitado. No entanto, elas não são idempotente, o que as tornam ideais para formulários onde os usuários precisam digitar informações confidenciais, como número de cartão de crédito. Desta forma o usuário é obrigado a digitar a informação toda vez que for enviar a requisição, não sendo possível registrar a requisição em um bookmark. 110 Concorrência Uma vez carregado o Servlet não é mais descarregado, a não ser que o servidor Web tenha sua execução interrompida. De modo geral, cada requisição que deve ser direcionada a determinada instância de Servlet é tratada por um thread sobre a instância de Servlet. Isto significa que se existirem duas requisições simultâneas que devem ser direcionadas para um mesmo objeto o container criará dois threads sobre o mesmo objeto Servlet para tratar as requisições. A figura XX.XX ilustra esta situação. usuário 1 usuário 2 usuário 3 thread1 thread2 thread1 Servlet1 Servlet2 Figura XV.XX. Relacionamento entre as instâncias dos Servlets e os threads.

112 Em conseqüência disto temos o benefícios de uma sobrecarga para servidor, uma vez que a criação de threads é menos onerosa do que a criação de processos, e uma aparente melhora no tempo de resposta. Por outro lado, o fato dos Servlets operarem em modo multi-thread aumenta a complexidade das aplicações e cuidados especiais, como visto no capítulo sobre concorrência, devem tomados para evitar comportamentos erráticos. Por exemplo, suponha um Servlet que receba um conjunto de números inteiros e retorne uma página contendo a soma dos números. A exemplo XX.XX mostra o código do Servlet. O leitor pode imaginar um código muito mais eficiente para computar a soma de números, mas o objetivo do código do exemplo é ilustrar o problema da concorrência em Servlets. O exemplo contém também um trecho de código para recebimento de valores de formulários, o que será 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>"); 111

113 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 o Vector que armazena os valores. Se não forem usadas primitivas de sincronização (como no código do exemplo) e duas requisições simultâneas chegarem ao Servlet o resultado pode ser inconsistente, uma vez que o Vector poderá conter parte dos valores de uma requisição e parte dos valores de outra requisição. Neste caso, para corrigir esse problema basta declarar a variável como 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ém várias informações importantes relacionadas com a requisição, como por exemplo o método empregado (POST ou GET), o protocolo utilizado, o endereço remoto, informações contidas no cabeçalho e muitas outras. O Servlet do exemplo XX.XX retorna uma página contendo informações sobre a requisiçã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> );

114 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 modo que o Servlet pode receber os dois tipos de requisição. A figura XX.XX mostra o resultado de uma execução do Servlet do exemplo XX.XX. Exemplo sobre Requisicao de Info Metodo: GET Request URI: /servlet/requestinfo Protocolo: HTTP/1.0 PathInfo: null Endereco remoto: Connection = Keep-Alive User-Agent = Mozilla/4.7 [en] (Win95; I) Pragma = no-cache Host = localhost:8080 Accept = image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */* Accept-Encoding = gzip Accept-Language = en Accept-Charset = iso ,*,utf-8 Figura XX.XX- Saída da execução do Servlet que exibe as informações sobre a requisição.

115 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 para Web. É por meio de formulários que os usuários fornecem dados, preenchem pedidos de compra e (ainda mais importante) digitam o número do cartão de crédito. As informações digitadas no formulário chegam até o Servlet por meio do objeto HttpServletRequest e são recuperadas por meio do método getparameter() deste objeto. Todo item de formulário HTML possui um nome e esse nome é passado como argumento para o método getparameter() que retorna na forma de String o valor do item de formulário. O Servlet do exemplo XX.XX exibe o valor de dois itens de formulários do tipo text. Um denominado nome e o outro denominado de sobrenome. Em seguida o Servlet cria um formulário contendo os mesmos itens de formulário. Note que um formulário é criado por meio do tag <form>. Como parâmetros opcionais deste tag temos método da requisição (method), é a URL para onde será submetida a requisição (action). No caso do exemplo, o método adotado é o POST e a requisição será submetida ao próprio Servlet Form. 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");

116 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 do servidor para o navegador no cabeçalho página. A partir de então, dependendo do tempo de validade do cookie, o navegador reenvia essa informação para o servidor a cada nova requisição. Dependo do caso o cookie é também armazenado no disco da máquina cliente e quando o site é novamente visitado o cookie enviado novamente para o servidor, fornecendo a informação desejada. Os cookies foram a solução adotada pelos desenvolvedores do Netscape para implementar a identificação de clientes sobre um protocolo HTTP que não é orientado à conexão. Esta solução, apesar das controvérsias sobre a possibilidade de quebra de privacidade, passou ser amplamente adotada e hoje os cookies são parte integrante do padrão Internet, normalizados pela norma RFC A necessidade da identificação do cliente de onde partiu a requisição e o monitoramento de sua interação com o site (denominada de sessão) é importante para o desenvolvimento de sistemas para a Web pelas seguintes razões:

117 116 É necessário associar os itens selecionados para compra com o usuário que deseja adquiri-los. Na maioria da vezes a seleção dos itens e compra é feita por meio da navegação de várias páginas do site e a todo instante é necessário distinguir os usuários que estão realizando as requisições. É necessário acompanhar as interação do usuário com o site para observar seu comportamento e, a partir dessas informações, realizar adaptações no site para atrair um maior número de usuários ou realizar campanhas de marketing. É necessário saber que usuário está acessando o site para, de acordo com o seu perfil, fornecer uma visualização e um conjunto de funcionalidades adequadas às suas preferências. Todas essas necessidades não podem ser atendidas com o uso básico do protocolo HTTP, uma vez que ele não é orientado à sessão ou conexão. Com os cookies é possível contornar essa deficiência, uma vez que as informações que são neles armazenadas podem ser usadas para identificar os clientes. Existem outras formas de contornar a deficiência do protocolo de HTTP, como a codificação de URL e o uso de campos escondidos nas páginas HTML, mas o uso de cookies é a técnica mais utiliza, por ser mais simples e padronizada. No entanto, o usuário pode impedir que o navegador aceite cookies, o que torna o ato de navegar pela Web muito desagradável. Neste caso, é necessário utilizar as outras técnicas para controle de sessão. A API Servlet permite a manipulação explicita de cookies. Para controle de sessão o programador pode manipular diretamente os cookies, ou usar uma abstraçã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 a codificação de URL. O exemplo XX.XX mostra o uso de cookies para armazenar 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, HttpServletResponse res) throws IOException, ServletException

118 res.setcontenttype("text/html"); 117 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);

119 118 Exemplo XX.XX- Servlet para lidar com Cookies. Para se criar um cookie é necessário criar um objeto Cookie, passando para o construtor um nome e um valor, sendo ambos instâncias de String. O cookie é enviado para o navegador por meio do método addcookie() do objeto HttpServletResponse. Um vez que os cookies são enviados no cabeçalho da página, o método addcookie() deve ser chamado antes do envio de qualquer conteúdo para o navegador. Para recuperar os cookies enviados pelo navegador usa-se o método getcookies() do objeto HttpServletRequest que retorna um array de Cookie. Os métodos getname() e getvalue() do objeto Cookie são utilizados para recuperar o nome o valor da informação associada ao cookie. Os objetos da classe Cookie possuem vários métodos para controle do uso de cookies. É possível definir tempo de vida máximo do cookie, os domínios que 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 deve ser enviado somente sob um protocolo seguro e etc. Por exemplo, para definir a idade máxima de um cookie devemos utilizar o método setmaxage(), passando um inteiro como parâmetro. Se o inteiro for positivo indicará em segundos o tempo máximo de vida do cookie. Um valor negativo indica que o cookie deve apagado quando o navegador terminar. O valor zero indica que o cookie deve ser apagado imediatamente. O trecho de código exemplo XX.XX mostra 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.br cookie.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 baixo nível, uma vez que o usuário deve se preocupar com a identificação, tempo de vida e outros detalhes. Por isso a API Servlet fornece um objeto com controles

120 de nível mais alto para monitorar a sessão, o HttpSession. O objeto HttpSession monitora a sessão utilizando cookies de forma transparente. No entanto, se o cliente não aceitar o uso de cookies é possível utilizar como alternativa a codificação de URL para adicionar o identificador da sessão. Essa opção, apesar de ser mais genérica, não á primeira opção devido a possibilidade de criação de gargalos pela necessidade da análise prévia de todas requisições que chegam ao servidor. O exemplo XX.XX mostra o uso de um objeto HttpSession para armazenar as informações digitadas em um formulário. 119 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();

121 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 por meio do método getsession() do objeto HttpServletRequest. Opcionalmente, o mé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 é para retorna null caso ele não exista (argumento false). Para se associar um objeto ou informaçã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 ser associado à sessão. Os objetos associados a uma sessão são recuperados com o uso método getattribute() do objeto HttpSession, que recebe como argumento o nome 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.

122 Teste de Sessao Java na Prática Volume II 121 Identificador: session3 Data: Sun May 28 15:19:15 GMT-03: Ultimo acesso: Sun May 28 15:19:43 GMT-03: Dados da Sessao: Alcione = 4 Alexandra = 6 Nome Valor Enviar Consulta Figura VI-1 Saída resultante da execução do Servlet que lida com Sessões.

123 122 JSP Servlets é uma boa idéia, mas você se imaginou montando uma página complexa usando println()? Muitas vezes o desenvolvimento de um site é uma tarefa complexa que envolve vários profissionais. A tarefa de projeto do layout da página fica a cargo do Web Designer, incluindo a diagramação dos textos e imagens, aplicação de cores, tratamento das imagens, definição da estrutura da informação apresentada no site e dos links para navegação pela mesma. Já o Desenvolvedor Web é responsável pela criação das aplicações que vão executar em um site. O trabalho destes dois profissionais é somado na criação de um único produto, mas durante o desenvolvimento a interferência mutua deve ser a mínima possível. Ou seja, um profissional não deve precisar alterar o que é foi feito pelo outro profissional para cumprir sua tarefa. A tecnologia Servlet não nos permite atingir esse ideal. Por exemplo, suponha que um Web Designer terminou o desenvolvimento de uma página e a entregou para o Desenvolvedor Web codificar em um Servlet. Se após a codificação o Web Designer desejar realizar uma alteração na página será necessário que ele altere o código do Servlet (do qual ele nada entende) ou entregar uma nova página para o Desenvolvedor Web para que ele a codifique totalmente mais uma vez. Qualquer uma dessas alternativas são indesejáveis e foi devido a esse problema a Sun desenvolveu uma tecnologia baseada em Servlets chamada de JSP. Java Server Pages (JSP) são páginas HTML que incluem código Java e outros tags especiais. Desta forma as partes estáticas da página não precisam ser geradas 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 projetada por um Web Designer que nada sabe de Java. A primeira vez que uma página JSP é carregada pelo container JSP o código Java é compilado gerando um Servlet que é executado, gerando uma página HTML que é enviada para o navegador. As chamadas subsequentes são enviadas diretamente ao Servlet gerado na primeira requisição, não ocorrendo mais as etapas de geração e compilação do Servlet. A figura XX.XX mostra um esquema das etapas de execução de uma página JSP na primeira vez que é requisitada. Na etapa (1) a requisição é enviada para um servidor Web que reencaminha a requisição (etapa 2) para o container Servlet/JSP. Na etapa (3) o container verifica que não existe nenhuma instância de Servlet correspondente à página JSP. Neste caso, a página JSP é traduzida para 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

124 instância da classe. Finalmente, na etapa (6) é invocado o método service() da instância Servlet para gerar a resposta à requisição. 123 Navegador (6) Resposta à requisição (1) Requisição de página JSP Servidor Http Container Servlet/JSP (2) Encaminha a requisição (3) Traduz Página jsp (5) Instancia e executa Bytecode Servlet (4) Compila Fonte Servlet 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áginas HTML 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 por outros fabricantes. Abaixo segue uma comparação de duas das tecnologias mais populares com JSP. PHP X JSP PHP (Personal Home Pages) é uma linguagem script para ser executada no 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 mas possui elementos de C++, Java e Perl. Possui suporte à programação OO por meio de classes e objetos. Possui também suporte extensivo à Banco de dados ODBC, MySql, Sybase, Oracle e outros. PHP é uma linguagem mais fácil no desenvolvimento de pequenas aplicações para Web em relação à JSP, uma vez

125 que é uma linguagem mais simples e menos rígida do que JSP. No entanto, a medida 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 mais rígidas e com maior suporte à escalabilidade, como é o caso de Java. 124 ASP X JSP ASP (Active Server Pages) é a solução desenvolvida pela Microsoft para atender as requisições feitas à servidores Web. Incorporada inicialmente apenas ao Internet Information Server (IIS), no entanto, atualmente já é suportada por outros servidores populares, como o Apache. O desenvolvimento de páginas que usam ASP envolve a produção de um script contendo HTML misturado com blocos de código de controle ASP. Este código de controle pode conter scripts em JavaScript ou VBScript. A primeira vantagem de JSP sobre ASP é que a parte dinâmica é escrita em Java e não Visual Basic ou outra linguagem proprietária da Microsoft, portanto JSP é mais poderoso e fácil de usar. Em segundo lugar JSP é mais portável para outros sistemas operacionais e servidores WEB que não sejam Microsoft. Primeiro exemplo em JSP Para que o leitor possa ter uma idéia geral da tecnologia JSP apresentaremos agora a versão JSP do Olá mundo. O exemplo XX.XX mostra o código da página. <html> <head> <title>exemplo JSP</title> </head> <body> <% String x = "Olá Mundo!"; %> <%=x%> </body> </html> Exemplo XX.XX- Versão JSP do Olá mundo.

126 Quem está habituado aos tags HTML notará que se trata basicamente de uma página HTML contendo código Java delimitado pelos símbolos <% e %>. Para facilitar a visualização destacamos os scripts Java com negrito. No primeiro trecho de script é declarada uma variável x com o valor Olá mundo (a seqüência é denota á em HTML). No segundo trecho de script o conteúdo da variável x é extraído e colocado na página resultante da execução do Servlet correspondente. Em seguida mostraremos como executar o exemplo XX.XX. 125 Executando o arquivo JSP Para executar o exemplo XX.XX salve-o com a extensão.jsp. Por exemplo ola.jsp. Se você estiver usando o servidor Tomcat, coloque-o arquivo no subdiretório /webapps/examples/jsp do Tomcat. Por exemplo examples/jsp/teste. Para invocar o arquivo JSP basta embutir a URL em uma página ou digitar diretamente a seguinte URL no navegador. Usamos o diretório /webapps/examples/jsp para testar rapidamente o exemplo. Para desenvolver uma aplicação é aconselhável criar um diretório apropriado como mostrado na seção que tratou de Servlets. O Servlet criado a partir da página JSP é colocado em um diretório de trabalho. No caso do Tomcat o Servlet é colocado em subdiretório associado à aplicação subordinado ao diretório /work do Tomcat. O exemplo XX.XX mostra os principais trechos do Servlet criado a partir da tradução do arquivo ola.jsp pelo tradutor do Tomcat. Note que o Servlet é subclasse de uma classe HttpJspBase e não da HttpServlet. Além disso, o método que executado em resposta à requisição é o método _jspservice() e não o método service(). Note também que todas as partes estáticas da página JSP são colocadas como argumentos do método write() do objeto referenciado out.

127 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á 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 que referenciam a alguns objetos importantes. Estas variáveis estão disponíveis para o projetista da página JSP. As variáveis mais importantes são: Classe HttpServletRequest HttpServletResponse PageContext ServletContext HttpSession JspWriter Variável request response pagecontext application session out

128 Os objetos referenciados pelas variáveis request e response já tiveram seu uso esclarecido na seção sobre Servlets. O objeto do tipo JspWriter tem a mesma função do PrinterWriter do Servlet. Os outros objetos terão sua função esclarecida mais adiante. 127 Tags JSP Os tags JSP possuem a seguinte forma geral: <% Código JSP %> O primeiro caractere % pode ser seguido de outros caracteres que determinam o significado preciso do código dentro do tag. Os tags JSP possuem correspondência com os tags XML. Existem cinco categorias de tags JSP: Expressões Expressões Scriptlets Declarações Diretivas Comentários Em seguida comentaremos cada uma dessas categorias. <%= expressões %> Expressões são avaliadas, convertidas para String e colocadas na pá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 em milésimo de segundos e no segundo será colocado o método usado na

129 requisição. Note que cada expressão contém apenas um comando Java. Note também que o comando Java não é terminado pelo caractere ;. 128 Scriptlets <% código Java %> Quando é necessário mais de um comando Java ou o resultado da computação não é para ser colocado na página de resposta é preciso usar outra categoria de tags JSP: os Scriptlets. Os Scriptlets permitem inserir trechos de código em Java na página JSP. O exemplo XX.XX mostra uma página JSP contendo um Scriptlet que transforma a temperatura digitada em celcius para o equivalente 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 de declaração. Todo o código digitado é inserido no método _jspservice(). A figura XX.XX mostra o resultado da requisição após a digitação do valor 30 na caixa de texto do formulário.

130 129 Valor em Fahrenheit:86.0 Celcius: Enviar Consulta Figura VI-XX Resultado da conversão de 30 graus celcius. O código dentro do scriptlet é inserido da mesma forma que é escrito e todo o texto HTML estático antes e após ou um scriptlet é convertido para comandos print(). Desta forma o scriptlets não precisa conter comandos para código estático e blocos de controle abertos afetam o código HTML envolvidos por scriptlets. O exemplo XX.XX mostra dois formas de se produzir o mesmo efeito. No código da esquerda os Scriplets se intercalam com código HTML. O código HTML, quando da tradução da página JSP para Servlet é inserido como argumentos de métodos println() gerando o código da direita. Ambas as formas podem ser usadas em páginas JSP e produzem o mesmo efeito. Previsão do Tempo <% if (Math.random() < 0.5) %> Hoje vai <B>fazer sol</b>! <% else %> Hoje vai <B>chover</B>! <% %> out.println("previsã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 %>

131 Uma declaração JSP permite definir variáveis ou métodos que são inseridos no corpo do Servlet. Como as declarações não geram saída, elas são normalmente usadas em combinação com expressões e scriptlets. O Exemplo XX.XX mostra a declaração de uma variável que é usada para contar o número de 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. 130 As variáveis declaradas desta forma serão variáveis de instância. Já as variáveis declaradas em Scriptlets são variáveis locais ao método _jspservice(). Por isso é possível contar o número de requisições com o exemplo XX.XX. Se variável fosse declarada em um Scriptlet a variável seria local ao método _jspservice() e, portanto, teria seu valor reinicializado a cada chamada. Como já foi dito, os tags de declarações permitem a declaração de métodos. O Exemplo XX.XX mostra a declaração de um método que converte celcius 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 para Fahrenheit. Comentários

132 Existem dois tipos de comentários utilizados em páginas JSP. O primeiro exclui todo o bloco comentado da saída gerada pelo processamento da página. A forma geral deste tipo de comentário é a seguinte: <%--comentário --%> O segundo tipo de comentário é o utilizado em páginas HTML. Neste caso o comentário é enviado dentro da página de resposta. A forma geral deste tipo de comentário é a seguinte: <! comentário --> 131 Diretivas Diretivas são mensagens para JSP container. Elas não enviam nada para a página mas são importantes para definir atributos JSP e dependências com o JSP container. A forma geral da diretivas é a seguinte: Diretiva atributo="valor" %> ou Diretiva atributo 1 ="valor 1 " atributo 2 ="valor 2 "... atributo N =" valor N " %> Em seguida comentaremos as principais diretivas. Diretiva page page atributo 1 ="valor 1 "... atributo N =" valor N " %> A diretiva page permite a definição dos seguintes atributos: import contenttype isthreadsafe

133 session buffer autoflush info errorpage iserrorpage language 132 Segue a descrição de cada um desses atributos. Atributo e Forma Geral import="package.class" ou import="package.class 1,...,package.class N " contenttype="mime-type" Descrição Permite especificar os pacotes que devem ser importados para serem usados na página JSP. Exemplo: page import="java.util.*" %> 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" session="true false buffer="sizekb none" autoflush="true false Um valor true (default) indica um processamento normal do Servlet, onde múltiplas requisições são processadas simultaneamente. Um valor false indica que o processamento deve ser feito por instancias separadas do Servlet ou serialmente. Um valor true (default) indica que a variável predefinida session (HttpSession) deve ser associada à sessão, se existir, caso contrário uma nova sessão deve ser criada e associada a ela. Um valor false indica que nenhuma sessão será usada. Especifica o tamanho do buffer para escrita usado pelo objeto JspWriter. O tamanho default não é menor que 8k.. Um valor true (default) indica que o buffer deve ser

134 133 info="mensagem" errorpage="url iserrorpage="true false Language="java esvaziado quando estiver cheio. Define uma cadeia de caracteres que pode ser recuperada via getservletinfo(). Especifica a página JSP que deve ser processada em caso de exceções não capturadas. Indica se a página corrente pode atuar como página de erro para outra página JSP. O default é false. 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 é traduzida em 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 objeto referenciado pela variável request para obter os valores dos parâmetros de um formulário. O exemplo XX.XX usado para converter graus Celcius em Fahrenheit fez uso deste recurso. O exemplo XX.XX mostra outra página JSP com formulário. Note que o scriptlet é usado para obter o nome e os valores de todos os parâmetros contidos no formulário. Como o método getparameternames() retorna uma referência a um objeto Enumeration é preciso importar o pacote java.util, por meio da diretiva page. page import="java.util.*" %> <html><body> <H1>Formulário</H1> <%

135 Enumeration campos = request.getparameternames(); While(campos.hasMoreElements()) String campo = (String)campos.nextElement(); String valor = request.getparameter(campo); %> <li><%= campo %> = <%= valor %></li> <% %> 134 <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 dos valores Alcione e nas caixas de texto do formulário. Formulário telefone = nome = Alcione submit = envie Nome: Telefone: envie Figura VI.XX- Saída do exemplo XX.XX. Criando e Modificando Cookies

136 Da mesma for que em Servlets os cookies em JSP são tratados por meio da classe Cookie. Para recuperar os cookies enviados pelo navegador usa-se o método getcookies() do objeto HttpServletRequest que retorna um arranjo de Cookie. Os métodos getname() e getvalue() do objeto Cookie são utilizados para recuperar o nome o valor da informação associada ao cookie. O cookie é enviado para o navegador por meio do método addcookie() do objeto HttpServletResponse. O exemplo XX.XX mostra uma página JSP que exibe todos os cookies recebidos em uma requisição e adiciona mais um na resposta. 135 <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ágina JSP. 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: 9ppfv0lsl1 Cookie name: Cookie 0 value: Valor 0 antiga idade máxima em segundos: -1 nova idade máxima em segundos: 5 Cookie name: Cookie 1

137 value: Valor 1 antiga idade máxima em segundos: -1 nova idade máxima em segundos: 5 Cookie name: JSESSIONID value: 9ppfv0lsl1 antiga idade máxima em segundos: -1 nova idade máxima em segundos: 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 HttpSession referenciado pela variável session. Pode-se armazenar valores em uma sessão por meio do método setattribute() e recuperá-los por meio do método getattribute(). O tempo de duração default de uma sessão inativa (sem o recebimento de requisições do usuário) é 30 minutos mas esse valor pode ser alterado por meio do método setmaxinactiveinterval(). O exemplo XX.XX mostra duas páginas JSP. A primeira apresenta um formulário onde podem ser digitados dois valores recebe dois valores de digitados em um formulário e define o intervalo máximo de inatividade de uma sessão em 10 segundos. A segunda página recebe a submissão do formulário, insere os valores na sessão e apresenta os valores relacionados com a sessão assim como a identificação da sessão. page import="java.util.*" %> <html><body> <H1>Formulário</H1> <H1>Id da sessão: <%= session.getid() %></H1> <H3><li>Essa sessã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>

138 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ã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 quando o usuário muda de página. As figura XX.XX e XX.XX mostram o resultado da requisição após a digitação dos valores Alcione e nas caixas de texto do formulário, à submissão para página sessao2.jsp e o retorno à página sessao1.jsp.

139 138 Formulário Id da sessão: soo8utc4m1 Essa sessão foi criada em Antigo intervalo de inatividade = 1800 Novo intervalo de inatividade= 10 telefone = nome = Alcione Nome: Telefone: envie Figura VI.XX- Tela da página sessao1.jsp. Id da sessão: soo8utc4m1 Retorna Figura VI.XX- Tela da página sessao2.jsp. O Uso de JavaBeans

140 A medida que o código Java dentro do HTML torna-se cada vez mais complexo o desenvolvedor pode-se perguntar: Java em HTML não é o problema invertido do HTML em Servlet? O resultado não será tão complexo quanto produzir uma página usando println()? Em outras palavras, estou novamente misturando conteúdo com forma? Para solucionar esse problema a especificação de JSP permite o uso de JavaBeans para manipular a parte dinâmica em Java. JavaBeans já foram descritos detalhadamente em um capítulo anterior, mas podemos encarar um JavaBean como sendo apenas uma classe Java que obedece a uma certa padronização de nomeação de métodos, formando o que é denominado de propriedade. As propriedades de um bean são acessadas por meio de métodos que obedecem a convenção getxxxx e setxxxx., onde Xxxx é o nome da propriedade. Por exemplo, getitem() é o método usado para retornar o valor da 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 para uma instância do JavaBean. Você também pode modificar o atributo scope para estabelecer o escopo do bean além da página corrente. <jsp:usebean id="nome" scope="session" class="package.class" /> 139 Para modificar as propriedades de um JavaBean você pode usar o jsp:setproperty ou chamar um método explicitamente em um scriptlet. Para recuperar o valor de uma propriedade de um JavaBean você pode usar o jsp:getproperty ou chamar um método explicitamente em um scriptlet. Quando é dito que um bean tem uma propriedade prop do tipo T significa que o bean deve prover um método getprop() e um método do tipo setprop(t). O exemplo XX.XX mostra uma página JSP e um JavaBean. A página instancia o JavaBean, altera a propriedade mensagem e recupera o valor da propriedade, colocando-o na página. Página bean.jsp <HTML> <HEAD> <TITLE>Uso de beans</title> </HEAD> <BODY> <CENTER>

141 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ágina bean.jsp. Uso de JavaBeans Mensagem: Ola mundo! Figura VI.XX- Resultado da requisição à página bean.jsp. Se no tag setproperty usarmos o valor * para o atributo property então todos os valores de elementos de formulários que possuírem nomes iguais à propriedades serão transferidos para as respectivas propriedades

142 no momento do processamento da requisição. Por exemplo, seja uma página jsp contendo um formulário com uma caixa de texto com nome mensagem, como mostrado no exemplo XX.XX. Note que, neste caso, a propriedade mensagem do JavaBean tem seu valor atualizado para o valor digitado na caixa de texto, sem a necessidade de uma chamada explícita no tag setproperty. Os valores são automaticamente convertidos para o tipo correto no bean. 141 <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ágina bean2.jsp após a digitação do texto Olá! Mensagem: Ola! Texto: envie Uso de JavaBeans Figura VI.XX- Resultado da requisição à página bean2.jsp.

143 Escopo Java na Prática Volume II 142 Existem quatro valores possíveis para o escopo de um objeto: page, request, session e application. O default é page. A tabela XX.XX descreve cada tipo de escopo. Escopo page request session application Descrição Objetos declarados com nesse escopo são válidos até a resposta ser enviada ou a requisição ser encaminhada para outro programa no mesmo ambiente, ou seja, só podem ser referenciados nas páginas onde forem declarados. Objetos declarados com escopo page são referenciados pelo objeto pagecontext. Objetos declarados com nesse escopo são válidos durante a 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ão referenciados pelo objeto request. Objetos declarados com nesse escopo são válidos durante a sessão desde que a página seja definida para funcionar em uma sessão. Objetos declarados com escopo session são referenciados pelo objeto session. Objetos declarados com nesse escopo são acessíveis por páginas no mesmo servidor de aplicação. Objetos declarados com escopo application são referenciados 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 de compras virtual. O carrinho de compras virtual simula um carrinho de compras de supermercado, onde o cliente vai colocando os produtos selecionados para compra até se dirigir para o caixa para fazer o pagamento. No carrinho de compras virtual os itens selecionados pelo usuário são armazenados em uma estrutura de dados até que o usuário efetue o pagamento. Esse tipo de exemplo

144 exige que a página JSP funcione com o escopo session para manter o carrinho de compras durante a sessão. O exemplo XX.XX mostra um exemplo simples de implementação de carrinho de compras. O exemplo é composto por dois arquivos: um para a página JSP e um para o JavaBean que armazena os itens 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ê 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ão <option>rádio <option>computador <option>vídeo Cassete </select> <p><input TYPE=submit name="submit" value="adicione"> <input TYPE=submit name="submit" value="remova"></form> </body> </html> 143 JavaBean compra/carrinho.java package compra;

145 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, deixando de 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 ele digitará o número do cartão de crédito que será transmitido por meio de uma conexão segura para o servidor. Existem outras formas de pagamento, como

146 boleto bancário e dinheiro virtual. O próprio carrinho de compras geralmente é mais complexo, uma vez que os para compra devem ser obtidos dinamicamente de um banco de dados. A figura XX.XX mostra a tela resultante de algumas interações com o carrinho de compras. 145 Figura VI.XX- Carrinho de compras virtual.

THREADS EM JAVA. George Gomes Cabral

THREADS EM JAVA. George Gomes Cabral THREADS EM JAVA George Gomes Cabral THREADS Fluxo seqüencial de controle dentro de um processo. Suporte a múltiplas linhas de execução permite que múltiplos processamentos ocorram em "paralelo" (em computadores

Leia mais

Programação Orientada a Objetos em Java. Threads Threads Threads. Threads

Programação Orientada a Objetos em Java. Threads Threads Threads. Threads Universidade Federal do Amazonas Departamento de Ciência da Computação IEC481 Projeto de Programas Programação Orientada a Objetos em Java Threads Threads Threads Threads Professor: César Melo Slides baseados

Leia mais

Threads e Concorrência em Java (Material de Apoio)

Threads e Concorrência em Java (Material de Apoio) Introdução Threads e Concorrência em Java (Material de Apoio) Professor Lau Cheuk Lung http//www.inf.ufsc.br/~lau.lung INE-CTC-UFSC A maioria dos programas são escritos de modo seqüencial com um ponto

Leia mais

Universidade Federal da Paraíba

Universidade Federal da Paraíba Universidade Federal da Paraíba Mestrado em Informática Fora de Sede Universidade Tiradentes Aracaju - Sergipe Disciplina Sistemas Operacionais Professor Jacques Philippe Sauvé Aluno José Maria Rodrigues

Leia mais

Fundamentos de Programaçã. ção Concorrente

Fundamentos de Programaçã. ção Concorrente Java 2 Standard Edition Fundamentos de Programaçã ção Concorrente Helder da Rocha www.argonavis.com.br 1 Programação concorrente O objetivo deste módulo é oferecer uma introdução a Threads que permita

Leia mais

Java Threads. Introdução

Java Threads. Introdução Java Threads mleal@inf.puc-rio.br 1 Introdução O único mecanismo de concorrência suportado explicitamente pela linguagem Java é multi-threading. threading. Os mecanismos de gerenciamento e sicronização

Leia mais

Threads e Sockets em Java. Threads em Java. Programas e Processos

Threads e Sockets em Java. Threads em Java. Programas e Processos Threads em Java Programas e Processos Um programa é um conceito estático, isto é, um programa é um arquivo em disco que contém um código executável por uma CPU. Quando este programa é executado dizemos

Leia mais

Programação Concorrente em Java. Profa Andréa Schwertner Charão DLSC/CT/UFSM

Programação Concorrente em Java. Profa Andréa Schwertner Charão DLSC/CT/UFSM Programação Concorrente em Java Profa Andréa Schwertner Charão DLSC/CT/UFSM O que é programação concorrente? Um programa, múltiplos fluxos de execução Quando usar programação concorrente? Desempenho Ex.:

Leia mais

Como foi exposto anteriormente, os processos podem ter mais de um fluxo de execução. Cada fluxo de execução é chamado de thread.

Como foi exposto anteriormente, os processos podem ter mais de um fluxo de execução. Cada fluxo de execução é chamado de thread. 5 THREADS Como foi exposto anteriormente, os processos podem ter mais de um fluxo de execução. Cada fluxo de execução é chamado de thread. 5.1 VISÃO GERAL Uma definição mais abrangente para threads é considerá-lo

Leia mais

PROGRAMAÇÃO PARALELA EM C# Notas de Aula Prof. Marcos E Barreto

PROGRAMAÇÃO PARALELA EM C# Notas de Aula Prof. Marcos E Barreto PROGRAMAÇÃO PARALELA EM C# Notas de Aula Prof. Marcos E Barreto Referências: - Programming C#. Jesse Liberty, O Reilly, 2001. - Threading in Java and C#: a focused language comparison. Shannon Hardt -

Leia mais

Um sistema é constituído de um conjunto de processos que executam seus respectivos códigos do sistema operacional e processos e códigos de usuários.

Um sistema é constituído de um conjunto de processos que executam seus respectivos códigos do sistema operacional e processos e códigos de usuários. Os sistemas computacionais atuais permitem que diversos programas sejam carregados na memória e executados simultaneamente. Essa evolução tornou necessário um controle maior na divisão de tarefas entre

Leia mais

Programação Concorrente

Programação Concorrente + XV Jornada de Cursos CITi Aula 2 Programação Concorrente Benito Fernandes Fernando Castor João Paulo Oliveira Weslley Torres + Agenda Conceitos básicos de Threads em Java Benefícios de Thread Estados,Métodos,

Leia mais

Threads em Java. Sistemas Operacionais - Laboratório Professor Machado

Threads em Java. Sistemas Operacionais - Laboratório Professor Machado Threads em Java Sistemas Operacionais - Laboratório Professor Machado 1 Conceitos de Programação Concorrente Uma unidade concorrente é um componente de um programa que não exige a execução seqüencial,

Leia mais

Aula 30 - Sockets em Java

Aula 30 - Sockets em Java Aula 30 - Sockets em Java Sockets Sockets são estruturas que permitem que funções de software se interconectem. O conceito é o mesmo de um soquete (elétrico, telefônico, etc...), que serve para interconectar

Leia mais

Computação Concorrente (MAB-117) Monitores

Computação Concorrente (MAB-117) Monitores Computação Concorrente (MAB-117) Monitores Prof. Silvana Rossetto 1 Departamento de Ciência da Computação (DCC) Instituto de Matemática (IM) Universidade Federal do Rio de Janeiro (UFRJ) Maio de 2012 1.

Leia mais

Mecanismo de Threads em Java 2

Mecanismo de Threads em Java 2 Mecanismo de Threads em Java 2 Índice MULTITHREADING : Múltiplos processos. 3 Paulo Costa, n.º 1000334 Mecanismo de Threads Página 2 de 20 MULTITHREADING : Múltiplos Processos As Threads são convenientes

Leia mais

(Aula 17) Threads em Java

(Aula 17) Threads em Java (Aula 17) Threads em Java Difícil As Threads thread threads de emjava classificar sãogerenciadaspelajvm. podemser com user criadasdas thread ou kernel Profa. Patrícia A seguintesmaneiras: Fazendo extend

Leia mais

Programação Concorrente em java - Exercícios Práticos Abril 2004

Programação Concorrente em java - Exercícios Práticos Abril 2004 Programação Concorrente em java - Exercícios Práticos Abril 2004 1. Introdução As threads correspondem a linhas de controlo independentes no âmbito de um mesmo processo. No caso da linguagem JAVA, é precisamente

Leia mais

O uso de exceções permite separar a detecção da ocorrência de uma situação excepcional do seu tratamento, ao se programar um método em Java.

O uso de exceções permite separar a detecção da ocorrência de uma situação excepcional do seu tratamento, ao se programar um método em Java. Exceções em Java Miguel Jonathan DCC/IM/UFRJ (rev. abril de 2011) Resumo dos conceitos e regras gerais do uso de exceções em Java O uso de exceções permite separar a detecção da ocorrência de uma situação

Leia mais

Notas da Aula 4 - Fundamentos de Sistemas Operacionais

Notas da Aula 4 - Fundamentos de Sistemas Operacionais Notas da Aula 4 - Fundamentos de Sistemas Operacionais 1. Threads Threads são linhas de execução dentro de um processo. Quando um processo é criado, ele tem uma única linha de execução, ou thread. Esta

Leia mais

Processos (Threads,Virtualização e Migração de Código)

Processos (Threads,Virtualização e Migração de Código) Processos (Threads,Virtualização e Migração de Código) Roteiro Processos Threads Virtualização Migração de Código O que é um processo?! Processos são programas em execução. Processo Processo Processo tem

Leia mais

BC1518-Sistemas Operacionais. Prof. Marcelo Z. do Nascimento marcelo.nascimento@ufabc.edu.br

BC1518-Sistemas Operacionais. Prof. Marcelo Z. do Nascimento marcelo.nascimento@ufabc.edu.br BC1518-Sistemas Operacionais Sincronização de Processos (aula 5 Parte 2) Prof. Marcelo Z. do Nascimento marcelo.nascimento@ufabc.edu.br Roteiro Semáforos Monitores Passagem de Mensagem Exemplos em S.O.

Leia mais

Java. Marcio de Carvalho Victorino www.dominandoti.eng.br

Java. Marcio de Carvalho Victorino www.dominandoti.eng.br Java Marcio de Carvalho Victorino www.dominandoti.eng.br 3. Considere as instruções Java abaixo: int cont1 = 3; int cont2 = 2; int cont3 = 1; cont1 += cont3++; cont1 -= --cont2; cont3 = cont2++; Após a

Leia mais

Curso Adonai QUESTÕES Disciplina Linguagem JAVA

Curso Adonai QUESTÕES Disciplina Linguagem JAVA 1) Qual será o valor da string c, caso o programa rode com a seguinte linha de comando? > java Teste um dois tres public class Teste { public static void main(string[] args) { String a = args[0]; String

Leia mais

Programação Orientada a Objetos Threads

Programação Orientada a Objetos Threads Threads Prof. Edwar Saliba Júnior Janeiro de 2013 1 Introdução Multithreading: fornece múltiplas threads de execução para a aplicação; permite que programas realizem tarefas concorrentemente; às vezes

Leia mais

Multithreading. Programação Orientada por Objectos com Java. Ademar Aguiar. MRSC - Programação em Comunicações

Multithreading. Programação Orientada por Objectos com Java. Ademar Aguiar. MRSC - Programação em Comunicações Programação Orientada por Objectos com Java www.fe.up.pt/~aaguiar ademar.aguiar@fe.up.pt 1 Multithreading 154 Objectivos Ser capaz de: Explicar os conceitos básicos de multithreading Criar threads múltiplos

Leia mais

Introdução ao uso de Threads em Java

Introdução ao uso de Threads em Java Introdução ao uso de Threads em Java Daniel de Angelis Cordeiro danielc@ime.usp.br 26 de março de 2004 Sumário 1 Introdução 1 1.1 O que são threads?............................... 1 1.2 Todo programa em

Leia mais

Introdução a Threads Java

Introdução a Threads Java Introdução a Threads Java Prof. Gerson Geraldo Homrich Cavalheiro Universidade Federal de Pelotas Departamento de Informática Instituto de Física e Matemática Pelotas RS Brasil http://gersonc.anahy.org

Leia mais

Escola Superior de Gestão e Tecnologia. Tratamento de Exceções

Escola Superior de Gestão e Tecnologia. Tratamento de Exceções Escola Superior de Gestão e Tecnologia Tratamento de Exceções Objetivos Compreender como o tratamento de exceção e de erro funciona. Como u4lizar try, throw e catch para detectar, indicar e tratar exceções,

Leia mais

Processos e Threads (partes I e II)

Processos e Threads (partes I e II) Processos e Threads (partes I e II) 1) O que é um processo? É qualquer aplicação executada no processador. Exe: Bloco de notas, ler um dado de um disco, mostrar um texto na tela. Um processo é um programa

Leia mais

Universidade da Beira Interior. Sistemas Distribuídos

Universidade da Beira Interior. Sistemas Distribuídos Folha 6-1 Sincronização de Threads A sincronização de Threads em Java é baseada no conceito do Monitor (de Hoare). Cada objecto Java tem associado um monitor (ou lock ) que pode ser activado se a palavra

Leia mais

Sistemas Distribuídos

Sistemas Distribuídos Sistemas Distribuídos Processos I: Threads, virtualização e comunicação via protocolos Prof. MSc. Hugo Souza Nesta primeira parte sobre os Processos Distribuídos iremos abordar: Processos e a comunicação

Leia mais

Capítulo 2 Processos e Threads Prof. Fernando Freitas

Capítulo 2 Processos e Threads Prof. Fernando Freitas slide 1 Capítulo 2 Processos e Threads Prof. Fernando Freitas Material adaptado de: TANENBAUM, Andrew S. Sistemas Operacionais Modernos. 3ª edição. Disponível em: http://www.prenhall.com/tanenbaum_br slide

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais SINCRONIZAÇÃO E COMUNICAÇÃO ENTRE PROCESSOS MACHADO/MAIA: CAPÍTULO 07, PÁGINA 101 Prof. Pedro Luís Antonelli Anhanguera Educacional sistemas multiprogramáveis Os sistemas multiprogramáveis

Leia mais

Sistemas Operacionais: Threads

Sistemas Operacionais: Threads Sistemas Operacionais: Threads Threads Única linha de execução x múltiplas linhas de execução Benefícios Tempo de resposta Compartilhamento de recursos Economia de recursos Desempenho em arquiteturas multiprocessadas

Leia mais

Java 2 Standard Edition Como criar classes e objetos

Java 2 Standard Edition Como criar classes e objetos Java 2 Standard Edition Como criar classes e objetos Helder da Rocha www.argonavis.com.br 1 Assuntos abordados Este módulo explora detalhes da construção de classes e objetos Construtores Implicações da

Leia mais

Programação de Computadores - I. Profª Beatriz Profº Israel

Programação de Computadores - I. Profª Beatriz Profº Israel Programação de Computadores - I Profª Beatriz Profº Israel As 52 Palavras Reservadas O que são palavras reservadas São palavras que já existem na linguagem Java, e tem sua função já definida. NÃO podem

Leia mais

Programação Orientada a Objetos Prof. Rone Ilídio UFSJ/CAP

Programação Orientada a Objetos Prof. Rone Ilídio UFSJ/CAP Programação Orientada a Objetos Prof. Rone Ilídio UFSJ/CAP 1) Introdução Programação Orientada a Objetos é um paradigma de programação bastante antigo. Entretanto somente nos últimos anos foi aceito realmente

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais Gerência de processos Controle e descrição de processos Edson Moreno edson.moreno@pucrs.br http://www.inf.pucrs.br/~emoreno Sumário Representação e controle de processos pelo SO Estrutura

Leia mais

Especialização em desenvolvimento para web com interfaces ricas. Tratamento de exceções em Java Prof. Fabrízzio A. A. M. N. Soares

Especialização em desenvolvimento para web com interfaces ricas. Tratamento de exceções em Java Prof. Fabrízzio A. A. M. N. Soares Especialização em desenvolvimento para web com interfaces ricas Tratamento de exceções em Java Prof. Fabrízzio A. A. M. N. Soares Objetivos Conceito de exceções Tratar exceções pelo uso de try, catch e

Leia mais

Programação Concorrente Processos e Threads

Programação Concorrente Processos e Threads Programação Concorrente Processos e Threads Prof. Eduardo Alchieri Processos O conceito mais central em qualquer sistema operacional é o processo Uma abstração de um programa em execução Um programa por

Leia mais

Capítulo 5 Reuso de Classes

Capítulo 5 Reuso de Classes Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. Desenvolvimento Orientado a Objetos com Java Capítulo 5 Reuso de Classes

Leia mais

Sistemas Distribuídos

Sistemas Distribuídos Sistemas Distribuídos Processos e Threads Gustavo Reis gustavo.reis@ifsudestemg.edu.br - O que são Processos? Uma abstração de um programa em execução. Mantêm a capacidade de operações (pseudo)concorrentes,

Leia mais

Sistemas Distribuídos

Sistemas Distribuídos Sistemas Distribuídos Marcelo Lobosco DCC/UFJF Comunicação em Sistemas Distribuídos Aula 06 Agenda Modelo Cliente-Servidor (cont.) Invocação Remota de Método (Remote Method Invocation RMI) Visão Geral

Leia mais

Sobre o Professor Dr. Sylvio Barbon Junior

Sobre o Professor Dr. Sylvio Barbon Junior 5COP088 Laboratório de Programação Aula 1 Java Prof. Dr. Sylvio Barbon Junior Sylvio Barbon Jr barbon@uel.br 1 Sobre o Professor Dr. Sylvio Barbon Junior Formação: Ciência e Engenharia da Computação (2005

Leia mais

Linguagem de Programação JAVA. Técnico em Informática Professora Michelle Nery

Linguagem de Programação JAVA. Técnico em Informática Professora Michelle Nery Linguagem de Programação JAVA Técnico em Informática Professora Michelle Nery Agenda Regras paravariáveis Identificadores Válidos Convenção de Nomenclatura Palavras-chaves em Java Tipos de Variáveis em

Leia mais

2 Modelos de Implementação

2 Modelos de Implementação 2 Modelos de Implementação Os modelos de concorrência definem como uma aplicação atende às requisições concorrentes. Os modelos de sandboxes definem como o ambiente das aplicações são criados. Os modelos

Leia mais

Revisão Ultima aula [1/2]

Revisão Ultima aula [1/2] SOP - TADS Comunicação Interprocessos - 2 IPC Prof. Ricardo José Pfitscher dcc2rjp@joinville.udesc.br Material cedido por: Prof. Rafael Rodrigues Obelheiro Prof. Maurício Aronne Pillon Revisão Ultima aula

Leia mais

Threads Aula 04 2 Quadrimestre

Threads Aula 04 2 Quadrimestre BC1518 - Sistemas Operacionais Threads Aula 04 2 Quadrimestre de 2010 Prof. Marcelo Z. do Nascimento Email: marcelo.nascimento@ufabc.edu.br Roteiro Threads: Visão Geral Benefícios Tipos Modelos de multithread

Leia mais

Analisar os sistemas operacionais apresentados na figura e responder as questões abaixo: Identificar

Analisar os sistemas operacionais apresentados na figura e responder as questões abaixo: Identificar Projeto Integrador Sistemas Operacionais Prof.ª Lucilia Ribeiro GTI 3 Noturno Grupo: Anderson Alves da Mota. André Luiz Silva. Misael bezerra dos santos. Sandro de almeida silva. Analisar os sistemas operacionais

Leia mais

Invocação de Métodos Remotos

Invocação de Métodos Remotos Invocação de Métodos Remotos Java RMI (Remote Method Invocation) Tópicos Tecnologia RMI Introdução Modelo de camadas do RMI Arquitetura Fluxo de operação do RMI Passos para implementação Estudo de caso

Leia mais

1.6. Tratamento de Exceções

1.6. Tratamento de Exceções Paradigmas de Linguagens I 1 1.6. Tratamento de Exceções Uma exceção denota um comportamento anormal, indesejado, que ocorre raramente e requer alguma ação imediata em uma parte do programa [GHE 97, DER

Leia mais

S.O.: Conceitos Básicos

S.O.: Conceitos Básicos S.O.: Conceitos Básicos Camada de software localizada entre o hardware e os programas que executam tarefas para o usuário; Acessa os periféricos Entrada e Saída Esconde os detalhes do hardware para o programador

Leia mais

Módulo 06 Desenho de Classes

Módulo 06 Desenho de Classes Módulo 06 Desenho de Classes Última Atualização: 13/06/2010 1 Objetivos Definir os conceitos de herança, polimorfismo, sobrecarga (overloading), sobreescrita(overriding) e invocação virtual de métodos.

Leia mais

JAVA VIRTUAL MACHINE (JVM)

JAVA VIRTUAL MACHINE (JVM) JAVA VIRTUAL MACHINE (JVM) Por Leandro Baptista, Marlon Palangani e Tiago Deoldoto, 11 de Abril de 2009 A linguagem de programação Java proporciona o desenvolvimento aplicações que podem ser executadas

Leia mais

Notas da Aula 6 - Fundamentos de Sistemas Operacionais

Notas da Aula 6 - Fundamentos de Sistemas Operacionais 1. Monitores Notas da Aula 6 - Fundamentos de Sistemas Operacionais Embora os semáforos sejam uma boa solução para o problema da exclusão mútua, sua utilização não é trivial. O programador é obrigado a

Leia mais

INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DE ALAGOAS CURSO TECNICO EM INFORMATICA DISCIPLINA:

INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DE ALAGOAS CURSO TECNICO EM INFORMATICA DISCIPLINA: INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DE ALAGOAS CURSO TECNICO EM INFORMATICA DISCIPLINA: PROGRAMAÇÃO ORIENTADA A OBJETOS PROFESSOR: REINALDO GOMES ASSUNTO: REVISÃO DA INTRODUÇÃO A ORIENTAÇÃO

Leia mais

Linguagens de. Aula 02. Profa Cristiane Koehler cristiane.koehler@canoas.ifrs.edu.br

Linguagens de. Aula 02. Profa Cristiane Koehler cristiane.koehler@canoas.ifrs.edu.br Linguagens de Programação III Aula 02 Profa Cristiane Koehler cristiane.koehler@canoas.ifrs.edu.br Linguagens de Programação Técnica de comunicação padronizada para enviar instruções a um computador. Assim

Leia mais

Redes de Computadores - 2010/1

Redes de Computadores - 2010/1 Redes de Computadores - 2010/1 Miniprojeto Universidade Federal do Espírito Santo - UFES Professor: Magnos Martinello 20 de maio de 2010 1 1 Proposta A proposta do trabalho é reforçar o compreendimento

Leia mais

Chamadas de Sistema e Processo

Chamadas de Sistema e Processo Andrique Amorim www.andrix.com.br professor@andrix.com.br Chamadas de Sistema e Processo Estrutura do Computador Sistemas Operacionais Estrutura do Computador Sistemas Operacionais Modos de Acesso ao S.O.

Leia mais

Chamadas de Sistema e Processo

Chamadas de Sistema e Processo Andrique Amorim www.andrix.com.br professor@andrix.com.br Chamadas de Sistema e Processo Estrutura do Computador Sistemas Operacionais Estrutura do Computador Sistemas Operacionais Modos de Acesso ao S.O.

Leia mais

Java : Comunicação Cliente-Servidor.

Java : Comunicação Cliente-Servidor. Java : Comunicação Cliente-Servidor. Objetivo: Capacitar o aluno a desenvolver uma aplicação servidora para múltiplos clientes. Apresentar as classes Socket e ServerSocket, classes de fluxo de dados, a

Leia mais

Uma Introdução à Arquitetura CORBA. O Object Request Broker (ORB)

Uma Introdução à Arquitetura CORBA. O Object Request Broker (ORB) Uma Introdução à Arquitetura Francisco C. R. Reverbel 1 Copyright 1998-2006 Francisco Reverbel O Object Request Broker (ORB) Via de comunicação entre objetos (object bus), na arquitetura do OMG Definido

Leia mais

(ou seja, boas praticas de programação orientada a objetos devem ser empregadas mesmo se não foram explicitamente solicitadas)

(ou seja, boas praticas de programação orientada a objetos devem ser empregadas mesmo se não foram explicitamente solicitadas) PC-2 / LP-2 2009/2 Lista 2 Prof. Alexandre Sztajnberg Em todos os exercícios a(o) aluna(o) deve, além de atender aos requisitos enunciados, utilizar os conceitos e características de orientação a objetos

Leia mais

RESUMO DOS IMPORTANTES

RESUMO DOS IMPORTANTES RESUMO DOS TÓPICOS + IMPORTANTES De instância (non-static) - estão no contexto da classe sem o modificador static De Classe (static) - estão no contexto da classe com o modificador static Local estão no

Leia mais

Criando documentação com javadoc

Criando documentação com javadoc H Criando documentação com javadoc H.1 Introdução Neste apêndice, fornecemos uma introdução a javadoc ferramenta utilizada para criar arquivos HTML que documentam o código Java. Essa ferramenta é usada

Leia mais

PROCESSOS. Prof. Maicon A. Sartin mapsartin@gmail.com

PROCESSOS. Prof. Maicon A. Sartin mapsartin@gmail.com PROCESSOS Prof. Maicon A. Sartin mapsartin@gmail.com Cenários em Sistemas Multitarefas Um usuário pode executar diversas atividades simultâneas Música Editoração de texto Navegar na Internet Servidor pode

Leia mais

Sistema Operacional. Processo e Threads. Prof. Dr. Márcio Andrey Teixeira Sistemas Operacionais

Sistema Operacional. Processo e Threads. Prof. Dr. Márcio Andrey Teixeira Sistemas Operacionais Sistema Operacional Processo e Threads Introdução a Processos Todos os computadores modernos são capazes de fazer várias coisas ao mesmo tempo. Enquanto executa um programa do usuário, um computador pode

Leia mais

Exercícios de Revisão Java Básico

Exercícios de Revisão Java Básico Exercícios de Revisão Java Básico (i) Programação básica (estruturada) 1) Faça um programa para calcular o valor das seguintes expressões: S 1 = 1 1 3 2 5 3 7 99... 4 50 S 2 = 21 50 22 49 23 48...250 1

Leia mais

SISTEMAS DISTRIBUÍDOS Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com.br

SISTEMAS DISTRIBUÍDOS Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com.br - Aula 5 PROCESSOS 1. INTRODUÇÃO Em sistemas distribuídos é importante examinar os diferentes tipos de processos e como eles desempenham seu papel. O conceito de um processo é originário do campo de sistemas

Leia mais

Comandos de repetição For (inicialização; condição de execução; incremento/decremento) { //Código }

Comandos de repetição For (inicialização; condição de execução; incremento/decremento) { //Código } Este documento tem o objetivo de demonstrar os comandos e sintaxes básicas da linguagem Java. 1. Alguns passos para criar programas em Java As primeiras coisas que devem ser abordadas para começar a desenvolver

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais Sistemas de Entrada/Saída Princípios de Hardware Sistema de Entrada/Saída Visão Geral Princípios de Hardware Dispositivos de E/S Estrutura Típica do Barramento de um PC Interrupções

Leia mais

DEFINIÇÃO DE MÉTODOS

DEFINIÇÃO DE MÉTODOS Cursos: Análise, Ciência da Computação e Sistemas de Informação Programação I - Prof. Aníbal Notas de aula 2 DEFINIÇÃO DE MÉTODOS Todo o processamento que um programa Java faz está definido dentro dos

Leia mais

A ) O cliente terá que implementar uma interface remota. . Definir a interface remota com os métodos que poderão ser acedidos remotamente

A ) O cliente terá que implementar uma interface remota. . Definir a interface remota com os métodos que poderão ser acedidos remotamente Java RMI - Remote Method Invocation Callbacks Folha 9-1 Vimos, na folha prática anterior, um exemplo muito simples de uma aplicação cliente/ servidor em que o cliente acede à referência remota de um objecto

Leia mais

2 Orientação a objetos na prática

2 Orientação a objetos na prática 2 Orientação a objetos na prática Aula 04 Sumário Capítulo 1 Introdução e conceitos básicos 1.4 Orientação a Objetos 1.4.1 Classe 1.4.2 Objetos 1.4.3 Métodos e atributos 1.4.4 Encapsulamento 1.4.5 Métodos

Leia mais

Orientação a Objetos em Java. Leonardo Gresta Paulino Murta leomurta@ic.uff.br

Orientação a Objetos em Java. Leonardo Gresta Paulino Murta leomurta@ic.uff.br Orientação a Objetos em Java Leonardo Gresta Paulino Murta leomurta@ic.uff.br Agenda Introdução; Orientação a Objetos; Orientação a Objetos em Java; Leonardo Murta Orientação a Objetos em Java 2 Agosto/2007

Leia mais

Objetos Distribuídos - Programação Distribuída Orientado a Objetos. Luiz Affonso Guedes

Objetos Distribuídos - Programação Distribuída Orientado a Objetos. Luiz Affonso Guedes Objetos Distribuídos - Programação Distribuída Orientado a Objetos Luiz Affonso Guedes Introdução Conceitos básicos programação distribuída + programação orientada a objetos = Objetos distribuídos Motivação

Leia mais

Sistemas Distribuídos. Professora: Ana Paula Couto DCC 064

Sistemas Distribuídos. Professora: Ana Paula Couto DCC 064 Sistemas Distribuídos Professora: Ana Paula Couto DCC 064 Questões Em uma rede de sobreposição (overlay), mensagens são roteadas de acordo com a topologia da sobreposição. Qual uma importante desvantagem

Leia mais

Sistemas Operacionais

Sistemas Operacionais Sistemas Operacionais Sistemas Operacionais Prof. Marcelo Sabaris Carballo Pinto Gerencia de Processos 4.1 Introdução aos Processos Gerenciamento de Processos Processo -Definição: É uma abstração do sistema

Leia mais

Invocação de Métodos Remotos RMI (Remote Method Invocation)

Invocação de Métodos Remotos RMI (Remote Method Invocation) Invocação de Métodos Remotos RMI (Remote Method Invocation) Programação com Objetos Distribuídos Um sistema de objetos distribuídos permite a operação com objetos remotos A partir de uma aplicação cliente

Leia mais

CURSO DE PROGRAMAÇÃO EM JAVA

CURSO DE PROGRAMAÇÃO EM JAVA CURSO DE PROGRAMAÇÃO EM JAVA Introdução para Iniciantes Prof. M.Sc. Daniel Calife Índice 1 - A programação e a Linguagem Java. 1.1 1.2 1.3 1.4 Linguagens de Programação Java JDK IDE 2 - Criando o primeiro

Leia mais

TECNÓLOGO EM ANÁLISE E DESENVOLVIMENTO DE SISTEMAS PROGRAMAÇÃO DE COMPUTADORES I Aula 01: Conceitos Iniciais / Sistema Operacional

TECNÓLOGO EM ANÁLISE E DESENVOLVIMENTO DE SISTEMAS PROGRAMAÇÃO DE COMPUTADORES I Aula 01: Conceitos Iniciais / Sistema Operacional TECNÓLOGO EM ANÁLISE E DESENVOLVIMENTO DE SISTEMAS PROGRAMAÇÃO DE COMPUTADORES I Aula 01: Conceitos Iniciais / Sistema Operacional O conteúdo deste documento tem por objetivo apresentar uma visão geral

Leia mais

Memory Leak em Java?

Memory Leak em Java? 1 Memory Leak em Java? Saiba como memory leaks se manifestam em Java e como evitá-los Sobre o Autor Carlos Eduardo G. Tosin (carlos@tosin.com.br) é formado em Ciência da Computação pela PUC-PR, pós-graduado

Leia mais

1ª QUESTÃO Linguagem C Vantagens: Desvantagens: Linguagem C++ Vantagens: Desvantagens:

1ª QUESTÃO Linguagem C Vantagens: Desvantagens: Linguagem C++ Vantagens: Desvantagens: 1ª QUESTÃO Linguagem C Vantagens: É uma linguagem simples que nos permite trabalhar com funções matemáticas, ficheiros, entre outras sendo necessário para tal a inclusão de bibliotecas padrão as quais

Leia mais

Aula 09 Introdução à Java. Disciplina: Fundamentos de Lógica e Algoritmos Prof. Bruno Gomes http://www.profbrunogomes.com.br/

Aula 09 Introdução à Java. Disciplina: Fundamentos de Lógica e Algoritmos Prof. Bruno Gomes http://www.profbrunogomes.com.br/ Aula 09 Introdução à Java Disciplina: Fundamentos de Lógica e Algoritmos Prof. Bruno Gomes http://www.profbrunogomes.com.br/ Agenda da Aula Java: Sintaxe; Tipos de Dados; Variáveis; Impressão de Dados.

Leia mais

Programação Orientada a Objetos em java. Polimorfismo

Programação Orientada a Objetos em java. Polimorfismo Programação Orientada a Objetos em java Polimorfismo Polimorfismo Uma característica muito importante em sistemas orientados a objetos Termo proveniente do grego, e significa muitas formas Em POO, significa

Leia mais

Chamadas Remotas de Procedimentos (RPC) O Conceito de Procedimentos. RPC: Programa Distribuído. RPC: Modelo de Execução

Chamadas Remotas de Procedimentos (RPC) O Conceito de Procedimentos. RPC: Programa Distribuído. RPC: Modelo de Execução Chamadas Remotas de Chamada Remota de Procedimento (RPC) ou Chamada de Função ou Chamada de Subrotina Método de transferência de controle de parte de um processo para outra parte Procedimentos => permite

Leia mais

Notas da Aula 15 - Fundamentos de Sistemas Operacionais

Notas da Aula 15 - Fundamentos de Sistemas Operacionais Notas da Aula 15 - Fundamentos de Sistemas Operacionais 1. Software de Entrada e Saída: Visão Geral Uma das tarefas do Sistema Operacional é simplificar o acesso aos dispositivos de hardware pelos processos

Leia mais

Sistemas Operacionais Aula 06: Threads. Ezequiel R. Zorzal ezorzal@unifesp.br www.ezequielzorzal.com

Sistemas Operacionais Aula 06: Threads. Ezequiel R. Zorzal ezorzal@unifesp.br www.ezequielzorzal.com Sistemas Operacionais Aula 06: Threads Ezequiel R. Zorzal ezorzal@unifesp.br www.ezequielzorzal.com Objetivos Introduzir o conceito de thread Discutir as APIs das bibliotecas de threads Pthreads, Win32

Leia mais

Curso Java Starter. www.t2ti.com 1

Curso Java Starter. www.t2ti.com 1 1 2 Apresentação O Curso Java Starter foi projetado com o objetivo de ajudar àquelas pessoas que têm uma base de lógica de programação e desejam entrar no mercado de trabalho sabendo Java, A estrutura

Leia mais

Sistemas Operacionais. Escalonamento de Processo. Prof. Dr. Márcio Andrey Teixeira

Sistemas Operacionais. Escalonamento de Processo. Prof. Dr. Márcio Andrey Teixeira Sistemas Operacionais Escalonamento de Processo Prof. Dr. Márcio Andrey Teixeira Quando um computador é multiprogramado, ele muitas vezes tem variados processos que competem pela CPU ao mesmo tempo; Essa

Leia mais

EMENTA DO CURSO. Tópicos:

EMENTA DO CURSO. Tópicos: EMENTA DO CURSO O Curso Preparatório para a Certificação Oracle Certified Professional, Java SE 6 Programmer (Java Básico) será dividido em 2 módulos e deverá ter os seguintes objetivos e conter os seguintes

Leia mais

Capítulo 2 Processos e Threads. 2.1 Processos 2.2 Threads 2.3 Comunicação interprocesso 2.4 Problemas clássicos de IPC 2.

Capítulo 2 Processos e Threads. 2.1 Processos 2.2 Threads 2.3 Comunicação interprocesso 2.4 Problemas clássicos de IPC 2. Capítulo 2 Processos e Threads 2.1 Processos 2.2 Threads 2.3 Comunicação interprocesso 2.4 Problemas clássicos de IPC 2.5 Escalonamento 1 Processos O Modelo de Processo Multiprogramação de quatro programas

Leia mais

Programação por Objectos. Java

Programação por Objectos. Java Programação por Objectos Java Parte 2: Classes e objectos LEEC@IST Java 1/24 Classes (1) Sintaxe Qualif* class Ident [ extends IdentC] [ implements IdentI [,IdentI]* ] { [ Atributos Métodos ]* Qualif:

Leia mais

Linguagem Java: Threads. Rômulo Silva de Oliveira Instituto de Informática - UFRGS. romulo@inf.ufrgs.br http://www.inf.ufrgs.

Linguagem Java: Threads. Rômulo Silva de Oliveira Instituto de Informática - UFRGS. romulo@inf.ufrgs.br http://www.inf.ufrgs. Linguagem Java: Threads Rômulo Silva de Oliveira Instituto de Informática - UFRGS romulo@inf.ufrgs.br http://www.inf.ufrgs.br/~romulo 1 Threads Java suporta programação multithread como parte da linguagem

Leia mais

SUMÁRIO CAPÍTULO 1 INTRODUÇÃO AO JAVA... 1

SUMÁRIO CAPÍTULO 1 INTRODUÇÃO AO JAVA... 1 SUMÁRIO CAPÍTULO 1 INTRODUÇÃO AO JAVA... 1 A Origem... 2 O Caminho Para a Aprendizagem... 4 Java Como Plataforma... 6 Finalidade... 9 Características... 11 Perspectivas... 13 Exercícios... 14 CAPÍTULO

Leia mais

Arquitetura de Computadores. Introdução aos Sistemas Operacionais

Arquitetura de Computadores. Introdução aos Sistemas Operacionais Arquitetura de Computadores Introdução aos Sistemas Operacionais O que é um Sistema Operacional? Programa que atua como um intermediário entre um usuário do computador ou um programa e o hardware. Os 4

Leia mais

Sistemas Operacionais Aula 08: Sincronização de Processos. Ezequiel R. Zorzal ezorzal@unifesp.br www.ezequielzorzal.com

Sistemas Operacionais Aula 08: Sincronização de Processos. Ezequiel R. Zorzal ezorzal@unifesp.br www.ezequielzorzal.com Sistemas Operacionais Aula 08: Sincronização de Processos Ezequiel R. Zorzal ezorzal@unifesp.br www.ezequielzorzal.com Objetivos Introduzir o problema da seção crítica, cujas soluções podem ser usadas

Leia mais