Postar e receber objetos serializados com Sevlet (via Swing + URLConnection) - RESOLVIDO!

Olá pessoal, depois de muito procurar sobre minha duvida ate consegui desenvolver alguma coisa, mas ainda não está como quero…

Tenho uma aplicação desktop com swing que se comunica com app web via Servlets usando o URLConnection ok?!
mais ou menos assim:

	        // meu objeto que implementa serializable
	        Bairro b = new Bairro();
	        b.setId(1);
	        // montanto o parametro onde o Parses.toString serializa o objeto em string com Encode64Base
	        String data = URLEncoder.encode("param", "UTF-8") + "=" + URLEncoder.encode(Parser.toString(b), "UTF-8");
	    
	       
	        URL url = new URL("http://localhot:8080/servlets/test?");
	        URLConnection conn = url.openConnection();
	        
	        conn.setDoOutput(true);
	        conn.setAllowUserInteraction(false);
	        OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
	        wr.write(data);
	        wr.flush();


	        // Recebendo a resposta do servlet
	        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
	        String line;
	        String outter = "";
	        while ((line = rd.readLine()) != null) {
	        	outter = outter + line;
	           System.out.println(line);
	        }
	        wr.close();
	        rd.close();

	        // Converte a resposta novamente para Bairro...
	        b = (Bairro)Parser.fromString(outter);
	        System.out.println(b.getBaidescricao());

Eis a classe parser…

public class Parser {

	/** Read the object from Base64 string. */
	public static Object fromString(String s) throws IOException,
			ClassNotFoundException {
		byte[] data = Base64Coder.decode(s);
		ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
				data));
		Object o = ois.readObject();
		ois.close();
		return o;
	}

	/** Write the object to a Base64 string. */
	public static String toString(Serializable o) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(baos);
		oos.writeObject(o);
		oos.close();
		return new String(Base64Coder.encode(baos.toByteArray()));

	}

Meu servlet, le isso des-serializa o “param” para Bairro, executa uma pesquisa, serializa retorna o objeto novamente…

A questão é:
Consigo fazer isso de forma mais elegante? postando/gravando diretamente em bytes? como o upload de um arquivo eu acho… :?
Tambem penso que com params String, caso eu venha usar um List<?> serializado pode haver problemas com tamanho e os caracteres da codificação…

Não quero usar [color=red]XML[/color], nem [color=blue]EJB[/color] (pois uso Tomcat somente e a aplicação é relativamente simples)…
e uso URLConnection pois quanto menor ficar o jar da aplicação no cliente é melhor… :smiley:

Alguem sugere algo ??

Obrigado desde já!!

ops, se eu postei no forum errado, pois swing e tals… se preferirem, pode mover… :smiley:

E ai dooda!

Vou te passar um exemplo simples e objetivo que irá solucionar seu problema!

MyObject:

public class MyObject implements Serializable{

    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Servlet:

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        //response.setContentType("text/;");
        PrintWriter out = response.getWriter();
        try {
            MyObject obj = new MyObject();
            obj.setId(100);
            obj.setName("José aparecido");

            JSONEncoder encoder = new JSONEncoder();
            encoder.encode(obj);
            out.write( encoder.toString() );
        } finally { 
            out.close();
        }
    } 

Requisição:

    public static void main( String arg[] ) throws Exception{

        URL url = new URL("http://localhost:8084/JsonWebTest/JSONServlet");
        URLConnection conn = url.openConnection();

        // Recebendo a resposta do servlet
        JSONDecoder decode = new JSONDecoder( conn.getInputStream() );
        MyObject o = (MyObject)decode.decode();
        System.out.println(o.getId());
        System.out.println(o.getName());
    }

Usei : jgates.sourceforge.net

Obrigado Mr. plic_ploc mas para a “postagem” no servlet qual seria a sugestão usando JSON então?

