Prática 03 - WebService (NetBeans)

Transcrição

Prática 03 - WebService (NetBeans)
Prática em Laboratório N.03
Passando dados binários para um serviço Web
Observação: Você não pode utilizar o GlassFish v3 para essa prática pois ele não reconhece o atributo
wsdlLocation para serviços Web.
Passando dados binários para um Serviço Web
O objetivo desta prática é criar um cliente usando Java Swing que irá consumir (e exibir) as imagens de um
módulo EJB (Enterprise Java Bean) através de um serviço Web.
Obtenha os arquivos da Prática N.03
Você deve recuperar os arquivos relacionados a essa prática a partir do Moodle.
1.
Crie um diretório vazio no disco local (por exemplo, com o nome Pratica3).
2.
Acesse o Moodle e salve todos os arquivos da Prática N.03 no diretório criado.
Criando um módulo EJB
Realize os seguintes passos para criar um Enterprise Java Bean (EJB) que contém as imagens que serão
passadas ao cliente através de um serviço Web como dados binários.
1.
Escolha Arquivo > Novo Projeto (Ctrl-Shift-N). Selecione Módulo EJB dentro da categoria Java EE e
clique em Próximo.
2.
Nomeie o projeto FlowerAlbum e clique em Próximo.
3.
Selecione o servidor GlassFish V2 e clique em Finalizar.
Criando um serviço Web a partir de uma classe Java
1.
Clique com o botão direito do mouse no nó FlowerAlbum e escolha Novo > Bean de Sessão.
2.
Chame o bean de sessão de Flower e digite flower.album no Pacote. Selecione o tipo de sessão
como Sem estado(Stateless)e crie apenas a interface Remota. Clique em Finalizar.
A IDE adiciona um bean de sessão ao nó Pacotes de código-fonte, junto com a interface remota. No nó
Enterprise Beans, um novo elemento é adicionado com nome de Flower.
4.
Selecione o elemento Flower com o botão direto do mouse e escolha Adicionar > Método de negócio.
5.
Digite os seguintes dados no diálogo de Método de negócio:
o
Nome: getFlower
o
Tipo de retorno: byte[]
Clique em Adicionar. Digite name em Nome. Em seguida, clique na aba Exceções e selecione Adicionar.
6.
O diálogo Localizar tipo srá exibido. Digite IO selecione a exceção IOException (java.io).
Clique em OK para retornar ao diálogo de Método de negócio, que exibe a exceção IOException.
Clique em OK. Agora você já possui um esboço de método na classe que implementa o bean, além da
declaração do método na interface remota.
7.
Chame novamente o diálogo de Método de negócio. Desta vez, ente com os seguintes dados:
o
Nome: allFlowers
o
Tipo de retorno: List<byte[]>
Como antes, adicione a exceção IOException usando a aba Exceções.
8.
Observe que na interface remota os métodos gerados anteriormente foram declarados com sucesso.
@Remote
public interface FlowerRemote {
byte[] getFlower(String name) throws IOException;
List<byte[]> allFlowers() throws IOException;
}
Verifique que na classe que implementa o bean os stubs para os métodos também foram gerados.
@Stateless
public class FlowerBean implements FlowerRemote {
public byte[] getFlower(String name) throws IOException {
return null;
}
public List<byte[]> allFlowers() throws IOException {
return null;
}
}
Alternativamente, ao invés de utilizar o diálogo de Método de negócio, você poderia adicionar
manualmente os códigos tanto à classe do bean quanto à interface remota. Entretanto, quando o
diálogo é utilizado, a IDE adiciona código em ambos simultaneamente.
9.
Ajuste as importações na classe do bean e na interface remota. Posione o cursor em qualquer lugar do
código, clique com o botão direito do mouse e selecione Corrigir importações. Um diálogo aparece
exibindo todas as importações necessárias. Selecione OK. Caso você tenha opção de selecionar uma
entre várias classes List para importar, selecione java.util.List.
Para Flower.java, as instruções de importação são:
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import javax.ejb.Stateless;
Para FlowerRemote.java, as instruções de importação são:
import java.io.IOException;
import java.util.List;
import javax.ejb.Remote;
10. You have now declared your methods in the remote interface and implemented stubs in the bean class.
The Projects window now shows two new nodes in the Enterprise Beans node, for your new methods,
as shown here.
11. Preencha a classe do bean com o seguinte código.
@Stateless
public class Flower implements FlowerRemote {
private static final String[] FLOWERS = {"aster", "honeysuckle", "rose", "sunflower"};
public byte[] getFlower(String name) throws IOException {
URL resource = this.getClass().getResource("/flower/album/resources/"+name+".jpg");
return getBytes(resource);
}
public List<byte[]> allFlowers() throws IOException {
List<byte[]> flowers = new ArrayList<byte[]>();
for (String flower:FLOWERS) {
URL resource =
this.getClass().getResource("/flower/album/resources/"+flower+".jpg");
flowers.add(getBytes(resource));
}
return flowers;
}
private byte[] getBytes(URL resource) throws IOException {
InputStream in = resource.openStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for(int read; (read = in.read(buf)) != -1;) {
bos.write(buf, 0, read);
}
return bos.toByteArray();
}
}
12. Selecione o nó correspondente ao pacote flower.album e crie um novo subpacote chamado
resources (clique com o botão direito e selecione Novo > Pacote Java). Copie os seguintes arquivos
de imagens para o pacote:
o
rose.jpg
o
sunflower.jpg
o
aster.jpg
o
honeysuckle.jpg
Na janela de Projeto, as imagens devem aparecer da seguinte forma:
Em seu código, observe que os métodos getFlower e allFlowers utilizam as imagens armazenadas
neste pacote.
Seu módulo EJB está completo! Em seguida, você irá criar um serviço Web para acessar o módulo EJB, de
forma a recupara as imagens.
Criando um serviço Web
O serviço Web que será gerado irá utilizar o módulo EJB criado previamente para recupara dados binários (isto
é, as imagens).
1.
Escolha Arquivo > Novo projeto (Ctrl-Shift-N). Selecione aplicação Web na categoria Java Web.
2.
Nomeie o projeto FlowerService e clique em Próximo.
3.
Selecione o servidor GlassFish V2 e clique em Finalizar.
4.
Coloque o módulo EJB no classpath do projeto, dessa forma o serviço Web irá conseguir ter acesso ao
módulo. Para tanto, clique com o botão direito sobre o nó Bibliotecas e selecione Adicionar projeto.
Localize e selecione o FlowerAlbum.
5.
Clique com o botão direito do mouse no nó FlowerService e escolha Novo > Serviço Web.
6.
Chame o serviço Web de FlowerService e digite flower.album no Pacote.
7.
Selecione Criar serviço Web a partir do Bean de sessão existente. Clique em Procurar para
localizar e selecionar o módulo EJB Flower. Em seguida, clique em OK e Finalizar.
8.
Clique em no botão Projeto no canto superior esquerdo do editor. O Visual Designer será exibido.
9.
Clique no botão Código-fonte para retornar a visão do código. Altere o código classe de forma que ela
fique com o seguinte conteúdo.
import java.awt.Image;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ejb.EJB;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService(serviceName = "FlowerService")
public class FlowerService {
@EJB
private FlowerRemote ejbRef;
@WebMethod(operationName = "getFlower")
public Image getFlower(String name) throws IOException {
byte[] bytes = ejbRef.getFlower(name);
return getImage(bytes, false);
}
@WebMethod(operationName = "allFlowers")
public List<Image> allFlowers() throws IOException {
List<byte[]> flowers = ejbRef.allFlowers();
List<Image> flowerList = new ArrayList<Image>(flowers.size());
for (byte[] flower : flowers) {
flowerList.add(getImage(flower, true));
}
return flowerList;
}
private Image getImage(byte[] bytes, boolean isThumbnail) throws IOException{
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
Iterator readers = ImageIO.getImageReadersByFormatName("jpeg");
ImageReader reader = (ImageReader) readers.next();
Object source = bis; // File or InputStream
ImageInputStream iis = ImageIO.createImageInputStream(source);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
if (isThumbnail) {
param.setSourceSubsampling(4, 4, 0, 0);
}
return reader.read(0, param);
}
}
O serviço Web está completo, delegating to the EJB module, and exposing its images.
Testado o serviço Web
Agora, você irá criar uma aplicação contendo ambos componente (o módulo EJB e o serviço Web) que foram
criados anteriormente. Após a implantação dessa aplicação, você poderá testar o serviço Web.
1.
Escolha Arquivo> Novo Projeto (Ctrl-Shift-N). Selecione Aplicativo corporativo dentro da categoria Java
EE.
2.
Nomeie o projeto FlowerApplication e clique em Próximo.
3.
Selecione o servidor GlassFish V2 e antes de clicar em Finalizar, assegure-se de desmarcar as
opções Criar módulo EJB e Criar módulo de aplicativo Web, uma vez que você ira utilizar os
módulos criados anteriormente.
4.
Clique com o botão direito em no nó Módulos Java EE de FlowerApplication e selecione Adicionar
Módulo Java EE. Localize e selecione ambos (usado tecla shift) os módulos criados anteriormente
FlowerAlbum e FlowerService. Você irá observar que os dois módulos são adicionados a aplicação.
5.
Clique com o botão direito em FlowerApplication e selecione Propriedades. Selecione a categoria
Executar. Digite /FlowerService?Tester no campo URL relativa e clique em OK.
6.
Clique com o botão direito em FlowerApplication e selecione Executar. O servidor ao tiver ativa, a
IDE irá inicializá-lo. Em seguida a aplicação (com os 2 módulos) será implantada no servidor.
Finalmente, em virtude da configuração anterior de URL relativa, o navegador ser ativado e a aplicação
Tester executada.
Assim que a aplicação Tester estiver ativa, retorne a janela do Netbeans (sem fechar o navegador) e
selecionar a aba (janela) de Serviços da IDE, expanda o nó Servidores > GlassFish V2 > Aplicativos >
Aplicativos empresariais, e observe que a aplicação FlowerApplication foi implantada com sucesso.
7.
Clique em WSDL File na aplicação Tester e observe o documento WSDL.
8.
O documento WSDL no navegador exibe várias informações entre as quais está a localização do
esquema. Digite ou copie/cole a URL do esquema no navegador para poder visualizá-lo.
9.
Retorne a aplicação Tester. Digite o nome de uma das imagens, por exemplo, "rose" e clique no botão
getFlower, você irá observar informações sobre a invocação do serviço.
Quando você examinar o que foi retornado (em "Method Returned"), verá apenas “lixo”. Você deseja
na verdade obter uma imagem, e não uma série de símbolos desconexos. Entretanto, como
java.awt.Image não é um tipo de esquema XML válido, é necessário que se configure manualmente
o arquivo do esquema para retornar dados binários (image/jpeg).
Modificando o Esquema e o Documento WSDL para Dados Binários
A seguir, você irá modificar o documento WSDL e o esquema XML do serviço Web criado anteriormente. Essa
modificação irá permitir que o serviço Web e seus clientes troquem imgens no formato JPEG.
1.
Na janela de Projetos, expanda o nó da aplicação para Web FlowerService até que você localize o
nó WEB-INF.
2.
Clique com o botão direito no nó WEB-INF e selecione Novo > Outro > Diretório. Nomeie o diretório
como wsdl.
3.
Expanda o nó Serviços Web e clique com botão direito em FlowerService. Selecione Gerar e Copiar
WSDL. Localize FlowerService > web > WEB-INF > wsdl e clique OK.
Você irá observar as cópias FlowerService.wsdl e FlowerService_schema1.xsd que aparecem
no nó wsdl.
4.
Você deve explicitamente configurar o servidor para utilizar sua nova versão do documento WSDL, caso
contrário o servidor de aplicações irá gerar um documento WSDL próprio quando o serviço for
implantado.
Abra
FlowerService.java
e
localize
a
anotação
@WebService.
Adicione
a
essaanotação o parâmetro wsdlLocation="WEB-INF/wsdl/FlowerService.wsdl" como pode ser
visto abaixo:
@WebService(serviceName = "FlowerService",
wsdlLocation = "WEB-INF/wsdl/FlowerService.wsdl")
5.
Modifique o esquema de modo que ele especifique o tipo esperado do conteúdo que será retornado.
Abra
o
arquivo
do
esquema
e
localize
os
tipo
complexos
allFlowersResponse
getFlowerResponse:
<xs:complexType name="allFlowersResponse">
<xs:sequence>
<xs:element name="return" type="xs:base64Binary" minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getFlowerResponse">
<xs:sequence>
<xs:element name="return" type="xs:base64Binary" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
e
6.
Adicione
os
seguintes
atributos
a
ambos
os
elementos
de
retorno
(<xs:element
name="return".../>):.
xmime:expectedContentTypes="image/jpeg"
xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
Ao final, os tipos complexos deverão ter a seguinte declaração.
<xs:complexType name="allFlowersResponse">
<xs:sequence>
<xs:element name="return" type="xs:base64Binary" minOccurs="0"
maxOccurs="unbounded"
xmime:expectedContentTypes="image/jpeg"
xmlns:xmime="http://www.w3.org/2005/05/xmlmime"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="getFlowerResponse">
<xs:sequence>
<xs:element name="return" type="xs:base64Binary" minOccurs="0"
xmime:expectedContentTypes="image/jpeg"
xmlns:xmime="http://www.w3.org/2005/05/xmlmime"/>
</xs:sequence>
</xs:complexType>
7.
Agora, quando você reimplantar a aplicação FlowerApplication e executar Tester, a invocação da
operação getFlower irá retornar as informações corretamente como abaixo:
Agora que a aplicação Tester confirmou que as imagens estão sendo corretamente retornadas, podemos criar
um cliente Swing para recuperá-las e exibi-las.
Criando um cliente Swing
1.
Escolha Arquivo > Novo Projeto (Ctrl-Shift-N). Selecione Aplicativo Java dentro da categoria Java.
Digite FlowerClient em Nome do Projeto e clique em Finalizar.
Clique com o botão direito em FlowerClient e selecione Novo > Cliente para serviço Web. Selecione
2.
o botão de rádio WSDL URL e digite a URL do documento WSDL associado ao serviço criado, por default
http://localhost:8080/FlowerService/FlowerService?WSDL. Clique em Finalizar.
Clique com o botão direito novamente em FlowerClient e selecione Novo > Formulário JFrame.
3.
Nomeie o frame como FlowerFrame. Coloque-o no pacote flowerclient.
Adicione um JPanel ao FlowerFrame. Expanda-o para preencher o FlowerFrame completamente.
4.
Nomeie o painel como gardenFlowersPanel.
Adicione os seguintes componentes ao painel gardenFlowersPanel, iniciando da parte superior do
5.
painel (ver exemplo abaixo).
o
Rótulo. nome da variável: titleLabel, texto: Flores do Jardim, posição: Centrada no topo.
Coloque o texto em negrito e aumente o tamanho da fonte.
o
Grupo de botões. Nome da variável: buttonGroup1.
o
Adicione os seguintes 04 botões de opção em uma linha horizontal logo abaixo de titleLabel. Na
propriedade buttonGroup de cada botão, colocá-lo como membro de buttonGroup1.
Nome da Variável
Selecionado Texto
asterRadioButton
true
Aster
honeysuckleRadioButton
false
Honeysuckle
roseRadioButton
false
Rose
sunflowerRadioButton
false
Sunflower
o
Painel de rolagem. nome da variável: mainScrollPane. posição: abaixo dos botões de opção,
ocupando todo o espaço horizontal e cerca de 2/3 do restante do espaço vertical. Dentro dele:

