PRDS - Programa de Residência em
Desenvolvimento de Software
Laboratório de Engenharia de Software (LES) da PUC-Rio
Carlos Lucena
[email protected]
Rodrigo Paes
[email protected]
Gustavo Carvalho
[email protected]
Cidiane Lobato
[email protected]
Conteúdo
• Módulo 1: Java I – 4 horas
• Módulo 4: UML – 8 horas
– Sintaxe
– Casos de Uso
– IDE Eclipse
– Classes
– Seqüência
• Módulo 2: Orientação a
Objetos com Java I – 4 horas
– Herança
• Módulo 5: Qualidade de
Software I – 4 horas
– Polimorfismo
– Teste
– Associação
– Assertiva de execução
– Delegação
– Collections
• Módulo 6: Orientação a
objetos com Java II – 8 horas
• Módulo 3: Java II – 8 horas
– Padrões de projeto
– Frameworks
– Manipulação de dados
– Persistência JDBC
– Sockets
© LES/PUC-Rio
2
Conteúdo
• Módulo 7: Java III – 12 horas
– Mapeamento OO --> ER
– Persistência Hibernate
• Módulo 8: Desenvolvimento WEB I – 16 horas
– Servlets, JSP, Desenvolvimento de taglibs
– Arquitetura 3 camadas
– MVC básico
• Módulo 9: Desenvolvimento WEB II – 20 horas
– MVC Struts
– Internacionalização
• Módulo 10: Desenvolvimento WEB III – 28 horas
– MVC Spring
– Testes na camada WEB
– Appfuse
© LES/PUC-Rio
3
Manipulação de Dados através
de Fluxos Java
Fluxos de Java: Introdução
• Muitos programas Java interagem com algum repositório
de dados. Por ex., dados podem ser armazenados:
– em arquivos em uma unidade de disco rígido ou CD-ROM;
– nas páginas de um site da Web;
– na memória de um computador;
– em outro programa.
• Em Java, dados são recuperados e armazenados usando um
sistema de comunicação denominado fluxos.
• É necessário criar fluxos de entrada para recuperar dados
e fluxos de saída para armazená-los.
© LES/PUC-Rio
5
Fluxos de Java: Introdução
Para obter dados, um programa abre um stream em uma fonte de dados
(um arquivo, memória, um socket) e lê os dados seqüencialmente.
Similarmente, um programa envia dados para um destino abrindo um
stream associado ao destino e escrevendo nele seqüencialmente os dados.
© LES/PUC-Rio
6
Fluxos de Java: Algoritmos
• Independente da origem e dos tipos de dados, os algoritmos
para recuperação (leitura) e armazenamento (escrita) dos
dados são basicamente os mesmos:
Reading
Writing
open a stream
open a stream
while more information
while more information
read information
write information
close the stream
close the stream
© LES/PUC-Rio
7
Fluxos de Java: Pacote e Hierarquias
• O pacote “java.io” contém uma coleção de classes de fluxos
que dão suporte aos algoritmos para leitura e escrita.
• Portanto, para utilizar fluxos de dados, um programa deve
importar o pacote “java.io”.
• As classes de fluxos são organizadas em duas hierarquias de
classes, baseadas nos tipos de dados (caracteres ou bytes).
© LES/PUC-Rio
8
Fluxos de Byte: Visão Geral
• Usados para leitura e escrita de bytes de 8 bits.
– Podem incluir dados numéricos, programas executáveis,
comunicação na Internet e bytecodes.
– Tipicamente são usados para ler e escrever imagens e sons.
– Na verdade, qualquer tipo de dado imaginável pode ser
(inclusive caracteres!) expresso usando uma série de bytes.
• Descendem das classes “InputStream” e “OutputStream”.
– “InputStream” fornece a API e a implementação parcial para
fluxos de entrada (fluxos para leitura de bytes de 8 bits).
– “OutputStream” fornece a API e a implementação parcial para
fluxos de saída (fluxos para escrita de bytes de 8 bits).
• Por ex., “ObjectInputStream” e “ObjectOutputStream” são
classes usadas para serialização de objetos.
© LES/PUC-Rio
9
Fluxos de Byte: “InputStream”
• Para leitura de arquivos de bytes de 8 bits, usamos a classe
“FileInputStream”.
© LES/PUC-Rio
10
Fluxos de Byte: “OutputStream”
• Para escrita de arquivos de bytes de 8 bits, usamos a classe
“FileOutputStream”.
© LES/PUC-Rio
11
Fluxos de Caractere: Visão Geral
• Usados para leitura e escrita de caracteres de 16 bits.
– Podem tratar caracteres Unicode, ao passo que fluxos de byte
limitam-se ao padrão ISO-Latin-1 (bytes com 8 bits).
– Para o tratamento de dados textuais, incluindo arquivos,
páginas Web e outros tipos comuns de texto, recomenda-se
sempre o uso de fluxos de caratere!
• Descendem das classes “Reader” e “Writer”.
– “Reader” fornece a API e a implementação parcial para fluxos
de entrada (fluxos para leitura de caracteres de 16 bits).
– “Writer” fornece a API e a implementação parcial para fluxos de
saída (fluxos para escrita de caracteres de 16 bits).
© LES/PUC-Rio
12
Fluxos de Caracter: “Reader”
© LES/PUC-Rio
13
Fluxos de Caracter: “Writer”
© LES/PUC-Rio
14
Fluxos de Entrada: Reader e InputStream
• “Reader” e “InputStream” definem APIs similares para
caracteres e bytes, respectivamente.
• Por exemplo, “Reader” contém os seguintes métodos para
leitura de caracteres e arrays de caracteres.
– int read()
– int read(char cbuf[])
– int read(char cbuf[], int offset, int length)
• Da mesma forma, “InputStream” define os mesmos
métodos para a leitura de bytes e arrays de bytes:
– int read()
– int read(byte cbuf[])
– int read(byte cbuf[], int offset, int length)
© LES/PUC-Rio
15
Fluxos de Entrada: Reader e InputStream
• No caso de um fluxo de entrada, o primeiro passo é criar
um objeto que esteja associado à origem de dados.
– Por ex., se a origem for um arquivo em disco rígido, um objeto
“FileInputStream”, poderá ser associado com este arquivo.
• Uma vez obtido o objeto de fluxo de entrada, é possível ler
informações desse fluxo usando métodos do objeto.
– “FileInputStream” inclui um método “read()” que retorna um
byte lido do arquivo.
• Ao acabar de usar os dados de um fluxo, um procedimento
de finalização deve ser chamado.
– “FileInputStream” inclui um método “close()” para indicar que
acabou de usar um arquivo.
© LES/PUC-Rio
16
Fluxos de Saída: Writer e OutputStream
• Similarmente… “Writer” e “OutputStream” definem APIs
similares para caracteres e bytes, respectivamente.
• “Writer” contém os seguintes métodos para escrita de
caracteres e arrays de caracteres.
– int write(int c)
– int write(char cbuf[])
– int write(char cbuf[], int offset, int length)
• “OutputStream” define os mesmos métodos para a escrita
de bytes e arrays de bytes:
– int write(int c)
– int write(byte cbuf[])
– int write(byte cbuf[], int offset, int length)
© LES/PUC-Rio
17
Fluxos de Entrada: Writer e OutputStream
• No caso de um fluxo de saída, é criado primeiro um objeto
associado ao destino dos dados.
– Tal objeto pode ser criado a partir da classe “BufferedWriter”,
que representa um modo eficiente de criar arquivos de texto.
• Os dados são então escritos no objeto associado ao destino.
– O método “BufferedWriter.write()” envia caracteres individuais
para o fluxo de saída.
• Assim como é feito com os fluxos de entrada, o fluxo é
então finalizado.
– O método “BufferedWriter.close()” é chamado em um fluxo de
saída quando não se têm mais informações a enviar.
© LES/PUC-Rio
18
Fluxos de Arquivo: Exemplo “Copy”
import java.io.*;
public class Copy {
public static void main(String[] args) throws IOException {
File inputFile = new File("farrago.txt");
File outputFile = new File("outagain.txt");
FileReader in = new FileReader(inputFile);
FileWriter out = new FileWriter(outputFile);
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();
out.close();
}
}
© LES/PUC-Rio
19
Fluxos de Arquivo: Exemplo “Copy”
1. O programa cria os objetos “File” que representam os
arquivos “farrago.txt” e “outagain.txt” no sistema.
2. O programa cria fluxos “FileReader” e “FileWriter” para os
arquivos “farrago.txt” e “outagain.txt”.
3. O programa lê o “reader” enquanto há caracteres no
arquivo “farrago.txt” e escreve tais caracteres no “writer”.
4. Quando a leitura da entrada termina, o programa fecha o
“reader” e o “writer”.
© LES/PUC-Rio
20
Fluxos de Arquivo: Exemplo “Copy”
• Saída do programa: uma cópia exata de “farrago.txt” em
um arquivo “outagain.txt” no mesmo diretório.
• Portanto, o conteúdo do arquivo “outagain.txt” é:
So she went into the garden to cut a cabbage-leaf, to make an apple-pie; and at the
same time a great she-bear, coming up the street, pops its head into the shop. 'What!
no soap?' So he died, and she very imprudently married the barber; and there were
present the Picninnies, and the Joblillies, and the Garyalies, and the grand
Panjandrum himself, with the little round button at top, and they all fell to playing
the game of catch as catch can, till the gun powder ran out at the heels of their boots.
Samuel Foote 1720-1777
© LES/PUC-Rio
21
Fluxos de Arquivo: Exemplo “CopyBytes”
import java.io.*;
public class CopyBytes {
public static void main(String[] args) throws IOException {
File inputFile = new File("farrago.txt");
File outputFile = new File("outagain.txt");
FileInputStream in = new FileInputStream(inputFile);
FileOutputStream out = new FileOutputStream(outputFile);
int c;
while ((c = in.read()) != -1)
out.write(c);
in.close();
out.close();
}
}
© LES/PUC-Rio
22
import java.io.*;
public class ReadBytes {
Fluxos de Arquivo: Exemplo “ReadBytes”
public static void main(String[] arguments) {
try {
FileInputStream file = new FileInputStream("class.dat");
boolean eof = false;
int count = 0;
while (!eof) {
int input = file.read();
System.out.print(input + " ");
if (input == -1) eof = true;
else count++;
}
file.close();
System.out.println("\nBytes read: " + count);
} catch (IOException e) {
System.out.println("Error -- " + e.toString());
}
}
}
© LES/PUC-Rio
23
Fluxos de Arquivo: Exemplo “ReadBytes”
105 109 112 111 114 116 32 106 97 118 97 46 105 111 46 42 59 13 10 13 10 112 117 98 108 105 99
32 99 108 97 115 115 32 82 101 97 100 66 121 116 101 115 32 123 13 10 32 32 32 32 112 117 98
108 105 99 32 115 116 97 116 105 99 32 118 111 105 100 32 109 97 105 110 40 83 116 114 105 110
103 91 93 32 97 114 103 117 109 101 110 116 115 41 32 123 13 10 32 32 32 32 32 32 32 32 116 114
121 32 123 13 10 32 32 32 32 32 32 32 32 32 32 32 32 70 105 108 101 73 110 112 117 116 83 116
114 101 97 109 32 102 105 108 101 32 61 32 110 101 119 13 10 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 70 105 108 101 73 110 112 117 116 83 116 114 101 97 109 40 34 99 108 97 115 115 46
100 97 116 34 41 59 13 10 32 32 32 32 32 32 32 32 32 32 32 32 98 111 111 108 101 97 110 32 101
111 102 32 61 32 102 97 108 115 101 59 13 10 32 32 32 32 32 32 32 32 32 32 32 32 105 110 116 32
99 111 117 110 116 32 61 32 48 59 13 10 32 32 32 32 32 32 32 32 32 32 32 32 119 104 105 108 101
32 40 33 101 111 102 41 32 123 13 10 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 105 110 116
32 105 110 112 117 116 32 61 32 102 105 108 101 46 114 101 97 100 40 41 59 13 10 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32 32 83 121 115 116 101 109 46 111 117 116 46 112 114 105 110 116
40 105 110 112 117 116 32 43 32 34 32 34 41 59 13 10 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 105 102 32 40 105 110 112 117 116 32 61 61 32 45 49 41 13 10 32 32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 101 111 102 32 61 32 116 114 117 101 59 13 10 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 101 108 115 101 13 10 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32
32 32 32 99 111 117 110 116 43 43 59 13 10 32 32 32 32 32 32 32 32 32 32 32 32 125 13 10 32 32 32
32 32 32 32 32 32 32 32 32 102 105 108 101 46 99 108 111 115 101 40 41 59 13 10 32 32 32 32 32
32 32 32 32 32 32 32 83 121 115 116 101 109 46 111 117 116 46 112 114 105 110 116 108 110 40 34
92 110 66 121 116 101 115 32 114 101 97 100 58 32 34 32 43 32 99 111 117 110 116 41 59 13 10 32
32 32 32 32 32 32 32 125 32 99 97 116 99 104 32 40 73 79 69 120 99 101 112 116 105 111 110 32
101 41 32 123 13 10 32 32 32 32 32 32 32 32 32 32 32 32 83 121 115 116 101 109 46 111 117 116 46
112 114 105 110 116 108 110 40 34 69 114 114 111 114 32 45 45 32 34 32 43 32 101 46 116 111 83
116 114 105 110 103 40 41 41 59 13 10 32 32 32 32 32 32 32 32 125 13 10 32 32 32 32 125 13 10
125 13 10 -1 Bytes read: 717
© LES/PUC-Rio
24
import java.io.*;
Fluxos de Arquivo: Exemplo “WriteBytes”
public class WriteBytes {
public static void main(String[] arguments) {
int[] data = { 71, 73, 70, 56, 57, 97, 15, 0, 15, 0,
128, 0, 0, 255, 255, 255, 0, 0, 0, 44, 0, 0, 0,
0, 15, 0, 15, 0, 0, 2, 33, 132, 127, 161, 200,
185, 205, 84, 128, 241, 81, 35, 175, 155, 26,
228, 254, 105, 33, 102, 121, 165, 201, 145, 169,
154, 142, 172, 116, 162, 240, 90, 197, 5, 0, 59 };
try {
FileOutputStream file = new
FileOutputStream("pic.gif");
for (int i = 0; i < data.length; i++)
file.write(data[i]);
file.close();
} catch (IOException e) {
System.out.println("Error -- " + e.toString());
}
}
}
© LES/PUC-Rio
25
Fluxos de Saída: Saída do Exemplo
© LES/PUC-Rio
26
Fluxos de Seqüência: Exemplo
• O “SequenceInputStream” cria um único fluxo de entrada
a partir de múltiplos fluxos de entrada.
• O programa “Concatenate” usa um SequenceInputStream a
fim de concatenar arquivos seqüencialmente na ordem em
que são especificados na linha de comando.
© LES/PUC-Rio
27
Fluxos de Seqüência: Exemplo
© LES/PUC-Rio
28
Fluxos de Seqüência: Exemplo
• “ListOfFiles” implementa a interface Enumeration.
• Após a criação do SequenceInputStream, o método main lê
do fluxo um byte a cada vez.
• Para ler o InputStream de uma nova fonte,
SequenceInputStream chama o “nextElement()” do objeto
“Enumeration” a fim de obter o próximo InputStream.
© LES/PUC-Rio
29
Fluxos Pipe: Introdução
• Pipes: são usados para fazer um canal entre a saída de
uma thread e a entrada de outra.
• “PipedReader” e “PipedWriter” (caracteres), bem como
“PipedInputStream” e “PipedOutputStream” (bytes),
implementam os componentes de entrada e saída de pipes.
• Por que pipes são úteis?
© LES/PUC-Rio
30
Fluxos Pipe: Exemplo
• Considere uma classe que implementa vários métodos de
manipulação de strings, tais como ordenação e reversão de
caracteres em uma string.
• Seria interessante se a saída de um destes métodos
pudesse ser usada como a entrada para outro de forma que
uma série de chamadas de método pudesse ser usada
como uma função de alto-nível.
• Por ex., se fosse necessário reverter cada palavra em uma
lista, ordenar as palavras e então reverter novamente cada
palavra a fim de criar uma lista de palavras “rhyming”...
© LES/PUC-Rio
31
Fluxos Pipe: Exemplo
• Sem fluxos pipe, o programa teria que armazenar os
resultados em algum lugar (em um arquivo ou na memória)
entre cada passo.
• Com fluxos pipe, a saída de um método poderia ser
“canalizada” para a entrada do próximo método.
© LES/PUC-Rio
32
Fluxos Pipe: Exemplo
© LES/PUC-Rio
33
Fluxos Pipe: Exemplo
© LES/PUC-Rio
34
Fluxos Pipe: Exemplo
© LES/PUC-Rio
35
Fluxos Pipe: Exemplo
© LES/PUC-Rio
36
Fluxos Pipe: Exemplo
• O programa usa “PipedReader” e “PipedWriter” para
conectar a entrada e saída dos métodos reverse e sort a fim
de criar uma lista de palavras “rhyming”.
• A chamada mais interna ao método reverse recebe um
“FileReader”, fluxo aberto no arquivo “words.txt”, que
contém uma lista de palavras.
• O retorno de reverse é passado ao sort, que, por sua vez,
tem seu retorno passado para a chamada mais externa ao
método reverse.
© LES/PUC-Rio
37
Fluxos Pipe: Exemplo
• No método reverse, as linhas de criação dos extremos de
um pipe – um “PipedWriter” e um “PipedReader” – fazem
também o PipedReader “ouvir" o PipedWriter.
• O método reverse inicia um “ReverseThread”, que escreve
sua saída no PipedWriter, mas retorna o PipedReader
(“ouvindo” o PipedWriter!) ao chamador.
© LES/PUC-Rio
38
Fluxos Pipe: Exemplo
• O método reverse contém outros comandos interessantes:
BufferedReader in = new BufferedReader(source); ...
PrintWriter out = new PrintWriter(pipeOut);
• O programa lê de “BufferedReader”, que por sua vez lê de
source, um fluxo “Reader”.
– O programa faz isso para se utilizar do conveniente método
readLine de BufferedReader.
– Similarmente, a escrita do PipedWriter é lida por “PrintWriter”
para que o programa possa usar o conveniente método println.
• A concatenção de fluxos é freqüente, pois permite a
combinação de características dos vários tipos de fluxos.
© LES/PUC-Rio
39
Exercícios
• Usando fluxos de byte (BufferedInputStream) e fluxos de
dados primitivos (DataInputStream) para filtrar dados
(FilterInputStream), implemente um programa
“WritePrimes” que escreve os primeiros 400 números
primos como inteiros em um arquivo chamado
“400primes.dat”.
• Implemente também um programa “ReadPrimes” que lê os
inteiros do arquivo “WritePrimes” e os apresenta.
© LES/PUC-Rio
40
Persistência usando JDBC
Introdução
• JDBC (Java Database Connectivity): biblioteca de classes
Java para conexão com bancos de dados (BDs) relacionais
desenvolvidos por Microsoft, Sybase, Oracle, Informix, etc.
• Driver: ponte para um banco de dado relacional fornecido
por uma empresa.
• Driver JDBC: ponte entre o JDBC e um banco de dado
relacional fornecido por uma empresa.
• Gerenciador de Drivers: controla os drivers exigidos para
o acesso aos registros de BDs, o que permite a
independência de JDBC dos formatos BDs.
© LES/PUC-Rio
42
Introdução
• Usando um driver JDBC como ponte para a fonte de dados,
é possível recuperar e armazenar dados diretamente da
linguagem Java.
• A biblioteca JDBC também um driver especial que a liga a
outro padrão de conectividade de BDs chamado ODBC.
• ODBC: biblioteca da Microsoft para o acesso a BDs SQL; é
gerenciado pelo ODBC Data Source Administrator.
• Ponte JDBC-ODBC: permite a conversão de chamadas
JDBC para chamadas ODBC.
© LES/PUC-Rio
43
Introdução
Start -> Settings -> Control Panel -> ODBC Data Sources
© LES/PUC-Rio
44
Visão Geral
• Usando JDBC (com ou sem ODBC), não há necessidade de
adaptação a formatos específicos de BDs, porque:
– existem drivers que fazerm o acesso direto aos BDs;
– a linguagem SQL (padrão!) é utilizada no código.
• O JDBC inclui classes para cada uma das tarefas que
costumam ser associadas à utilização de BDs:
– estabelecimento de uma conexão com um banco de dados;
– criação de uma consulta usando SQL;
– execução da consulta SQL no banco de dados;
– exibição dos registros resultantes.
• Portanto, classes que usam JDBC seguem o conhecido
modelo de programação com instruções SQL.
© LES/PUC-Rio
45
Visão Geral
• Na configuração de dados, porém, são possíveis duas
alternativas: a ponte JDBC-ODBC ou um driver JDBC.
• Para usar a ponte JDBC-ODBC, é necessário:
– o driver JDBC-ODBC incluído com a linguagem Java 2:
sun.jdbc.odbc.JdbcOdbcDriver;
– um driver ODBC;
– uma fonte de dados ODBC associada ao driver anterior usando
o software ODBC Data Source Administrator.
• Para usar um driver JDBC, é necessário:
– obter e instalar o driver (não está incluído em Java!);
– associar uma fonte de dados ao driver JDBC.
© LES/PUC-Rio
46
Usando JDBC-OBDC: Fontes de Dados
• Todas as fontes de dados ODBC devem receber um nome
descritivo curto.
• Este nome é usado em Java para estabelecer uma conexão
com o banco de dados a que a fonte se refere.
© LES/PUC-Rio
47
Usando JDBC-OBDC: Fontes de Dados
© LES/PUC-Rio
48
Usando JDBC-OBDC: Fontes de Dados
© LES/PUC-Rio
49
Usando JDBC-OBDC: Fontes de Dados
© LES/PUC-Rio
50
Usando JDBC-OBDC: Fontes de Dados
© LES/PUC-Rio
51
Usando JDBC-OBDC: Fontes de Dados
© LES/PUC-Rio
52
Usando JDBC-OBDC: Exemplo
© LES/PUC-Rio
53
Usando JDBC-OBDC: Exemplo
• Entrada do programa: “Poland”
• Saída do programa:
© LES/PUC-Rio
54
Usando um Driver JDBC: Fontes de Dados
• Alguns drivers JDBC estão disponíveis para avaliação.
• O JDataConnectServer da NetDirect está disponível para
download de teste a partir do endereço
– http://www.j-netdirect.com/
• O JDataConnectServer usa o ODBC Data Source
Administrator para criar uma nova fonte de dados associada
a um banco de dados.
© LES/PUC-Rio
55
Usando um Driver JDBC: Exemplo
© LES/PUC-Rio
56
Usando um Driver JDBC: Exemplo
• Antes que o programa seja executado com êxito, o
JDataConnectServer precisa ser iniciado.
• A referência a “localhost:1150” significa que:
– “localhost” é o nome da máquina que atua como servidor (a
máquina local, neste caso);
– “1150” é o número de porta padrão em que o servidor
JDataConnect é executado.
• O JDataConnectServer pode ser usado em servidores
remotos na Internet, de modo que “localhost” poderia ser
substituído por um endereço na Internet.
© LES/PUC-Rio
57
JDBC: Carregando Drivers
• Se a ponte JDBC-ODBC é usada, a seguinte linha de código
carrega o driver:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
• No caso de um driver JDBC, a documentação indica o nome a ser
usado. Se o nome do driver é “jdbc.DriverXYZ”, então a seguinte
linha carrega o driver:
Class.forName("jdbc.DriverXYZ");
• Não é necessário criar uma instância do driver, porque a chamada
“Class.forName” faz isso automaticamente.
• Após o carregamento, o driver está disponível para efetuar
conexões com um BD.
© LES/PUC-Rio
58
JDBC: Estabelecendo a conexão
• A forma geral para o estabelecimento de uma conexão é:
Connection con = DriverManager.getConnection(url,
"myLogin", "myPassword");
• Se a ponte JDBC-ODBC é usada, a url começa por “jdbc:odbc:”. O
resto da url é o nome da fonte de dados. Se uma fonte ODBC
chamada “Fred” é usada, a url deve ser “jdbc:odbc:Fred”.
• Em "myLogin”, é colocado o nome usado para logar no BD; em
“myPassword”, é colocada a senha. Se o nome e a senha são
“Fernanda” e “J8”, as linhas a seguir estabelecem uma conexão:
String url = "jdbc:odbc:Fred"; Connection con =
DriverManager.getConnection(url, "Fernanda", "J8");
© LES/PUC-Rio
59
JDBC: Estabelecendo a conexão
• Se um driver JDBC é usado, a documentação indica qual
subprotocolo usar, isto é, o que colocar após “jdbc:” na url.
Se “acme” é o subprotocolo, a url será “jdbc:acme:”.
• A documentação fornece outras diretivas se necessário. A
última parte da “url” identifica a fonte de dados.
• O retorno do método “DriverManager.getConnection” é uma
conexão aberta para a criação de comandos JDBC, os quais
contêm instruções SQL a serem efetuadas sobre um BD.
© LES/PUC-Rio
60
JDBC: Criando comandos
• Um objeto Statement pode enviar um comando SQL ao
gerenciador de BD. Para criar um objeto Statement, é necessária
uma instância de uma conexão ativa. Na linha a seguir, um objeto
Connection chamado “con” é usado para criar um comando
Statement “stmt”: Statement stmt = con.createStatement();
• Após a criação de um objeto Statement, deve ser executado sobre
ele o comando SQL apropriado. Para um comando SELECT, o
método de Statement usado é “executeQuery”. Para comandos que
criam ou modificam tabelas, o método a usar é “executeUpdate”.
Por exemplo:
createTableCoffees = "CREATE TABLE COFFEES " +
"(COF_NAME VARCHAR(32), SUP_ID INTEGER, PRICE FLOAT, " +
"SALES INTEGER, TOTAL INTEGER)”;
stmt.executeUpdate(createTableCoffees);
© LES/PUC-Rio
61
JDBC: Recuperando valores
• JDBC retorna o resultado de um comando em um objeto
ResultSet. A linha a seguir declara o objeto ResultSet “rs” e
assinala o resultado do comando “stmt” a “rs”: ResultSet
rs = stmt.executeQuery( "SELECT COF_NAME, PRICE
FROM COFFEES");
• O objeto ResultSet “rs” contém linhas de cafés e preços. O
método “next” move o cursor no objeto para a próxima
linha. Sucessivas invocações de “next” movem o cursor uma
linha por vez do início ao fim de “rs”.
© LES/PUC-Rio
62
JDBC: Recuperando valores
• O método getXXX recupera o valor de cada coluna. Por exemplo, a
primeira coluna em cada linha de “rs” é “COF_NAME”, que
armazena um valor de tipo SQL VARCHAR . O método para
recuperação de um VARCHAR é getString. A segunda coluna
armazena um valor de tipo SQL FLOAT. O método para recuperação
de valores FLOAT é getFloat.
• A seguinte linha de código acessa os valores armazenados na linha
corrente de “rs” e imprime cada linha com o nome seguido por três
espaços e o preço.
String query = "SELECT COF_NAME, PRICE FROM COFFEES";
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String s = rs.getString("COF_NAME");
float n = rs.getFloat("PRICE");
System.out.println(s + " " + n);
}
© LES/PUC-Rio
63
JDBC: Resumo
© LES/PUC-Rio
64
Exercícios
• Implemente um programa que usa a biblioteca JDBC para:
– estabelecer uma conexão com uma fonte de dados;
– criar tabelas em um banco de dados associado à fonte;
– inserir dados nos campos das tabelas criadas;
– executar consultas no banco de dados usando SQL;
– exibir os registros recuperados através de consulta.
• Para fazer o exercício, use o tutorial da Sun sobre JDBC.
© LES/PUC-Rio
65
Sockets
Protocolos de Redes
• Computadores na Internet se comunicam uns com os outros
usando ou o “Transmission Control Protocol” (TCP) ou o
“User Datagram Protocol” (UDP).
© LES/PUC-Rio
67
Protocolos em Java: TCP e UDP
• A programação Java se dá na camada de aplicação.
• Tipicamente, não é necessário conhecer detalhes internos
da camada de transporte, que usa TCP e UDP.
• Em vez disso, o programador Java se utiliza das classes
disponíveis no pacote “java.net”, que provêem comunicação
em rede encapsulando detalhes dos protocolos.
• Contudo, para saber quais classes Java usar, é necessário
entender as diferenças entre TCP e UDP.
© LES/PUC-Rio
68
Características do TCP
• Aplicações que precisam se comunicar com confiabilidade:
– estabelecem uma conexão e
– enviam os dados uma à outra através da conexão.
• Isto é análogo ao serviço de chamadas telefônicas. Como
uma companhia telefônica, o TCP garante que:
– os dados enviados a partir de um extremo efetivamente
chegam no outro extremo da conexão e, além disso,
– na mesma ordem em que foram enviados.
• Portanto, fornece um canal ponto a ponto para aplicações
que requerem comunicação confiável.
© LES/PUC-Rio
69
Aplicações TCP
• O “Hypertext Transfer Protocol” (HTTP) e o “File Transfer
Protocol” (FTP) são aplicações que requerem confiabilidade.
– A ordem em que os dados são enviados e recebidos é crítica
para o sucesso dessas aplicações.
– Por exemplo, quando o HTTP é usado para ler de uma URL, os
dados devem ser recebidos na ordem em que são enviados.
– De outra forma, o resultado é um HTML mal formatado, um
arquivo zip corrompido ou outras informações inválidas.
• Portanto, TCP é um protocolo baseado em conexão que
provê um fluxo de dados confiável entre dois computadores.
© LES/PUC-Rio
70
Características do UDP
• Permite comunicação sem confiabilidade entre aplicações.
• UDP não é baseado em conexão como o TCP. Ao contrário,
envia pacotes de dados independentes, chamados
“datagramas”, de uma aplicação à outra.
• Isto é análogo ao serviço de correio: a ordem de entrega
não é importante, pois cada mensagem é independente das
outras. Também não há garantia de entrega.
© LES/PUC-Rio
71
Aplicações UDP
• Um “servidor de relógio” é uma aplicação que envia a hora
atual para os clientes que requisitam tal informação.
– Se o cliente perde um pacote, não faz sentido reenviá-lo.
– A confiabilidade do TCP é desnecessária, porque pode diminuir
a performance e a utilidade do serviço.
• Outro exemplo é o comando “ping”, que testa a
comunicação entre dois programas na rede.
– Ping não se importa de enviar/receber pacotes quebrados ou
fora de ordem para determinar se a conexão é ou não boa.
– Um canal confiável invalidaria este serviço.
• Portanto, UDP é um protocolo que envia pacotes de dados
independentes entre aplicações sem confiabilidade.
© LES/PUC-Rio
72
Conceito de Portas
• Um computador possui uma única conexão “física” em uma
rede. Todos os dados destinados para um computador
chegam através desta conexão.
• Contudo, os dados podem ter sido enviados para aplicações
diferentes. Tais dados são associados a uma aplicação
específica através do uso de “portas”.
• Portanto, os dados têm seus destinos identificados através
do par de identificadores relativos ao computador e à porta.
– O endereço do computador é o seu IP, com 32 bits.
– Uma porta é identificada por um número de 16 bits, usado por
TCP e UDP para entregar dados à aplicação correta.
© LES/PUC-Rio
73
Portas no TCP
• Usando TCP, uma aplicação servidor cria um “socket” para
um número de porta específico.
• “Criar o socket” significa registrar a aplicação servidor no
sistema para receber dados destinados a uma porta através
de uma comunicação baseada em conexão.
© LES/PUC-Rio
74
Portas no UDP
• Usando UDP, o pacote de datagramas contém o número da
porta a que os dados são destinados; o UDP efetua então o
roteamento para entregar o pacote à aplicação correta.
© LES/PUC-Rio
75
Número de Portas
• Números de portas variam de uma faixa de 0 a 65.535
porque as portas são representadas por números de 16 bits.
• Os números de portas variando de 0 a 1023 são restritas;
elas são reservadas para uso de serviços conhecidos como
HTTP e FTP e outros serviços de sistema.
• As aplicações não devem utilizar os números de portas
restritas aos serviços bem conhecidos.
© LES/PUC-Rio
76
Descrição de Sockets
• Um servidor roda em um computador específico e possui um
socket associado a um número de porta específico:
– no lado do servidor, o servidor apenas espera, “ouvindo” no
socket possíveis requisições de clientes;
– no lado do cliente, o cliente sabe o nome da máquina do
servidor e o número da porta na qual o servidor está
conectado. O cliente tenta estabelecer uma conexão através de
uma requisição.
© LES/PUC-Rio
77
Descrição de Sockets
• Se tudo está OK, o servidor aceita a conexão.
– O servidor obtém um novo socket associado à mesma porta.
– Ele usa o novo socket de tal maneira que possa continuar a
“ouvir” no socket original as requisições de clientes enquanto
atende às necessidades do cliente conectado no novo socket.
• Se a conexão é aceita, um socket é criado do lado do cliente
e pode ser usado para a comunicação com o servidor.
© LES/PUC-Rio
78
Descrição de Sockets
• Formalizando: “socket” é um ponto extremo de uma
comunicação bilateral entre dois programas sendo
executados em uma rede.
• Exemplo: implementação de “EchoClient”, que se conecta
ao servidor “Echo”. Este serviço está disponível em uma
rede através da porta 7 e possui a função de receber dados
de um cliente e “enviá-los” de volta.
© LES/PUC-Rio
79
Exemplo de Cliente usando Socket
© LES/PUC-Rio
80
Exemplo de Cliente usando Socket
© LES/PUC-Rio
81
Exemplo de Cliente usando Socket
• EchoClient é um programa “trivial” porque o servidor Echo
implementa um protocolo simples. Programas que ouvem
servidores HTTP são bem mais complexos.
• Contudo, o “esqueleto” básico de um cliente é:
1. abrir um socket;
2. abrir fluxos de entrada e saída no socket;
3. ler e escrever nos fluxos de acordo com protocolo do servidor;
4. fechar os fluxos;
5. fechar o socket.
– Somente o passo 3 difere de cliente para cliente,
dependendo do servidor.
© LES/PUC-Rio
82
Servidor e Cliente usando Sockets
© LES/PUC-Rio
83
Servidor e Cliente usando Sockets
© LES/PUC-Rio
84
Servidor e Cliente usando Sockets
© LES/PUC-Rio
85
Servidor e Cliente usando Sockets
© LES/PUC-Rio
86
Servidor e Cliente usando Sockets
© LES/PUC-Rio
87
Servidor e Cliente usando Sockets
© LES/PUC-Rio
88
Exercícios
• Para manter a simplicidade do exemplo
“KnockKnockServer”, o servidor foi projetado para ouvir e
tratar apenas uma única requisição de conexão.
• Contudo, o servidor deve ser capaz de tratar múltiplas
requisições simultaneamente. Neste caso, deve ser mantida
uma fila de requisições, de tal maneira que o servidor possa
tratá-las seqüencialmente.
• Reimplemente o servidor “KnockKnockServer” para a
escuta e tratamento de múltiplas requisições
simultaneamente (tutorial da Sun: use threads!).
© LES/PUC-Rio
89
Referências
[1] Tutorial da Sun Microsystems.
http://java.sun.com/docs/books/tutorial/java/TOC.html
[2] Cadenhead, Rogers; Lemay, Laura. Java 2: professional
reference. Rio de Janeiro, Campus, 2001.
© LES/PUC-Rio
90
Download

Transparências - (LES) da PUC-Rio