Logovanie správ v JAX-WS 2.0 endpointe

Vieme, že vypublikovať webservice cez JAX-WS je jednoduché. Stačí zobrať 1 ks obslužnej triedy a vypublikovať ju v main():

package sk.upjs.ics.novotnyr.jaxws;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public class HelloService {
    @WebMethod
    public String sayHello() {
        return "Hello";
    }

    public static void main(String[] args) {
        Endpoint endpoint = Endpoint.publish("http://localhost:10000/ws/hello", new HelloService());
    }
}

Ako logovať správy na strane servera?

Máme dve možnosti: buď programovo alebo šperkovaním cez anotácie.

V oboch prípadoch budeme potrebovať vlastnú implementáciu SOAPHandlera, teda triedy, ktorú vieme zavesiť na reťaz objektov spracujúcich správu.

Handler môže vyzerať napr. takto:

package sk.upjs.ics.novotnyr.jaxws.log;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {

    private static final String DEFAULT_CHARSET = "UTF-8";

    private String messageCharset = DEFAULT_CHARSET;

    public static Logger LOGGER_OUTBOUND = Logger.getLogger(SOAPLoggingHandler.class.getName() + ".outbound");

    public static Logger LOGGER_INBOUND = Logger.getLogger(SOAPLoggingHandler.class.getName() + ".inbound");    

    public static Logger LOGGER = Logger.getLogger(SOAPLoggingHandler.class.getName()); 

    public Set<QName> getHeaders() {
        return null;
    }

    public boolean handleMessage(SOAPMessageContext smc) {
        logToSystemOut(smc);
        return true;
    }

    public boolean handleFault(SOAPMessageContext smc) {
        logToSystemOut(smc);
        return true;
    }

    public void close(MessageContext messageContext) {
        // nothing to clean up      
    }

    private void logToSystemOut(SOAPMessageContext soapMessageCtx) {
        try {
            SOAPMessage message = soapMessageCtx.getMessage();

            Boolean outboundProperty = (Boolean) soapMessageCtx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
            if (outboundProperty.booleanValue()) {
                LOGGER_OUTBOUND.info(toString(message));
            } else {
                LOGGER_INBOUND.info(toString(message));
            }
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, "I/O exception while logging the message.", e);
        } catch (SOAPException e) {
            LOGGER.log(Level.SEVERE, "SOAP exception while logging the message.", e);
        }
    }

    private String toString(SOAPMessage message) throws IOException, SOAPException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        message.writeTo(byteOut);

        return byteOut.toString(messageCharset);
    }

    public void setMessageCharset(String messageCharset) {
        this.messageCharset = messageCharset;
    }

    public String getMessageCharset() {
        return messageCharset;
    }
}

Programové nastavenie logovania

Použitie je nasledovné: vytvoríme endpoint cez Endpoint#create(), získanú inštanciu nakonfigurujeme a následne endpoint vypublikujeme:

    Endpoint endpoint = Endpoint.create(new HelloService());

    @SuppressWarnings("rawtypes")
    List<Handler> handlerChain = endpoint.getBinding().getHandlerChain();
    handlerChain.add(new SOAPLoggingHandler());

    endpoint.getBinding().setHandlerChain(handlerChain);

    endpoint.publish("http://localhost:10000/ws/hello");

Pozor na viacero zádrheľov!

Endpoint musíte vytvoriť, nakonfigurovať a vypublikovať!

Ak sa pokúsite nakonfigurovať inštanciu, ktorú vráti publish() (áno, publish()), nebudete úspešní. Publikovaný endpoint zjavne nemožno konfigurovať.

getHandlerChain() vracia kópiu zoznamu!

Ak sa pokúsite pridať nový handler štýlom

endpoint.getBinding().getHandlerChain().add(new SOAPLoggingHandler());

nebude to fungovať. Metóda getHandlerChain() totiž vracia kópiu zoznamu handlerov a ak doň pridáte nový prvok, endpoint zmenu neuvidí. Musíte to urobiť naookolo. Toto správanie explicitne popisuje dokumentácia, ale kto ju číta? ;-)

Nastavenie v XML

Ak máte HelloService v balíčku sk.upjs.ics.novotnyr.jaxws, môžete ho anotovať pomocou @HandlerChain, kde uvediete názov súboru s popisom handlerov, ktoré sa priradia k tomuto endpointu.

@HandlerChain(file = "handlers.xml")
public class HelloService {
    ...
}

Do adresára s balíčkom sk.upjs.ics.novotnyr.jaxws vložte handlers.xml. Naozaj, tento súbor sa musí nachádzať pri spustení v rovnakom adresári ako HelloService.class.

Obsah toť:

<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
  <handler-chain>
    <handler>
      <handler-name>log</handler-name>
      <handler-class>sk.upjs.ics.novotnyr.jaxws.log.SOAPLoggingHandler</handler-class>
    </handler>
  </handler-chain>
</handler-chains>

Následne nemusíte konfigurovať endpoint v kóde, stačí ho publish()núť a je to.

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *