1. Resumo Anotações JAX-WS para Certificação SCDJWS 5.0
Gilberto Augusto Holms
@gibaholms
gibaholms85@gmail.com
http://gibaholms.wordpress.com/
Modos de Criar um WebService
• WSDL-to-Java Aproach: wsimport
• Java-to-WSDL Aproach: apt / wsgen
Tools
• apt
o Entrada códigos-fonte (.java) da SEI Implementation
o Saída Portable Artifacts (SEI + Service + JAXB Types + Faults)
• wsgen
o Entrada classe (.class) da SEI Implementation
o Saída WSDL e Portable Artifacts
• wsimport
o Entrada documento WSDL
o Saída Portable Artifacts (SEI + Service + JAXB Types + Faults)
@WebService
• Exemplo de SEI em Servlet:
@WebService
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
• Exemplo de SEI em EJB:
@WebService
@Stateless
public class Calculator {
@Resource
private WebServiceContext context;
public int add(int a, int b) {
return a + b;
}
}
• Utilizando SEI em interface separada:
@WebService
public interface CalculatorEndpoint {
public int add(int a, int b);
}
@WebService(
endpointInterface = "com.example.CalculatorEndpoint"
)
public class Calculator implements CalculatorEndpoint {
public int add(int a, int b) {
return a + b;
Gilberto Holms http://gibaholms.wordpress.com/
2. }
}
• Métodos expostos como operações do serviço:
o Sem SEI separada, todos os métodos públicos são expostos
o Com SEI separada, apenas os métodos da interface SEI são expostos
o Se houverem métodos anotados com @WebMethod, apenas estes serão expostos
• Se utilizada SEI separada, a classe de serviço não pode definir o parâmetro @WebService.name
• As classes/interfaces SEI podem, mas não precisam extender java.rmi.Remote
• As classes de serviço precisam fornecer construtor default
• As classes de serviço podem utilizar as anotações @PostConstruct e @PreDestroy
• Os métodos podem, mas não precisam lançar java.rmi.RemoteException
• Atributos de @WebService:
o name nome do wsdl:portType
Valor default: nome abreviado da classe
o targetNamespace targetNamespace do WSDL, que será atribuído aos itens do WSDL
Valor default: package da classe
Se utilizado numa SEI, afeta apenas wsdl:portType
Se utilizado num serviço com SEI separada, afeta apenas wsdl:service
Se utilizado num serviço sem SEI separada, afeta wsdl:portType e wsdl:service
o serviceName nome do wsdl:service
Proibido usar na interface SEI
Valor default: nome da classe + “Service”
o portName nome do wsdl:port
Proibido usar na interface SEI
Valor default: @WebService.name + “Port”
o endpointInterface para usar interface SEI separada, setar o nome completo dela aqui
Proibido usar na interface SEI, vale apenas para a classe de serviço
o wsdlLocation para criar o serviço através de um WSDL pré-existente
@WebMethod
• Exemplo:
@Stateless
@WebService(name = "TravelAgent")
public class TravelAgentBean {
@WebMethod(operationName = "Reserve")
public String makeReservation(int cruiseId) {
// implementation
}
}
• Customiza uma operação do serviço
• Os métodos anotados com @WebMethod precisam ser public
• Os métodos anotados não podem ser static nem final
• Atributos de @WebMethod
o operationName nome do wsdl:operation
Valor default: nome do método java
o action nome da SOAPAction que constará no WSDL (apenas para bindings tipo SOAP)
Valor default: string vazia “”
WSDL: <soap:operation soapAction=”xxx”/>
o exclude não expõe o método no WebService
Pode anular um @WebMethod herdado de uma superclasse
Não pode ser usado em uma interface SEI
@SOAPBinding
• Exemplo:
@WebService
@SOAPBinding(
Gilberto Holms http://gibaholms.wordpress.com/
3. style = SOAPBinding.Style.RPC,
use = SOAPBinding.Use.LITERAL
)
public class ExampleService {
@WebMethod
public int add(int a, int b) {
return a + b;
}
}
• Customiza o tipo de binding do serviço
• Atributos de @SOAPBinding:
o style estilo da mensagem (DOCUMENT ou RPC)
Valor default: DOCUMENT
o use tipo de encoding (LITERAL ou ENCODED)
Valor default: LITERAL
O uso do tipo ENCODED é proibido pelo Basic Profile 1.1
o parameterStyle como os parâmetros serão montados (BARE ou WRAPPED)
Valor default: WRAPPED
• Possíveis combinações:
Style Use ParameterStyle Descrição
Cada parâmetro é mapeado para uma wsdl:part, que
RPC LITERAL <n/a>
pertence a um schema (padrão RPC/Literal)
Permite apenas um elemento como parâmetro, que é o root-
DOCUMENT LITERAL BARE
element de um schema (padrão Document/Literal)
Agrupa diversos parâmetros dentro de um elemento root
com o mesmo nome da wsdl:operation (“forja” um padrão
DOCUMENT LITERAL WRAPPED
Document/Literal mesmo quando é setado mais de elemento
como parâmetro)
@WebParam
• Exemplo:
@WebService
@SOAPBinding(
style = SOAPBinding.Style.RPC
)
public class PingService {
@WebMethod(operationName = "PingTwoWay")
public void ping(@WebParam(mode = WebParam.Mode.INOUT) Holder<PingDocument> ping) {
// implementation
}
@WebMethod(operationName = "SecurePing")
public void ping(PingDocument ping, @WebParam(header = true) SecurityHeader secHeader) {
// implementation
}
}
• Customiza o mapeamento de um parâmetro para uma message part
• Holder types: javax.xml.ws.Holder<Object>
• Atributos de @WebParam:
o name nome do parâmetro
Se for RPC/Literal e não foi definido @WebParam.partName, define o nome do
wsdl:part
Se for Document/Literal ou um header block, define o nome local do elemento que
representa o parâmetro
É obrigatório quando for Document/ BARE e quando mode for OUT ou INOUT
Valor default: @WebMethod.operationName para Document/BARE e nome do
argumento java para demais casos
o partName nome do wsdl:part
É utilizado apenas para estilos RPC e Document/BARE
Valor default: @WebParam.name
o targetNamespace namespace do parâmetro
Usado apenas para Document/Literal ou se a part representa um header block
o mode tipo de parâmetro (IN, OUT ou INOUT)
Gilberto Holms http://gibaholms.wordpress.com/
4. Permitido OUT e INOUT apenas para Holder types
Valor default: normalmente IN, ou INOUT se for um Holder type
o header booleano indicando se a part representa um header block
Valor default: false
@WebResult
• Exemplo:
@WebService
public class CustomerService {
@WebMethod
@WebResult(name = "CustomerRecord")
public CustomerRecord locateCustomer(
@WebParam(name = "FirstName") String firstName,
@WebParam(name = "LastName") String lastName,
@WebParam(name = "Address") USAddress addr) {
// implementation
}
}
• Customiza o mapeamento de um valor de retorno para uma message part
• É sempre a message part que não aparece no parameterOrder, ou seja, é o retorno geral do response
do operação
• Atributos de @WebResult:
o name nome do valor de retorno
Se for RPC/Literal e não foi definido @WebParam.partName, define o nome do
wsdl:part do retorno
Se for Document/Literal ou um retorno de header block, define o nome local do
elemento que representa
Valor default: @WebMethod.operationName + “Response” para Document/BARE e
“return” para demais casos
o partName nome do wsdl:part
É utilizado apenas para estilos RPC e Document/BARE
Valor default: @WebResult.name
o targetNamespace namespace do retorno
Usado apenas para Document/Literal ou se a part representa um header block
o header booleano indicando se a part representa um retorno de header block
Valor default: false
@OneWay
• Exemplo:
@WebService
public class PingService {
@WebMethod
@Oneway
public void ping() {
// implementation
}
}
• Define que trata-se de uma mensagem one-way, ou seja, omite a tag output da operation do WSDL
• Os métodos one-way:
o Precisam retornar void
o Não podem lançar exceções verificadas
o Não podem ter parâmetros Holder types
@SOAPMessageHandlers
• Não deve ser utilizada, pois foi “deprecated” a partir da versão 2.0 da JAX-WS
• No lugar dela, deve-se utilizar @HandlerChain
Gilberto Holms http://gibaholms.wordpress.com/
5. WebServiceContext
• Exemplo:
@WebService
public class Teste {
@Resource
private WebServiceContext context;
public void checkOut(int orderId) {
MessageContext messageCtx = context.getMessageContext();
SOAPMessageContext soapMessageCtx = (SOAPMessageContext)messageCtx;
SOAPMessage message = soapMessageCtx.getMessage();
}
}
• É um objeto de contexto obtido por injeção de dependência
• Fornece acesso ao contexto em que se encontra (Servlet ou EJB)
• Principais métodos de WebServiceContext:
o getUserPrincipal
o isUserInRole
o getMessageContext
• MessageContext é uma “property-bag”, um Map<String, Object> contendo referências a diversas
variáveis do contexto em que se encontra (javax.servlet.http.HttpServletRequest, Http Request
Headers, Query String, etc)
• Se for um serviço do tipo SOAP, contém uma instância de SOAPMessageContext, onde podemos
obter, por exemplo, a representação SAAJ da mensagem
@WebServiceProvider
• Exemplo de um Provider SOAP:
@WebServiceProvider(portName = "stockQuoteReporterPort", serviceName = "stockQuoteReporter")
@ServiceMode(value = Service.Mode.MESSAGE)
public class stockQuoteReporterProvider implements Provider<SOAPMessage> {
public SOAPMessage invoke(SOAPMessage request) {
SOAPBody requestBody = request.getSOAPBody();
if(requestBody.getElementName.getLocalName.equals("getStockPrice")) {
MessageFactory mf = MessageFactory.newInstance();
SOAPFactory sf = SOAPFactory.newInstance();
SOAPMessage response = mf.createMessage();
SOAPBody respBody = response.getSOAPBody();
Name bodyName = sf.createName("getStockPriceResponse");
respBody.addBodyElement(bodyName);
SOAPElement respContent = respBody.addChildElement("price");
respContent.setValue("123.00");
response.saveChanges();
return response;
}
}
}
• Exemplo de um Provider HTTP (RESTful):
@WebServiceProvider(targetNamespace = "http://example.org", serviceName = "NearbyCityService")
@BindingType(value = HTTPBinding.HTTP_BINDING)
public class NearbyCity implements Provider<Source> {
@Resource
private WebServiceContext wsContext;
public Source invoke(Source source) {
try {
MessageContext messageContext = wsContext.getMessageContext();
String query = (String) messageContext.get(MessageContext.QUERY_STRING);
if (query != null && query.contains("lat=") && query.contains("long=")) {
return createSource(query); // cria mensagem via JAXP
} else {
throw new HTTPException(404);
}
} catch (Exception e) {
throw new HTTPException(500);
}
Gilberto Holms http://gibaholms.wordpress.com/
6. }
}
• Não pode ser usada junto com a anotação @WebService
• Permite criar um serviço sem SEI, capaz de receber e responder qualquer tipo de XML, manipulando a
mensagem em baixo nível através de SAAJ ou JAXP
• Útil para criar serviços RESTful baseados em XML
• Podemos utilizar uma extensão para utilizar RESTful baseado em JSON :
o @BindingType(JSONBindingID.JSON_BINDING)
• Anotação @BindingType:
o Indica o tipo de binding (protocolo de aplicação) que será utilizado
o Principais tipos:
SOAPBinding.SOAP11HTTP_BINDING mensagem SOAP 1.1
SOAPBinding.SOAP12HTTP_BINDING mensagem SOAP 1.2
HTTPBinding.HTTP_BINDING mensagem HTTP POST
• Anotação @ServiceMode:
o Indica a forma como a mensagem será recebida no método invoke
o Varia de acordo com o tipo de binding
o Tipos permitidos:
Service.Mode.MESSAGE XML contendo o protocolo completo da mensagem
• Para SOAP, envia todo o soap:Envelope
• Para HTTP, envia Http Headers e POST Payload
Service.Message.PAYLOAD XML apenas com o payload da mensagem
• Para SOAP, envia apenas conteúdo do soap:Body
• Para HTTP, envia apenas POST Payload
• Tipos de mensagem aceitas para T na interface Provider<T>:
javax.xml.transform.Source objeto TrAX / SAX
javax.activation.DataSource objeto JAF
javax.xml.soap.SOAPMessage objeto SAAJ
Java.lang.Object objeto JAXB
• Atributos de @WebServiceProvider:
o targetNamespace namespace do serviço
o serviceName nome do serviço (caso seja baseado em WSDL, representa wsdl:service)
o portName nome da porta (caso seja baseado em WSDL, representa wsdl:port)
o wsdlLocation para criar o serviço através de um WSDL pré-existente
@WebServiceRef
• Exemplo:
public class JAXWSClient {
@WebServiceRef(
wsdlLocation = "http://localhost:8080/CalculatorService?WSDL"
)
private CalculatorService calculatorService;
public void doTest() {
Calculator port = calculatorService.getCalculatorPort();
int ret = port.add(2, 2);
}
}
• Container injeta um client de serviço através da sua classe Service
• Permitido uso apenas no contexto JEE
• Identico a tag do ejb-jar.xml: <web-service-ref>
• Atributos de @WebServiceRef:
o name nome que será gravado no JNDI-ENC
o wsdlLocation caminho para o WSDL
Valor default: o mesmo informado na anotação @WebServiceClient da classe Service
o mappedName caminho JNDI global do recurso (fornecedor)
o type não precisa definir, é o atributo anotado
o value especifica o subtipo a ser injetado no atributo anotado
Opcional se utilizado na classe Service
Gilberto Holms http://gibaholms.wordpress.com/
7. Obrigatório se utilizado na interface SEI
• Exemplos de uso de type e value:
// como a injeção é em Service impl, não precisa setar o value
@WebServiceRef
private ProcessorService processorService;
// como a injeção é em SEI Interface, precisa setar o value (Service impl a usar)
@WebServiceRef(ProcessorService.class)
private Processor processorSEI;
Tipos de JAX-WS Clients
• Tipos de Client:
o Estático com Proxy Dinâmico
Anotação @WebServiceClient
o Dinâmico com Proxy Dinâmico
Não utiliza anotações
Através das “factories” da classe javax.xml.ws.Service
o Cliente Dispatch
Totalmente dinâmico, pois trabalha a nível de XML (JAXP)
Utilizado para consumir serviços RESTful
• No JAX-WS, não existe a opção de gerar os stubs por ferramenta. Os stubs são gerados sempre
dinamicamente, em tempo de execução, utilizando o recurso de Proxy Dinâmico do Java 5. Isto torna
os clientes portáveis e tira a necessidade de re-gerar stubs sempre que mudar de fornecedor de
container
Client Estático com Proxy Dinâmico
• Exemplo:
@WebServiceClient(
name = "ProcessorService",
targetNamespace = "http://charge-it.com/Processor",
wsdlLocation = "http://charge-it.com/Processor?wsdl"
)
public class ProcessorService extends javax.xml.ws.Service {
public ProcessorService() {
super(
new URL("http://charge-it.com/Processor?wsdl"),
new QName("http://charge-it.com/Processor", "ProcessorService")
);
}
public ProcessorService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
@WebEndpoint(name = "ProcessorPort")
public Processor getProcessorPort() {
return (Processor) super.getPort(
new QName("http://charge-it.com/Processor", "ProcessorPort"),
Processor.class
);
}
}
• Os clients estáticos com proxy dinâmico:
o Precisam extender javax.xml.ws.Service
o Devem fornecer dois construtores públicos:
Construtor default: chamando super(URL, QName), que devem ser idênticos (equals)
os valores da anotação @WebServiceClient
Construtor: URL e QName, chamando super(URL, QName)
o Devem fornecer um método @WebEndpoint para cada wsdl:port do serviço, chamando
super.getPort(QName, Class) onde o parâmetro class é a interface SEI
• Atributos de @WebServiceClient:
o name nome do wsdl:service que representa
o targetNamespace namespace do serviço
Gilberto Holms http://gibaholms.wordpress.com/
8. o wsdlLocation endereço URL para o documento WSDL do WebService
• Atributos de @WebEndpoint
o name name do wsdl:port que representa
Client Dinâmico com Proxy Dinâmico
• Exemplo:
URL wsdlLocation = new URL("http://example.org/my.wsdl");
QName serviceName = new QName("http://example.org/sample", "ProcessorService");
Service s = Service.create(wsdlLocation, serviceName); // informa o WSDL
QName portName = new QName("http://example.org/sample", "ProcessorPort");
Processor processorPort = s.getPort(portName, Processor.class);
processorPort.getPrice();
• Os clients dinâmicos com proxy dinâmico:
o Não utilizam anotações
o Informam o WSDL
o Não precisam extender javax.xml.ws.Service
o Podem ser criados através da chamada Service.create(...) de duas maneiras:
Informando o caminho do WSDL (mais comum)
Sem informar o WSDL (precisa setar outros parâmetros, pois o serviço não conhecerá
os schemas de request e response)
Client Dinâmico com Dispatch
• Exemplo:
URL wsdlLocation = new URL("http://example.org/my.wsdl");
QName serviceName = new QName("http://example.org/sample", "ProcessorService");
Service s = Service.create(serviceName); // sem informar o WSDL
Source requestMsg = ...;
QName portName = ...;
Dispatch<Source> disp = s.createDispatch(portName, Source.class, Service.Mode.PAYLOAD);
Source responseMsg = disp.invoke(requestMsg);
• Os clients dinâmicos com dispatch:
o São muito utilizados para acessar serviços RESTful
o Não precisam extender javax.xml.ws.Service
o Podem operar em dois modos:
Service.Mode.MESSAGE XML contendo o protocolo completo da mensagem
Service.Message.PAYLOAD XML apenas com o payload da mensagem
o Tipos de mensagem aceitas para T na interface Dispatch<T>:
javax.xml.transform.Source objeto TrAX / SAX
javax.activation.DataSource objeto JAF
javax.xml.soap.SOAPMessage objeto SAAJ
Java.lang.Object objeto JAXB
o Tipos de chamadas síncronas e assíncronas disponíveis:
T invoke(T msg)
Response<T> invokeAsync(T msg)
Future<?> invokeAsync(T msg, AsyncHandler<T> handler)
void invokeOneWay(T msg)
BindingProvider
• Exemplo:
Service s = Service.create(...);
Processor processorPort = s.getPort(portName, Processor.class);
BindingProvider bp = (BindingProvider)processorPort;
Map<String, Object> requestContext = bp.getRequestContext();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://blah/newendpoint");
requestContext.setProperty("javax.xml.ws.session.maintain", Boolean.TRUE);
Gilberto Holms http://gibaholms.wordpress.com/
9. processorPort.getPrice();
Map<String, Object> responseContext = bp.getResponseContext();
Integer responseCode = (Integer)responseContext.get(MessageContext.HTTP_RESPONSE_CODE);
• É uma API para obter o contexto da mensagem a partir do cliente
• É uma “property-bag”, um Map<String, Object> contendo referências a diversas variáveis do contexto
da mensagem em que trabalha, tanto de request quanto response
• Podemos, por exemplo, modificar a endpoint url do stub dinamicamente, setando a propriedade
“BindingProvider.ENDPOINT_ADDRESS_PROPERTY”
• Principais métodos de BindingProvider:
o getRequestContext
o getResponseContext
o getBinding
o getEndpointReference
APIs JavaSE 6
• Endpoint
o Publica uma SEI Implementation (@WebService) em um servidor standalone (socket) JavaSE,
publicando automaticamente também o WSDL
// Create and publish an endpoint
Calculator calculator = new Calculator();
Endpoint endpoint = Endpoint.publish(
"http://localhost/calculator",
calculator
);
// Stop an endpoint
endpoint.stop();
• Service
o Cria um client standalone JavaSE para acessar um serviço
// Create a Service object
CalculatorService svc = new CalculatorService();
// Create a proxy from the Service object
Calculator proxy = svc.getCalculatorPort();
// Invoke a Web service operation
int answer = proxy.add(35, 7);
Handler Framework
Gilberto Holms http://gibaholms.wordpress.com/
10. • Interface Handler<C extends MessageContext>
o Subinterface LogicalHandler<L extends LogicalMessageContext>
Define um handler independente de protocolo
LogicalMessage LogicalMessageContext.getMessage()
• Trabalha com qualquer payload XML
o Subinterface SOAPHandler<T extends SOAPMessageContext>
Define um handler para mensagens SOAP
SOAPMessage SOAPMessageContext.getMessage()
• Trabalha com uma mensagem SOAP (SAAJ API)
• Exemplo de SOAPHandler
public class MySOAPHandler implements SOAPHandler<SOAPMessageContext> {
public boolean handleMessage(SOAPMessageContext messageContext) {
SOAPMessage msg = messageContext.getMessage();
// trata a mensagem via SAAJ
return true;
}
}
• Aplicando Handlers no Client
o 1 – Implementar HandlerResolver
List<Handler> getHandlerChain(PortInfo)
public class CardServiceHandlerResolver implements HandlerResolver {
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerChain = new ArrayList<Handler>();
ClientAuthenticationSOAPHandler authn = new ClientAuthenticationSOAPHandler();
EnvelopeLoggingSOAPHandler logging = new EnvelopeLoggingSOAPHandler();
JAXPPayloadLoggingLogicalHandler payload = new JAXPPayloadLoggingLogicalHandler();
QName serviceName = portInfo.getServiceName();
QName portName = portInfo.getPortName();
String bindingID = portInfo.getBindingID();
if (serviceName.getNamespaceURI().equals(
"http://cardservice.handler.jaxws.company.com/service")
&& serviceName.getLocalPart().equalsIgnoreCase("CardService")) {
handlerChain.add(authn);
handlerChain.add(logging);
}
if (bindingID.equals("http://schemas.xmlsoap.org/wsdl/soap/http")) {
handlerChain.add(payload);
}
Gilberto Holms http://gibaholms.wordpress.com/
11. if (bindingID
.equals("http://java.sun.com/xml/ns/jaxws/2003/05/soap/bindings/HTTP/"))
{
handlerChain.add(payload);
}
return handlerChain;
}
}
o 2 – Setar a lista de Handlers na classe Service
Service service = Service.create(wsdlLoc, serviceName);
service.setHandlerResolver(ccResolver);
• Aplicando Handlers no Server
o Anotação @HandlerChain
o Seta um arquivo de configuração
@WebService(...)
@HandlerChain(file="handler-chain.xml")
public class CreditCardServiceImpl {
}
Gilberto Holms http://gibaholms.wordpress.com/