poss no servlet receber com:

    // Processar o inputStream do request ?   
    JSONDecoder decode = new JSONDecoder( request.getInputStream() );  
    MyObject o = (MyObject)decode.decode(); 

e na postagem como envio o objeto? via parametro ainda? ou da pra escreve no outputStream da URLConnection?

Obrigado por enquanto…

Nesse outro exemplo, o objeto é enviado ao servlet e o servlet devolve o objeto.

Requisição:

    public static void main( String arg[] ) throws Exception{

        MyObject send = new MyObject();
        send.setId( 100 );
        send.setName("José aparecido dos Santos");

        JSONEncoder encoder = new JSONEncoder();
        encoder.encode(send);
        String value = encoder.toString();
        value = URLEncoder.encode(value, "UTF-8");

        String end = "http://localhost:8084/JsonWebTest/JSONServlet?value="+value;
        URL url = new URL(end);
        URLConnection conn = url.openConnection();

        conn.setDoOutput(true);
        conn.setAllowUserInteraction(false);
        
        // Recebendo a resposta do servlet
        JSONDecoder decode = new JSONDecoder( conn.getInputStream() );
        MyObject o = (MyObject)decode.decode();
        System.out.println(o.getId());
        System.out.println(o.getName());
    }

Servlet:

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        String json = request.getParameter("value");
        PrintWriter out = response.getWriter();
        JSONEncoder encoder = new JSONEncoder();
        try{
            JSONDecoder decoder = new JSONDecoder( json );
            MyObject obj = (MyObject)decoder.decode();
            encoder.encode(obj);
        }
        catch( Exception e ){
            //Erro
            encoder.encode(null);
        }
        finally{
            out.write( encoder.toString() );
            out.close();
        }
    } 

Obrigdo novamente, com teu exemplo, consegui usando a propria classe de parses com Base64 que eu ja estava usando…

:smiley:

assim já melhorou bastante, qualquer avanço, posto aqui novamene…

vallew’s

Finalmente com um pouco mais de estudos cheguei ao resultado que gostaria sem nenhuma biblioteca/jar a mais…

Meu servlet ficou assim:

public class Servlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}
	
	private void processRequest(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		Bairro o = null;
		
        //-------------------------------------------------------------------------------		
        // Recebe o "objeto" diretamente do InputStream
        ObjectInputStream ois = new ObjectInputStream(req.getInputStream());
		try {
			o = (Bairro) ois.readObject();
			o.getById();	
			
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}

		//-------------------------------------------------------------------------------
        // Devolve o "objeto" diretamente no OutputStream
        ObjectOutputStream ous = new ObjectOutputStream(res.getOutputStream());
        ous.writeObject(o);
        ous.flush();
        ous.close();
		//-------------------------------------------------------------------------------
	}	
}

Meu programa teste ficou assim:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.net.URLConnection;

import javax.swing.JOptionPane;

public class Teste {

	public static void main(String[] args) throws Exception{
		
		Bairro b = new Bairro();
		b.setIdbairro(Integer.valueOf(JOptionPane.showInputDialog("Informe o id do bairro (Integer)")));
		
	    try {
	        URL url = new URL("http://localhost:8080/servlets/Servlet");
        
	        JOptionPane.showMessageDialog(null, "Vou enviar uma requisição:\n" + url + "\nbuscando pelo IdBairro = " + b.getIdbairro());
	        
	        URLConnection conn = url.openConnection();
	        conn.setDoOutput(true);
	        conn.setAllowUserInteraction(false);
	        
	        //--------------------------------------------------------------------
	        ObjectOutputStream wr = new ObjectOutputStream(conn.getOutputStream());
	        wr.writeObject(b); // "Escreve" o objeto no servlet
	        wr.flush();
	        wr.close();
	        //--------------------------------------------------------------------
	        ObjectInputStream ois = new ObjectInputStream(conn.getInputStream());
	        b = (Bairro) ois.readObject(); // "Lê" o retorno objeto o servlet
	        ois.close();
	        //--------------------------------------------------------------------
	        
	        JOptionPane.showMessageDialog(null, "A requisição retornou o bairro:\n\n" + 
	        		"Id: " + b.getIdbairro() + "\n" +
	        		"Bairro: " + b.getBaidescricao() + "\n" +
	        		"Situacao: " + b.getBaisituacao());
	        
	    } catch (Exception e) {
	    	JOptionPane.showMessageDialog(null, "ERRO: " + e.getMessage());
	    	e.printStackTrace();
	    }

	}	
}

