Construir Webservice SOAP com Netbeans, Glassfish, Windows Server 2003 e MS SQL Server 2000

Caros amigos, venho aqui expor um pouco do que tenho aprendido para montar um servidor de aplicações e obter assim uma opinião de como melhorar o trabalho ja que sou muito “noob” como dizem os gamers.

Bom a história, meio comum é que preciso montar um webservices que ira se comunicar com clientes iPad. Nossa empresa já trabalha a muito tempo com aplicações desktop feitas em Delphi e a plataforma aqui é Microsoft total. No entanto ultimamente a empresa responsável pela nossa ferramenta não tem um futuro estabelecido e o ultimo grande filho de pascal esta se tornando uma plataforma de nicho. Testamos por alguns meses o .net mas não gostamos da plataforma, ai cogitamos varias tecnologias e por fim como é de se prever escolhemos o Java.

Lindo mas o fato é que todos aqui são programadores procedurais desde sempre (Basic, Clipper, Delphi, PHP) e por necessidade fomos com tudo para a orientação de objetos. Meio que um choque cultural, ainda mais tento de realizar um projeto como esse do webservice.

Não há instituições em centenas de KM desse fim de mundo para estudarmos, e mesmo as mais longe só iniciam turmas em 2013. Tenho lido muito este forum e os amigos tem sido de grande ajuda, bem como o Youtube e os livros (que alias recomendo):

[list]Core Java 1 e 2 de Cay S. Horstmann e Gary Cornell[/list]
[list]Use a Cabeça Servelets e JSP[/list]
[list]Padrões de Projeto[/list]
[list]Introdução a Plataforma Java EE6 com Glassfish 3 de Antonio Gonçalves[/list]
[list]Java Web Services Implementando[/list]

Apesar de atualmente compreender muito parcialmente ja montei um webservice que faz determinadas tarefas como buscar no banco de dados, geral XML em uma String e outras tarefas mais simples.

Configurando Glassfish 3 com MS SQL Server 200

O primeiro desafio foi implementar uma comunicação entre o SQL Server 2000, uma vez que o driver padrão disponível no site da microsoft por alguma razão não funcionava corretamente com o Glassfish. Depois de muitas tentativas e pesquisa consegui criar o JNDI no servidor usando os seguintes passos:

::::::::::::::::::::::::::::::::::::::::::::::::::::::
::Notas de configuração SQL Server 2000 no Glassfish::
::::::::::::::::::::::::::::::::::::::::::::::::::::::

1 - Nome do Driver baixado no site da Microsoft.
nome: sqljdbc.jar
http://msdn.microsoft.com/pt-br/sqlserver/aa937724.aspx

2 - Diretório de instação no Glasssfish (Local onde o arquivo sqljdbc.jar devera ser copiado).
glassfish3\glassfish\domains\DOMINIO\lib\ext

3 - Acesse a configuração do seu Glassfish (geralmente http:\SeuServidor:4848), va em Recursos/JDBC/Pools de Conexão JDBC.

  • Definições gerais do Pool
    Tipo de Recurso: javax.sql.DataSource
    Nome de Classe da Fonte de Dados (os nomes padrão do sqlserver não funcionam com a versão 2000): com.microsoft.jdbcx.sqlserver.SQLServerDataSource

4 - Agora em Recursos/JDBC/Recurso JDBC, crie um recurso com base no pool criado acima.

5 - Chamar a referência ao recurso no web.xml do projeto.

<resource-ref> <res-ref-name>jdbc/RECURSO</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref>

6 - Uma Classe para criar o chamado do recurso.

