Laboratórios 5, 6, 7 - Servlets Introdução Como já deverão ter noção, uma Servlet é uma classe Java utilizada para estender as capacidades dos servidores web, utilizando para tal um modelo de pedido-resposta. Uma servlet executa num ambiente próprio dentro do servidor, chamado container e pode responder a qualquer tipo de pedido via web. Numa aplicação que funcione neste modelo de pedido-resposta e interacção entre clientes e servidores, foi desenvolvida uma API de servlets que trabalha sobre o protocolo HTTP, bem como haverá outra API para que as servlets possam lidar com pedidos e respostas via protocolos distintos. Os packages Java que contêm a API de Servlets são: 1. javax.servlet 2. javax.servlet.http (para o caso específico do protocolo http) Todas as Servlets têm de implementar a interface Servlet, que define o ciclo de vida de um objecto deste tipo. Para implementar tal interface, podemos escrever uma classe Java que estende de javax.servlet.genericservlet, ou então uma HTTPServlet, que estende de javax.servlet.http.httpservlet. A interface de uma servlet define métodos para inicializar a servlet, para processar pedidos, enviar respostas, remover uma servlet do servidor, etc. Esses métodos definem aquilo a que se chama ciclo de vida de uma servlet e são invocados na sequência seguinte: 1. A servlet é construída, e depois inicializada com o método init 2. Quaisquer chamadas feitas por um cliente são atendidas pelo método service 3. Uma servlet é retirada de execução, destruída com o método destroy, há um processamento de garbage collection (limpeza de memória) e depois é chamado um método finalize Adicionalmente aos métodos do ciclo de vida, estão também disponíveis métodos como o 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 1/11
getservletconfig, que pode ser utilizado para obter informação relativa ao startup da servlet, ou getservletinfo, que permite à servlet retornar informação acerca de si própria, relativa, por exemplo, a autor, copyright e versão. Para mais informação relativa aos métodos disponibilizados por uma GenericServlet ou HttpServlet consultar: 1. http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/genericservlet.html 2. http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/httpservlet.html Para lidar com serviços específicos do protocolo http, a HTTPServlet oferece ainda os métodos doget e dopost. Onde encontrar exemplos de pequenas servlets que poderão ser úteis: http://www.servlets.com/jservlet2/examples/ Dentro da própria instalação do tomcat existem vários servlet examples Leitura de Parâmetros e Cabeçalhos Um pedido pode ser feito ao servidor por Get ou por Post. Através do objecto javax.servlet.httpservletrequest, temos diversos métodos à nossa disposição para obter dados acerca dos cabeçalhos do pedido, nomeadamente: Enumeration getheadernames() String getheader("nome") Enumeration getheaders("nome") - obtém nomes dos cabeçalhos - obtém primeiro valor do cabeçalho - todos os valores do cabeçalho String getparameter(param) - obtém parâmetro HTTP String[] getparametervalues(param)- obtém parâmetros repetidos Enumeration getparameternames() - obtém nomes dos parâmetros Cookie[] getcookies() HttpSession getsession() - recebe cookies do cliente - retorna a sessão setattribute("nome", obj) - define um atributo obj chamado "nome". Object getattribute("nome") - recupera atributo chamado nome String getremoteuser() - obtém utilizador remoto (se autenticado, caso contrário devolve null) No que diz respeito à leitura dos parâmetros que foram enviados, temos disponíveis os seguintes métodos (também no objecto request): 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 2/11
Object getparameter( nome_do_parametro ) - para obter o valor de um parâmetro String[] getparametervalues( nome_do_parametro ) - para obter todos os valores de um parâmetro que possa ter valores múltiplos (por exemplo, uma lista de checkboxes todas com o mesmo nome) Enumeration getparameternames() - para obter uma enumeração dos nomes de todos os parâmetros enviados no pedido. Ao percorrer a enumeração podemos usar getparameter do nome em que estamos a passar para saber qual é o seu valor. Processamento de Respostas Após atender um pedido, a servlet poderá produzir uma resposta a apresentar ao cliente. Essa resposta é gerada através do objecto javax.servlet.httpservletresponse, que nos disponibiliza métodos como: addheader(string nome, String valor) - adiciona cabeçalho HTTP setcontenttype(tipo MIME) - define o tipo MIME que será usado para gerar a saída (text/html, image/gif, etc.) sendredirect(string location) - envia informação de redirecionamento para o cliente (Location: url) Writer getwriter() - obtém um Writer para gerar a saída. Ideal para saída de texto. OutputStream getoutputstream() - obtém um OutputStream. Ideal para gerar formatos diferentes de texto (imagens, etc.) addcookie(cookie c) - adiciona um novo cookie encodeurl(string url) - envia como anexo da URL a informação de identificador de sessão (sessionid) reset() - limpa toda a saída inclusive os cabeçalhos resetbuffer() - limpa toda a saída, excepto cabeçalhos Sessões O protocolo HTTP trata de cada pedido de forma individual e nunca se partilha informação relativa a pedidos distintos, pois não mantém estado. A manutenção de estado numa aplicação web, ou seja, a partilha de informação que dura ao longo de diversos pedidos, pode ser simulada com recurso ao conceito de sessão, implementado através de: 1. Cookies 2. Campos escondidos em formulários HTML 3. Re-escrita de URLs Cada um destes métodos tem vantagens e desvantagens, daí que há que ponderar qual deles se poderá aplicar a uma situação específica. 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 3/11
Independentemente do método escolhido, temos à nossa disponibilização uma API na qual existe uma interface HTTPSession, que define os métodos que devem executar as seguintes tarefas de forma transparente: 1. Gera ids únicos 2. Mantém informações de todas as sessões actuais 3. Associa pedidos com suas respectivas sessões 4. Utiliza o método de rastreamento disponível para cada utilizar: cookies se estiver disponível, ou reescrita de URLs 5. Possibilita o armazenamento de objectos na sessão 6. Descarta sessões completas ou abandonadas Através de um objecto da classe HTTPServletRequest, podemos invocar um método getsession para obter uma sessão. Este método recebe um parâmetro do tipo booleano. Se tiver o valor true, retorna a sessão existente. Se não existir nenhuma, cria uma nova. Se o valor do parâmetro dado for false, retorna a sessão existente. Caso não existir, o resultado é null. Para saber se uma sessão é nova, podemos sempre recorrer ao método isnew. Resumo dos métodos de javax.servlet.http.httpsession: 1. getattribute: Extrai um valor previamente armazenado de uma sessão. Retorna null se não existir um valor associado com o nome. 2. SetAttribute: Associa um valor com um nome. 3. RemoveAttribute: Remove todos os valores associado com um nome. 4. GetAttributeNames: Retorna os nomes de todos os atributos da sessão. 5. GetId: Retorna o identificar, único, associado com a sessão. 6. IsNew: Verifica se a sessão é nova 7. getcreationtime: Retorna a hora em que a sessão foi criada 8. getlastaccessedtime: Retorna a hora do último acesso pelo cliente 9. getmaxinactiveinterval, setmaxinactiveinterval : Obtém ou define o tempo máximo de espera para que uma sessão seja considerada inválida 10. invalidate: Invalida uma sessão e liberta todos os objectos associados com ela RequestDispatcher A partir do nosso pedido (um objecto do tipo HTTPServletRequest), podemos invocar um método chamado getrequestdispatcher, que nos permite fazer duas coisas: 1. include 2. forward Um include, tipicamente, serve para invocar uma outra servlet, obter a resposta da mesma e incluir essa resposta/resultado na resposta que estamos a produzir para o pedido original. 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 4/11
Tudo sem que o cliente se aperceba. Um forward, serve para passar o pedido que nos foi feito a outra servlet que o irá processar de forma conveniente. No objecto response também podemos fazer algo semelhante a um forward, invocando o método sendredirect, ao qual damos a página de destino. A diferença entre os dois é que o sendredirect perde por completo a sessão e dados do pedido anterior, é como se fosse um novo pedido efectuado pelo browser do cliente. No caso do forward, feito através de um requestdispatcher, podemos perfeitamente manter a sessão e os dados do pedido original para posterior processamento. Contexto Um contexto define, no fundo, uma aplicação web. O servidor (no nosso caso, o tomcat), representa o papel de um web-container, no qual podemos instalar múltiplas aplicações web, ou seja, múltiplos contextos. Cada um executa em separado, como será de esperar. O contexto colocado em execução é aquele para o qual esteja definido um tipo de pedido idêntico ao submetido pelo cliente. Dentro do tomcat, como já deverão ter tomado conhecimento, existe uma directoria chamada webapps. Esta é a directoria pública do servidor web, na qual estão instalados todos os contextos/aplicações. Vamos supor que definimos um contexto para uma aplicação chamado MinhaApp. No eclipse, desenvolvemos todos os aspectos relacionados com MinhaApp, respeitando algumas regras: 1. Na raíz (directoria principal) de MinhaApp, estão colocadas as páginas a disponibilizar. São de acesso público. 2. Dentro de uma directoria images, colocaremos imagens que podemos utilizar nas nossas páginas 3. Dentro de uma directoria style, colocaremos todas as CSS que pretendemos utilizar na nossa aplicação 4. Dentro de uma directoria scritps, podemos colocar todos os ficheiros.js que vamos utilizar, contendo funções javascript 5. Deve existir uma directoria de nome WEB-INF dentro do nosso contexto. Esta é de acesso privado, e contém: 1. uma directoria chamada classes com o resultado da compilação de todas as classes que desenvolvemos para a aplicação 2. uma directoria chamada lib onde podemos colocar bibliotecas que estejam a ser utilizadas pelas nossas classes 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 5/11
3. um ficheiro de configuração chamado web.xml que define, entre outras coisas, qual (ou quais) são as servlets que estão a atender pedidos, quanto tempo pode durar uma sessão, qual o padrão (a forma como devem ser escritos) os URLs dos pedidos feitos à nossa aplicação, etc, etc. Posteriormente veremos que há ainda mais informação que pode (e deve) ser colocada na zona de acesso privado de uma aplicação web. Esta zona é acedida apenas pelo servidor, para avaliar não só a configuração a seguir, mas também onde está o código que deve executar. O aspecto de uma aplicação web (em termos de estrutura) será mais ou menos o seguinte: Exemplo de um ficheiro de configuração (web.xml): <web-app> <context-param> <param-name>tempdir</param-name> <!-- parametro que pode ser lido por --> <param-value>/tmp</param-value> <!-- todos os componentes --> 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 6/11
</context-param> <servlet> <servlet-name>myservlet</servlet-name> <!-- instancia de uma servlet --> <!-- que vai processar pedidos --> <!-- nome e package + classe --> <!-- seguidos de parametros --> <!-- e outros dados de inic. --> <servlet-class>exemplo.pacote.myservlet</servlet-class> <init-param> <param-name>datafile</param-name> <param-value>data/data.txt</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> Através da API das servlets podemos ter acesso ao contexto da aplicação que estamos a implementar. Para isso utilizamos o método getservletcontext(), a partir do qual podemos: 1. Aceder a parâmetros de inicialização da aplicação 2. Aceder a atributos definidos na aplicação, logo partilhado por todas as servlets de uma aplicação 3. Aceder ao resgisto de ocorrencias do servidor 4. Aceder a informação especifica do servidor 5. Possibilita realizar operação de inclusão de uma Servlet ou de redirecionamento do processamento para outra Servlet Esta informação pode ser obtida através do objecto ServletContext recorrendo aos métodos: 1. String getinitparameter(string name) 2. Object GetAttribute(String name) 3. Void setattribute(string name, Object value) 4. Void log(string message) 5. String getserverinfo() 6. RequestDispacher getrequestdispacher() Deploy de uma aplicação web no tomcat Há várias formas de publicar (fazer deploy) de uma aplicação no tomcat. Uma delas e, talvez, a mais simples, é fazer um ficheiro zip com toda a estrutura da aplicação, desde a raíz do contexto. Mudar a extensão do ficheiro para war (Web ARchive) e copiar o ficheiro para dentro da directoria webapps do tomcat. Faz-se restart ao tomcat e o ficheiro terá sido descomprimido e a aplicação está pronta a executar. Outra forma, é usar o próprio IDE (ambiente de desenvolvimento) para gerar um ficheiro WAR que vamos colocar dentro da directoria webapps. Ainda uma outra forma, é ter o contexto definido num path criado por nós em qualquer 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 7/11
parte do disco, criar um ficheiro com um context (como explicado no laboratório 1) e sempre que modificarmos alguma coisa na aplicação basta fazer restart do tomcat que as actualizações relativas a qualquer modificação na aplicação serão feitas por ele próprio no arranque. Nota: O tomcat tem uma directoria chamada work, dentro da qual mantém uma cache para cada aplicação. Quando fazemos alterações a uma aplicação podemos sempre ir a esta directoria work (com o tomcat parado), apagar a cache relativa à mesma e fazer start novamente para garantir que não estamos a ver versões antigas da mesma página. Trabalho de Laboratório O objectivo deste trabalho, será pôr em prática os conhecimentos adquiridos no que diz respeito às servlets nas aulas teóricas e práticas. O nosso objectivo agora, será, para além de publicar as páginas já construídas, colocar o servidor web a responder aos pedidos dos clientes, efectuando todo o pós-processamento envolvido na submissão de dados através de um formulário. No entanto, passaremos por alguns exercícios adicionais. Nota: Deverão ter duas versões do mesmo formulário, uma que envie os dados por Get e outra que envie os dados por Post, para que possam avaliar do lado da servlet a forma como os pedidos irão ser atendidos. A arquitectura geral a utilizar será a seguinte: Sendo que, de momento, não iremos utilizar nenhuma BD. Iremos ainda exercitar os conceitos de sessão, contexto, inclusão e reencaminhamento de pedidos, upload de ficheiros e envio de ficheiros binários. 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 8/11
Para cada exercício, fazer o respectivo deploy da aplicação e testá-la antes de avançar para o próximo. Cada grupo tem uma semana para resolver os exercícios de cada um dos laboratórios aqui propostos. Significa que têm até ao início da aula seguinte para entregar o trabalho desenvolvido. Laboratório 5 Sempre dentro do mesmo contexto (ie, da mesma aplicação), vamos colocar em prática os seguintes exercícios: 1. Criar uma servlet que faça um helloworld No ficheiro web.xml, definir o servlet-name para esta servlet com o valor hi. O nome da classe deverá ser HelloWorld Para esta servlet, definir os seguintes url-pattern (para diferentes servletmappings) no web.xml para o servlet-name hi 1. /hello.html 2. *.hello 3. /hello/* 2. Criar uma servlet com um contador, sendo que de cada vez que é efectuado um pedido nos é mostrado como resultado o número de pedidos feitos até ao momento 3. Criar uma página com um formulário contendo um input do tipo text onde o utilizador escreve o seu primeiro nome e um botão de submit. A submissão de um pedido neste formulário deve ser dirigida a uma servlet chamada Hello, que obtém o nome do utilizador e o cumprimenta com uma mensagem de boas vindas (eg Olá João ). Utilizar o método POST como método de submissão de dados do form. 4. Repetir o exercício anterior utilizando GET como método de submissão de dados 5. Criar uma servlet que gera um formulário com um grupo de checkboxes todas com o mesmo nome mas com valores diferentes. Ao atender o pedido, deverá mostrar na página de resultado quais os nomes e valores das checkboxes que tenham sido seleccionadas. Se nenhuma o foi, escrever a mensagem Não foram seleccionados dados. 6. Para a página já desenvolvida em laboratórios anteriores, criar uma servlet que no processamento do pedido se encarregue de: Ler os cabeçalhos da página Leitura dos parâmetros (dados) enviados pelo cliente Validar esses mesmos dados (em termos de valor e tipo) Mostrar os dados obtidos na página de resultado dentro de uma tabela (nomes e valores) 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 9/11
Laboratório 6 Este trabalho de laboratório consistirá no seguinte: 1. Com base no pequeno exemplo fornecido nos links que vos foram dados acerca da API para fazer uploads da apache: 1. Ler o pequeno guia 2. Fazer download da API 3. Usar o exemplo para construir uma servlet que gera o html com um form para fazer upload de um ficheiro e que uma vez que um ficheiro seja submetido, o copie na íntegra para o servidor 4. Verificar se o ficheiro gravado no servidor é exactamente igual ao original que está no cliente 2. Criar uma servlet que apresenta uma lista de ficheiros de uma directoria à vossa escolha. Ao clickar no link para um dos ficheiros apresentados, a servlet deverá criar todos os headers necessários e efectuar o respectivo processamento para enviar o ficheiro para o cliente. Se o link onde se clickou for uma directoria, a servlet deverá responder com a lista dos ficheiros dessa directoria. O objectivo é aplicar este processamento a ficheiros binários (pdf, imagens, executáveis, etc). Simular o que acontece quando requisitamos um download via http. Laboratório 7 Na sequência dos trabalhos dos laboratórios anteriores iremos agora passar à realização de exercícios que envolvem a configuração de uma servlet utilizando parâmetros de inicialização, redireccionamento e inclusão de pedidos. Vamos ainda elaborar alguns testes de carga sobre uma aplicação web. 1. A partir do primeiro exercício do laboratório 5, criar uma nova servlet que, para além de contar o número de vezes que já foi acedida, mostra no resultado o número de instâncias dessa servlet criadas pelo servidor e o total de vezes que cada uma delas foi acedida. 2. Ainda a partir do exercício 2 do laboratório 5, criar uma servlet que para além de contar o número de acessos lê um parâmetro de inicialização que diz a partir de que número deve começar a contar. 3. Criar uma servlet que conta o número de acessos e que no método destroy grava esse número para um ficheiro por forma a tornar a informação gerada persistente. 4. Criar uma servlet que gera (no método Get) dois formulários com um botão de submit cada um. Um deles submete um campo (que pode estar escondido) com o valor 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 10/11
incluir e o outro submete um campo com o valor redireccionar. No método que atende o POST, deverá identificar que pedido foi feito e responder de forma adequada. Ou seja, se foi submetido o valor re-direccionar, devem re-direccionar o pedido para uma segunda servlet, que responde mostrando uma mensagem alusiva ao acontecimento. Se foi submetido o valor incluir, no processamento da primeira servlet devem incluir a resposta da segunda, com uma mensagem igualmente alusiva ao processamento efectuado. 2005 ESTSetúbal Susana Cabaço - Computação na Internet 2005/2006 Página 11/11