e o “Bairro” é apenas um POJO que implementa Serializable…

O resultado pra mim ficou perfeito, será que pode haver algum problema nesse tipo de comunicação?

Por enquanto esta ótimo assim!
Vallew…

[quote=dooda]Finalmente com um pouco mais de estudos cheguei ao resultado que gostaria sem nenhuma biblioteca/jar a mais…

Meu servlet ficou assim:

public class Servlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}
	
	private void processRequest(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		Bairro o = null;
		
        //-------------------------------------------------------------------------------		
        // Recebe o "objeto" diretamente do InputStream
        ObjectInputStream ois = new ObjectInputStream(req.getInputStream());
		try {
			o = (Bairro) ois.readObject();
			o.getById();	
			
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}

		//-------------------------------------------------------------------------------
        // Devolve o "objeto" diretamente no OutputStream
        ObjectOutputStream ous = new ObjectOutputStream(res.getOutputStream());
        ous.writeObject(o);
        ous.flush();
        ous.close();
		//-------------------------------------------------------------------------------
	}	
}

Meu programa teste ficou assim:

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.net.URLConnection;

import javax.swing.JOptionPane;

public class Teste {

	public static void main(String[] args) throws Exception{
		
		Bairro b = new Bairro();
		b.setIdbairro(Integer.valueOf(JOptionPane.showInputDialog("Informe o id do bairro (Integer)")));
		
	    try {
	        URL url = new URL("http://localhost:8080/servlets/Servlet");
        
	        JOptionPane.showMessageDialog(null, "Vou enviar uma requisição:\n" + url + "\nbuscando pelo IdBairro = " + b.getIdbairro());
	        
	        URLConnection conn = url.openConnection();
	        conn.setDoOutput(true);
	        conn.setAllowUserInteraction(false);
	        
	        //--------------------------------------------------------------------
	        ObjectOutputStream wr = new ObjectOutputStream(conn.getOutputStream());
	        wr.writeObject(b); // "Escreve" o objeto no servlet
	        wr.flush();
	        wr.close();
	        //--------------------------------------------------------------------
	        ObjectInputStream ois = new ObjectInputStream(conn.getInputStream());
	        b = (Bairro) ois.readObject(); // "Lê" o retorno objeto o servlet
	        ois.close();
	        //--------------------------------------------------------------------
	        
	        JOptionPane.showMessageDialog(null, "A requisição retornou o bairro:\n\n" + 
	        		"Id: " + b.getIdbairro() + "\n" +
	        		"Bairro: " + b.getBaidescricao() + "\n" +
	        		"Situacao: " + b.getBaisituacao());
	        
	    } catch (Exception e) {
	    	JOptionPane.showMessageDialog(null, "ERRO: " + e.getMessage());
	    	e.printStackTrace();
	    }

	}	
}

e o “Bairro” é apenas um POJO que implementa Serializable…

O resultado pra mim ficou perfeito, será que pode haver algum problema nesse tipo de comunicação?

Por enquanto esta ótimo assim!
Vallew…[/quote]

Oi dooda!

Vc recebeu algum feedback a respeito de possíveis problemas com esse tipo de comunicação? Estou prestes a adotar essa ideia, e queria saber a respeito!
Obrigado!
Rodrigo

Cara!!! o que posso te afirmar com certeza é que trabalhei em outro projeto que tratava tudo com .xml e num projeto pessoal que esta funcionamento ate hoje.

