Recentemente estive em um projeto e tive que desenvolver uma solução bem bacana com integração entre Oracle Webcenter e Siebel. O Siebel trabalha com tokens de segurança nas chamadas de serviços, o problema é que não há implementação de WS-Security que faça a autenticação e tokens de segurança de forma satisfatória e sem problemas. Além disso há outros detalhes de desempenho que a equipe da Siebel disse que é fundamental.
A equipe Siebel queria que fosse associada a sessão Web do usuário com a sessão de conexão com o Siebel para as chamadas de serviços. O problema dessa solução é justamente fazer essa associação de maneira correta (uma não tem nada a ver com a outra) e tratar solicitações paralelas. Isso é fundamental em ambientes Web. Outro problema é que haveriam muitas sessões Siebel ociosas, pois após o usuário realizar a requisição, ele fica analisando os dados e navegando na página, antes de realizar novas requisições. A solução que eu sugeri foi a implementação de um pool de conexões compartilhadas com o Siebel, da mesma maneira que utilizamos para Banco de Dados. Para atender o requisito solicitado pela equipe Siebel, definimos o número de conexões mínimas igual a 1 e o máximo igual ao número de usuários conectados.
Antes de entender o funcionamento do pool Siebel, vamos entender o funcionamento dos tokens no Siebel.
Requisição anônima sem sessão
Essa foi nossa primeira abordagem antes de criar o pool de conexões. No header da requisição SOAP não enviamos nenhuma informação de usuário, apenas definimos o SessionType como None. Para cada requisição ele abre uma sessão e fecha ao enviar a resposta. O problema dessa implementação é que embora seja simples, o tempo de criação de sessão sem o uso do token é bem elevado.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <SessionType xmlns="http://siebel.com/webservices">None</SessionType> </soap:Header> <soap:Body> <!-- ... --> </soap:Body> </soap:Envelope>
Autorização Siebel, sem sessão
Semelhante à anterior, inclusive com os mesmo problemas, mas passando o usuário e senha para autorização.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <UsernameToken xmlns="http://siebel.com/webservices">usuario</UsernameToken> <PasswordText xmlns="http://siebel.com/webservices">senha</PasswordText> <SessionType xmlns="http://siebel.com/webservices">None</SessionType> </soap:Header> <soap:Body> <!-- ... --> </soap:Body> </soap:Envelope>
Autorização Siebel com sessão stateless
Essa foi a solução adotada para o pool de conexões com o Siebel que explicarei mais adiante. O funcionamento é simples de entender, mas um pouco trabalhoso de se implementar já que é sempre necessário guardar o último token de cada conexão. A primeira requisição deve realizar a autorização passando o usuário, senha e o tipo de sessão Stateless, com isso uma sessão é aberta para esse usuário. Ao obter uma resposta da requisição, o Siebel retornará o token de segurança que deverá ser utilizado na próxima requisição, que retornará outro token de segurança que deverá ser utilizado na próxima requisição e assim por diante. Para finalizar a sessão Siebel é só passar o usuário e senha com o tipo de sessão None.
Requisição inicial
Para efetuar o log in e criar a sessão é necessário passar o seguinte header:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <UsernameToken xmlns="http://siebel.com/webservices">usuario</UsernameToken> <PasswordText xmlns="http://siebel.com/webservices">senha</PasswordText> <SessionType xmlns="http://siebel.com/webservices">Stateless</SessionType> </soap:Header> <soap:Body> <!-- ... --> </soap:Body> </soap:Envelope>
Dessa forma ele deverá retornar uma resposta parecida com essa:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <siebel-header:SessionToken xmlns:siebel-header="http://siebel.com/webservices">TokenCriptografadoComValorEstranhoDoResponse </siebel-header:SessionToken> </soap:Header> <soap:Body> <!-- ... --> </soap:Body> </soap:Envelope>
Próximas Requisições
Para as próximas requisições e necessário recuperar o token da resposta e colocar no header da requisição da seguinte forma:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <SessionType xmlns="http://siebel.com/webservices">Stateless</SessionType> <SessionToken xmlns="http://siebel.com/webservices">TokenCriptografadoComValorEstranhoDoResponse</SessionToken> </soap:Header> <soap:Body> <!-- ... --> </soap:Body> </soap:Envelope>
E assim por diante. Existe a opção do tipo de sessão ser Statefull também para manter o estado da sessão. O funcionamento é o mesmo, só muda o SessionType para Statefull.
Pool de conexões
Como assinei contrato de confidencialidade não poderei disponibilizar o código fonte utilizado no projeto, mas explicarei os caminhos necessários para tudo funcionar adequadamente.
Para a inclusão do header nas requisições do Siebel, utilizei um SOAPHandler. O SOAPHandler funciona como uma espécie de "filtro" para todas as requisições que utilize ele. Dessa forma eu posso modificar o header, alterar a requisição e obter ou mesmo alterar os dados da resposta. Para realizar isso, é só criar uma classe que implemente a interface SOAPHandler como no exemplo e associar com os clientes de cada serviço (JAX-WS 2.0).
public class SiebelHandler implements SOAPHandler<SOAPMessageContext> { //... }
Dos métodos que a interface nos obriga a implementar utilizaremos dois: handleMessage e handleFault. Que é onde iremos modificar os headers e obter os tokens.
public boolean handleMessage(SOAPMessageContext smc) { Boolean outboundProperty = (Boolean)smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (outboundProperty.booleanValue()) { SiebelTokenHelper tokenHelper = SiebelSimpleTokenManager.getInstance().getNextToken(); tokenUsed(tokenHelper); SOAPMessage message = smc.getMessage(); try { SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope(); SOAPHeader header = envelope.getHeader(); if (header == null) { header = envelope.addHeader(); } // Token if (tokenHelper.getTokenValue() == null) { SOAPElement userNameToken = header.addChildElement("UsernameToken", "", "http://siebel.com/webservices"); userNameToken.addTextNode(tokenHelper.getUser()); SOAPElement password = header.addChildElement("PasswordText", "", "http://siebel.com/webservices"); password.addTextNode(tokenHelper.getPassword()); } else { SOAPElement sessionToken = header.addChildElement("SessionToken", "", "http://siebel.com/webservices"); sessionToken.addTextNode(tokenHelper.getTokenValue()); } SOAPElement sessionType = header.addChildElement("SessionType", "", "http://siebel.com/webservices"); if (isLogout(envelope)) { sessionType.addTextNode("None"); SiebelSimpleTokenManager.getInstance().removeFromPool(tokenHelper); tokenHelper = null; } else { sessionType.addTextNode("Stateless"); } } catch (Exception e) { logger.log(Level.SEVERE, "Erro ao invocar servico", e); } } else { SiebelTokenHelper tokenHelper = getUsedToken(); try { SOAPMessage message = smc.getMessage(); if (tokenHelper != null) { Node tokenCHild = message.getSOAPHeader().getFirstChild(); if (tokenCHild != null) { tokenHelper.setTokenValue(tokenCHild.getTextContent()); } } } catch (Exception ex) { ex.printStackTrace(); tokenHelper.setTokenValue(null); } finally { if (tokenHelper != null) { SiebelSimpleTokenManager.getInstance().pushElemIntoPool(tokenHelper); } } } return outboundProperty; }
A primeira condição na linha 05 verifica se é requisição ou reposta. Se for requisição o valor será verdadeiro. Na linha 07 e 08 utilizo uma implementação de fila (utilizando LinkedList) para recuperar o elemento mais antigo (menos recente usado), removo ele da fila para que outras conexões Siebel possam ser utilizadas, ele será novamente inserido no final da fila (linha 74), após ter o valor do token atualizado (linha 65) na resposta (linha 57). Na linha 22 verifico se existe token anterior ou se é uma conexão nova. Se for uma conexão nova, atribuo o valor do usuário (linha 26), da senha (linha 31). Caso contrário, utilizo o token (linha 38) atributo anteriomente na resposta (linha 65). Se for uma requisição de logout (linha 45), atribuo o SessionType como None, caso contrário Stateless.
O coração da implementação é esse método, mas ainda não estamos tratando exceção e reestabelecendo a conexão caso a sessão expire. Para reestabelecer a conexão, basta colocar no catch do cliente uma nova tentativa se o erro tiver o código 10944642. No nosso handleFault basta remover da fila para não ser mais utilizado, caso contrário, se for outro tipo de erro, o token é retornado no fault e devemos atribuir para ser utilizado na próxima requisição. Quando a sessão expira o Siebel retorna a seguinte mensagem:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <SOAP-ENV:Body> <SOAP-ENV:Fault> <faultcode>SOAP-ENV:Client</faultcode> <faultstring>Error Code: 10944642 Error Message: Error: Inbound SOAP Message - Session Token is missing or invalid or has expired</faultstring> <detail> <siebelf:errorstack xmlns:siebelf="http://www.siebel.com/ws/fault"> <siebelf:error> <siebelf:errorsymbol/> <siebelf:errormsg>Error: Inbound SOAP Message - Session Token is missing or invalid or has expired</siebelf:errormsg> </siebelf:error> </siebelf:errorstack> </detail> </SOAP-ENV:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
Por isso devemos tratar quando obtermos o código: 10944642, pois não é um erro de serviço, é somente sessão expirada. O controle de criação de novas conexões foi feito em uma classe que possui a fila de conexões e tokens e o controle do número máximo foi feito utilizando um Managed Bean para contabilização de login e logout e um Session Listener, para caso o usuário esqueça de clicar no botão Sair.
Para testar a Oracle disponibiliza um VM do Siebel no e-delivery. Com essa implementação, tivemos um ganho de performance absurdo, paralelizamos as requisições e otimizamos o uso de sessões e compartilhamento de tokens no Siebel.
Sou Analista de sistemas. Não interajo com SIEBEL-Oracle desde 2009.
Gostaria de obter "Se for POSSÍVEL" uma noção Básica de como construir um projeto NOVO nesta ferramenta,
Através de um filme, tutorial ou qualquer coisa.
Um abraço.
JOAO
Desconheço