[code]public class CriarConexoes {

public Connection getConnection() {
    try {
        InitialContext initialContext = new InitialContext();
        DataSource dataSource = (DataSource) initialContext.lookup("jdbc/NTServer");
        return dataSource.getConnection();
    } catch (NamingException ex) {
        Logger.getLogger(CriarConexoes.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SQLException ex) {
        Logger.getLogger(CriarConexoes.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

public Statement getStatement() {
    Connection con = getConnection();
    try {
        Statement stmt = con.createStatement();
        return stmt;
    } catch (SQLException ex) {
        Logger.getLogger(CriarConexoes.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

public ResultSet getResultset(String sql) {
    try {
        ResultSet rs = getStatement().executeQuery(sql);
        return rs;
    } catch (SQLException ex) {
        Logger.getLogger(CriarConexoes.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

}[/code]

Com isso em qualquer lugar que eu precisar fazer uma consulta por exemplo uso:

CriarConexoes con = new CriarConexoes(); ResultSet rs = con.getResultset("SELECT coluna1, coluna2 FROM tabela_qualquer"); List<ClasseDescreveTabela> lista = new ArrayList(); try { while (rs.next()) { ClasseDescreveTabela tab = new ClasseDescreveTabela(); tab.setColuna1(rs.getInt("coluna1")); tab.setColuna2(rs.getString("coluna2")); lista.add(tab); } } catch (SQLException ex) { Logger.getLogger(ClasseAtual.class.getName()).log(Level.SEVERE, null, ex); }

Bem simples né? Mas pra chegar nisso eu ralei… Não reclamo não, meu chefe ta pior já que ele esta estudando o Objective C para criar os clientes no iPad.
Esse foi o inicio e a partir dai consegui criar tarefas mais “reais” e praticas com o Java.

Desculpe, parece que postei no local errado (acho) alguem poderia move-lo para o Java Enterprise Edition (Java EE)?

Continuando…

Montado o WebService. Basicamente a primeira tarefa é a aplicação cliente importar dados para alimentar suas tabelas internas. Basicamente a estrutura do Sistema vai ser assim.

[app Cliente]
[list]Possui tabelas próprias (Até o momento possui 15), que são usadas em operações locais.[/list]
[list]O cliente ira se conectar ao WebService na sua primeira utilização para importar os dados básicos que irão alimentar essas tabelas.[/list]
[list]O cliente ira se conectar ao Webservice para enviar novos dados e verificar se há atualizações dos dados básicos.[/list]

[Webservice]

Autentificar usuário e:
[list]Enviar dados para alimentar tabelas.[/list]
[list]Executar regras do negocio a partir dos dados recebidos do Cliente.[/list]
[list]Devolver resultado ao Cliente (atualizar dados do Cliente).[/list]

Como disse anteriormente, esse lance de Webservice e camadas é novo para mim, assim como o próprio Java. Estou usando SOAP, já montei uma aplicação usando o Glassfish que consulta dados no SQLService.

Então gostaria muito da opinião de vocês sobre maneiras mais profissionais e funcionais de fazer as tarefas que vou postando aqui, servindo assim tanto de ajuda para mim quanto de referência para algum outro novato interessado no assunto.

Métodos do Servidor

[list]Importar Tabelas[/list]
Pelo conhecimento que temos, decidimos importar as tabelas em formato XML dentro de uma String. A ideia geral é, consultar dados (usando o procedimento do texto anterior) colocar o resultado dentro de um List e gerar o XML a partir desse List. Para isto uma solução prática foi usar o XStream (disponível em http://xstream.codehaus.org/). Criei uma classe que ao mesmo tempo lê o conteúdo do List e muda os Alias do XML com base no nome da classe do objeto (tabela).

Classe que cria o XML:

[code]public class CriarXMLdoArray {

public static String CriaXML(Object Lista, Class objeto2) {
    XStream xs = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
    nomeObj n = new nomeObj();
    xs.alias(n.nomeObj(objeto2) + "_S", List.class);
    xs.alias(n.nomeObj(Lista.getClass()) + "X", Lista.getClass());
    xs.alias(n.nomeObj(objeto2), objeto2);
    String retorno = xs.toXML(Lista);
    return retorno;
}

}[/code]

Classe que pega o nome dos objetos:

[code]public class nomeObj {

public String nomeObj(Class obj) {
    String name = obj.getName();
    if (obj.getName().indexOf('.') < 0) {
        return name;
    }
    return name.substring(name.lastIndexOf('.') + 1, name.length());
}

}[/code]

Então na classe principal do projeto, eu declaro o seguinte @WEBMETHOD:
[code]/**
* Operação de serviço web
*/
@WebMethod(operationName = “ImportarTabelaX”)
public String ImportarTabelaX(@WebParam(name = “nome”) String nome, @WebParam(name = “senha”) String senha, @WebParam(name = “id”) String id) {
//TODO write your implementation code here:
//Esse IF chama uma classe que verifica se o usuario que chama o método existe (Repare que todos os parâmetros são String, não sei se isso é recomendado)
if (!new ValidarUsuario(nome, senha, id).valida()) {
return “Usuário inválido!”;
}
//Código que lê a tabela e gera e cria o List (resumido só para ilustrar)
ResutSet = new CriarConexoes().getResultset(“SELECT A, B FROM TabelaX”);
List lista = new ArrayList();
try{
while(rs.next()){
ClasseTabelaX tabela = new ClasseTabelaX();
tabela.setA(rs.getString(“A”);
tabela.setB(rs.getString(“B”);
lista.add(OBJGerados);
}
} catch (Exception e) {

    }
    return CriarXMLdoArray.CriaXML(lista, ClasseTabelaX.class);
}[/code]

Basicamente estou fazendo isso. O retorno dentro da String é mais ou menos assim:

<TabelaX_S> <TabelaX> <A>Amora</A> <B>Banana</B> </TabelaX> <TabelaX> <A>Avião</A> <B>Balão</B> </TabelaX> <TabelaX> <A>Armadura</A> <B>Baioneta</B> </TabelaX> <TabelaX> <A>Arcode</A> <B>Baixo</B> </TabelaX> </TabelaX_S>

Bem é isso ai… Em suas opiniões o que acham do jeito que estou fazendo as coisas? Alguém tem experiência com este assunto?

Continuando…

Agora vou criar uma classe para poder ler o XML enviado das aplicações clientes e colocar os valores em uma lista.
Crio uma nova classe chamada XMLparaArray contendo o seguinte código:

[code]public class XMLparaArray {

public static List LerXML(Class objeto2, String xml) {
    XStream xs = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
    nomeObj n = new nomeObj();
    xs.alias(n.nomeObj(objeto2) + "_S", List.class);
    xs.alias(n.nomeObj(objeto2), objeto2);
    List lista = (List) xs.fromXML(xml);
    return lista;
}

}[/code]

O XML será enviado para o servidor dentro de uma String. Para então utilizar esta classe eu faria a seguinte declaração:

[code]List listaTabelaX = XMLparaArray.LerXML(ClasseTabelaX.class, StringXML);

String retorno ="";
for (int i = 0; i < listaTabelaX .size(); i++) {
retorno += listaTabelaX .get(i).getA() + " - ";
}[/code]

o conteúdo da variável retorno será:
Amora - Avião - Armadura - Arcode -

Aquilo de sempre… Quem ler tiver duvidas e principalmente sugestões eu ficarei contente. :3 :stuck_out_tongue:

Continuando…

Estava preocupado a respeito das conexões ficarem abertas, pesquisando nas documentações e aqui no forum, resolvi que a melhor maneira (até o momento claro) seria fechar as conexões criadas, pois ao que parece o coletor de lixo automático do java (responsável por excluir da memória o que não é mais utilizado) não é 100% funcional para este caso.

Então quando eu fizer uma conexão para consultar ou inserir algum dado eu tenho de finalizar essa conexão.

No exemplo abaixo eu me conecto usando as classes criadas anteriormente retorno uma lista e finalizo a conexão:

public class TabelaX_Select {
    //Aqui recebo os filtros extras para o WHERE do SQL

    String filtros;

    //Constructor da classe
    public TabelaX_Select(String filtro) {
        this.filtros = " " + filtro;
    }

    public List GrupoProdutos() {
        String sql = "SELECT a, b"
                + "FROM tabelax"
                + "WHERE 1 = 1"
                + filtros;
        // TabelaX é o nome da Classe que descreve os campos da tabela consultada no Banco de Dados
        List<TabelaX> lista = new ArrayList<TabelaX>();
        CriarConexoes con = new CriarConexoes();
        try {
            ResultSet rs = new CriarConexoes().getResultset(sql);
            try {
                while (rs.next()) {
                    TabelaX x = new TabelaX();
                    x.setA(rs.getString("a"));
                    x.setB(rs.getString("b"));
                    lista.add(x);
                }
            } catch (SQLException ex) {
                // Uso essa linha no Exception para que o arquivo de log no servidor indique o que houve
                Logger.getLogger(TabelaX_Select.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                // Fecho o ResultSet
                rs.close();
            }
            return lista;
        } catch (Exception ex) {
            Logger.getLogger(TabelaX_Select.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            // Fecho a conexão
            try {
                con.getConnection().close();
            } catch (SQLException ex) {
                Logger.getLogger(TabelaX_Select.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }
}

Isso ai. Se alguém ler e tiver sugestões ela é mais que bem vindas. Se alguém ler e tiver duvidas também.