Este modelo de comunicação, que foi um pouco aprimorado, funciona mais do que perfeitamente… estamos muito satisfeitos com os resultados e não mudaríamos não.

No modelo abaixo todas as classes que navegam pela nuvem, usamos externalização ou invés de serialização, pelo fato de heranças e muitos objetos e atrbiutos inuteis nas classes comuns, então na externalização, nós definimos como e quais campos serão transmitidos basicamente…

Tambem usamos um Objeto genérico para transmissão, uma especie de classe protocolo e ao enviar para a nuvem tambem compactamos usando o GZip. Então compartilhamos entre client e server o mesmo jar assinado que é comum entre eles…

Enfim, com isso conseguimos trafegar arrays de bytes entre os dois e assim transmitir arquivos com umas melhoras aqui e acolá. O que ressalto é que em nosso projeto funciona mais do que bem, e alguns trechos de codigos e comunicação foram projetados já pensando nesse ambiente.

Fizemos testes de carga no server, uso de banda, etc etc com JBoss 5 ainda, e tudo se mostrou mto bom pra nós…

Poste suas experiencias e considerações assim que tive-las…
Um grande abraço!!

Dooda,
Muito bom esse material que vc disponibilizou, porem to com um problema aqui.

Quando eu rodo o Servlet e o teste.class do mesmo projeto, funciona de boa tranquilo!

Agora o problema está sendo quando eu rodo o tomcat com o servlet e tento acessar de um outro projeto em swing.
No debug ele vai ate essa linha:

ObjectInputStream ois = new ObjectInputStream(conn.getInputStream());

Depois ele retorna erro = null e eu não to conseguindo resolver isso;

Segue meu servlet:

[code]import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/@author Mateus Cordeiro/

@WebServlet(name = “Servlet”, urlPatterns = {"/Servlet"})
public class Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void processRequest(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {
    GravarTxt gravarTxt = new GravarTxt();
    Objeto o = null;
    
    //-------------------------------------------------------------------------------         
    // Recebe o "objeto" diretamente do InputStream  
    ObjectInputStream ois = new ObjectInputStream(req.getInputStream());  
    try {  
        o = (Objeto) ois.readObject();                        
        gravarTxt.escreventoTxt(o);//Chama um metodo que salva o objeto em um TXT 
        System.out.println("Objeto Salvo: " + o.getNome());
        
          
    } catch (Exception e) {  
        e.printStackTrace();  
        return;  
    }  
    //------------------------------------------------------------------------------- 
    // Devolve o "objeto" diretamente no OutputStream  
    ObjectOutputStream ous = new ObjectOutputStream(res.getOutputStream());  
    ous.writeUTF("OK Tranquilo");//Apenas quero que me retorne uma String
    ous.flush();  
    ous.close();  
    //-------------------------------------------------------------------------------  
}


@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}


@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}


@Override
public String getServletInfo() {
    return "Short description";
}// </editor-fold>

}[/code]

Segue o class que faz a conexao com servlet de dentro de um swing:

[code]
package digitacao;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.net.URLConnection;
import javax.swing.JOptionPane;

/* @author Mateus Cordeiro */
public class EnviarObjeto implements Runnable{

public void enviando(Objeto b){
    
    try{
        URL url = new URL("http://localhost:8080/digitacaoWeb/Servlet");
        JOptionPane.showMessageDialog(null, "Vou enviar uma requisição: \n" + url +
                "\nInformando o ID do Objeto = " + b.getId()
                + "\nInformando o Nome do Obejto = " + b.getNome());
        
        URLConnection conn = url.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setAllowUserInteraction(false);

        ObjectOutputStream wr = new ObjectOutputStream(conn.getOutputStream());
        wr.writeObject(b); //Escreve o Objeto
        wr.flush();
        wr.close();

        ObjectInputStream ois = new ObjectInputStream(conn.getInputStream());//No debug ele trava aqui 
        String resposta = ois.readUTF();
        ois.close();
        
        JOptionPane.showMessageDialog(null, "Resposta do Servlet: "+ resposta );    
        
    }catch (Exception e){
        JOptionPane.showMessageDialog(null, "Erro: " + e.getMessage());//E sai o erro aqui "NULL"
        e.printStackTrace();
    }
}

@Override
public void run() {
    throw new UnsupportedOperationException("Not supported yet.");
}

}[/code]