Painel. nome da variável: mainPanel. layout: Borda. posição: Filling the mainScrollPane.

Botão. nome da variável: mainPictureButton. texto: Aguardando Imagem... posição:
dentro de mainPanel (devido ao layout Border do painel, o botão irá automaticamente preencher
todo o espaço)
o
Painel
de
rolagem.
nome
da
variável:
thumbnailScrollPane.
posição:
Abaixo
de
mainScrollPane, preenchendo o espaço horizontal e o restante do espaço vertical. Dentro dele:

Painel.
nome
da
variável:
thumbnailPanel. layout: Grade. posição: Preenchendo o
thumbnailScrollPane. O Layout Grade significa que os 04 botões seguintes terão o mesmo
tamanho e preencherão completamente o painel thumbnailPanel.

Botões. Adicione 04 botões dentro do painel thumbnailPanel usando a seguintes informações
Nome da Variável Texto
asterButton
Aguandando...
honeysuckleButton
Aguandando...
roseButton
Aguandando...
sunflowerButton
Aguandando...
6.
Este ponto, o frame FlowerFrame deve parece com a seguinte janela.
7.
No editor de código, inicialize o frame FlowerFrame da seguinte forma:
public
static
"sunflower"};
final
String[]
FLOWERS
=
{"aster",
"honeysuckle",
"rose",
private Map<String, Image> flowers;
public FlowerFrame(Map<String, Image> flowers) {
this.flowers = flowers;
for (String flower:FLOWERS) {
flowers.put(flower,null);
}
initComponents();
setTitle("Flores do Jardim [aguardando imagem]");
ItemListener rbListener = new RBListener();
asterRadioButton.addItemListener(rbListener);
honeysuckleRadioButton.addItemListener(rbListener);
roseRadioButton.addItemListener(rbListener);
sunflowerRadioButton.addItemListener(rbListener);
ActionListener bListener = new ButtonListener();
asterButton.addActionListener(bListener);
honeysuckleButton.addActionListener(bListener);
roseButton.addActionListener(bListener);
sunflowerButton.addActionListener(bListener);
}
8.
Quando um botão de opção for selecionado, deseja-se que uma nova imagem seja exibida no botão
principal.
private class RBListener implements ItemListener {
public void itemStateChanged(ItemEvent e) {
showFlower();
}
}
public void showFlower() {
Image img = null;
if (asterRadioButton.isSelected()) {
img = flowers.get("aster");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Flores do Jardim [Aster]");
}
} else if (honeysuckleRadioButton.isSelected()) {
img = flowers.get("honeysuckle");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Flores do Jardim [Honeysuckle]");
}
} else if (roseRadioButton.isSelected()) {
img = flowers.get("rose");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Flores do Jardim [Rose]");
}
} else if (sunflowerRadioButton.isSelected()) {
img = flowers.get("sunflower");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Flores do Jardim [Sunflower]");
}
}
if (img == null) {
mainPictureButton.setIcon(null);
setTitle("Flores do Jardim [aguardando imagem]");
} else mainPictureButton.setText("");
}
9.
Quando um dos botões da parte inferior da janela for selecionado, o botão de opção correspondente
será ativado:
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == asterButton)
asterRadioButton.setSelected(true);
else if (e.getSource() == honeysuckleButton)
honeysuckleRadioButton.setSelected(true);
else if (e.getSource() == roseButton)
roseRadioButton.setSelected(true);
else if (e.getSource() == sunflowerButton)
sunflowerRadioButton.setSelected(true);
}
}
10. Na classe principal Main, o método setThumbnails irá ser chamado, porém ele deve ser declarado
em FlowerFrame.
public void setThumbnails(Map<String, Image> thumbs) {
Image img = thumbs.get("aster");
if (img != null) {
asterButton.setIcon(new ImageIcon(img));
asterButton.setText("");
}
img = thumbs.get("honeysuckle");
if (img != null) {
honeysuckleButton.setIcon(new ImageIcon(img));
honeysuckleButton.setText("");
}
img = thumbs.get("rose");
if (img != null) {
roseButton.setIcon(new ImageIcon(img));
roseButton.setText("");
}
img = thumbs.get("sunflower");
if (img != null) {
sunflowerButton.setIcon(new ImageIcon(img));
sunflowerButton.setText("");
}
}
11. Corrija as importações em FlowerFrame. O conjunto completo de importações deve se como o
seguinte:
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Map;
import javax.swing.ImageIcon;
12. Adicione o seguinte códigoa classe principal em Main.java.
public class Main {
private static int downloadedPictures;
public static void main(String[] args) {
final Map<String,Image> flowers = new HashMap<String,Image>(4);
final Map<String,Image> thumbs = new HashMap<String,Image>(4);
// Exibe o frame FlowerFrame.
final FlowerFrame frame = new FlowerFrame(flowers);
frame.setVisible(true);
// Client conecta ao serviço Web.
FlowerService_Service service = new FlowerService_Service();
final FlowerService port = service.getFlowerServicePort();
Runnable[] tasks = new Runnable[4];
// A operacao getFlower do serviço Web eh chamada
// 04 vezes, cada uma em uma thread separada.
// Quando a operação termina a imagem eh exibida em
// um dos botoes.
for (int i=0; i<4;i++) {
final int index = i;
tasks[i] = new Runnable() {
public void run() {
try {
// Chama a operacao getFlower
// do serviço Web
Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
System.out.println("imagem carregada: "+
FlowerFrame.FLOWERS[index]);
// Adiciona strings ao hashmap:
flowers.put(FlowerFrame.FLOWERS[index],img);
// Chama a operação showFlower
// do frame FlowerFrame:
frame.showFlower();
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
downloadedPictures++;
}
};
new Thread(tasks[i]).start();
}
// A operação allFlowers é chamada em uma thread separada
// logo após as 4primeiras threads terminarem.
// Após as imagens serem carregadas, elas são exibidas nos
// botões na parte inferior do frame.
Runnable thumbsTask = new Runnable() {
public void run() {
try {
while (downloadedPictures < 4) {
try {Thread.sleep(100);}
catch (InterruptedException ex) {}
}
// Chama a operação allFlowers
// doservico Web:
List<Image> images = port.allFlowers();
System.out.println("icones carregados");
if (images != null && images.size() == 4) {
for (int i=0;i<4;i++) {
thumbs.put(FlowerFrame.FLOWERS[i],images.get(i));
}
frame.setThumbnails(thumbs);
}
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
}
};
new Thread(thumbsTask).start();
}
}
13. Corrija as importações em FlowerFrame. O conjunto completo de importações deve se como o
seguinte:
import flower.album.FlowerService;
import flower.album.FlowerService_Service;
import flower.album.IOException_Exception;
import java.awt.Image;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
14. Remova o método main existente na classe FlowerFrame.
O serviço Web está completo! Clique com o botão direito no cliente e selecione Executar. Caso as imagens não
apareçam, selecione Limpar e Construir no projeto FlowerService e execute o cliente novamente. Veja
abaixo como deve ficar sua aplicação cliente em execução.