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