Obrigado

Ola Mateus,

Que bom que o material lhe foi util, é isso que mantém o GUJ =), vamos ao seu caso.

Noto que na linha que da o erro no teu client swing você esta tentando recuperar o InputStream da conexão. Porém no teu servlet você escreve com WriteUTF8();
Não consigo testar aqui no servico, mas tente assim:

  ous.writeObject(new String("OK"));
  ous.flush();

O bacana dessa solução é trafegar os objetos serializados tanto pra envio quanto pra recebimento, uma dica, que usamos aqui, é um Objeto Protocolo, que que é enviado e recebido, ele contem alguns atributos como Classe, Método, Parametros, Sucesso, Mensagem etc…

Posta ai o resultado…
Abraço!

Amigo,
Ainda está dando o mesmo erro:

java.io.EOFException
at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2280)
at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2749)
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:779)
at java.io.ObjectInputStream.(ObjectInputStream.java:279)
at digitacao.EnviarObjeto.enviando(EnviarObjeto.java:39)

Mateus,

Tambem não consigo confirmar isso pra ti, vejo que no teu client vc usa java.net.URLConnection, em meu comunicador uso java.net.HttpURLConnection.

E ai abrir a conexão, faço o cast dai

						httpConn = ((HttpURLConnection)url.openConnection());
						httpConn.setDefaultUseCaches(false);
						httpConn.setUseCaches(false);
						httpConn.setDoInput(true);
						httpConn.setDoOutput(true);

Tambem confirme se ele escreve mesmo no output no servlet

        ObjectOutputStream ous = new ObjectOutputStream(res.getOutputStream());    
        ous.writeObject("OK Tranquilo");
        ous.flush();    
        ous.close(); 

        System.out.println("Escreveu com sucesso");

Veja lá…
abraço!

Apenas p saber, eu criei um objeto no projeto do servlet e outro no projeto do swgin os dois assim:

[code]
package digitacao;

import java.io.Serializable;

/* @author Mateus Cordeiro */
public class Objeto implements Serializable{

private int id;
private String nome;

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

public String getNome() {
    return nome;
}

public void setNome(String nome) {
    this.nome = nome;
}      

}[/code]

Está correto?

hmm, não sei se era esse teu problema,

Mas você deve ter um objeto só, ou seja, um terceiro projeto com este teu “Objeto” então vc exporta o jar, e usa ele tanto no client/swing como no server/servlet.

Vai tentando, qualquer progresso, posta ai.
=)

Dooda!
Perfeito resolvido, era isso mesmo tive que criar um .jar com apenas o objeto e importar o mesmo tanto no SERVLET como no SWING.

Muito obrigado pela ajuda!

Que ótimo!
att.

Dooda!
Vc sabe como pode ser feito a parte de segurança?
Será que tem como controlar uma sessão, e o usuario logado pode ter acesso aquele Servlet?
Ou tem outro modo de controlar o acesso ao Servlet?

Mateus,

Não sei e não pesquisei pra saber se tem como manter uma sessão entre o client e o server assim como o browser faz.

Hoje nós serializamos e usamos o GZipIpunt para compactar e criptografar ( ver item 3 deste artigo http://www.ibm.com/developerworks/br/library/j-5things1/index.html )

o Servlet só trabalha com objetos e não responde a requisições que não seja assim.

Por hora resolve, caso descubra algo interessante, poste pra gente…

Att…