11.07.2016

Еще раз о java памяти и сборке мусора



Давайте начнем с простых вещей,  с типов языка java, существуют в нем  два типа данных примитивы и ссылочные типы или точнее сказать указатели на память. Для примера:

int a = 10; // примитивный тип
Person person = new Person(“Vit”); //сыллочный тип

 Особенность типов языка java  в том что в не зависимости от  JVM и платформы на которой JVM  крутится,  будь то Linux или Windows  размерность типов остается постоянной.  В зависимости от разрядности системы, указатели на память могут быть или 32-разрядными или 64-разрядными. Когда полю объекта ссылочного типа присваивается другой объект ссылочного типа, обычно копируется только ссылка на сам объект и при этом не создается новый объект. Рассмотрим наш пример:

Person person = new Person(“Vit”); //создаем ссылочный тип
Person person2 =person; // скопировали ссылку на объект Person(“Vit”); теперь на один объект ссылаются две переменные ссылочного типа person2 и person.
person2.setName(“Oleg”);//  и если мы изменим значение имени в ссылке person2 System.out.println(person.getName);// то при выводе объекта person  мы получим значение Oleg т.к. мы работаем с одним и тем же объектом

Продолжим,  любая программа на языке java по умолчанию запускается в главном потоке  (Thread), потоков  вы можете запустить столько, сколько вам нужно.  Поток работает с двумя видами памяти JVM: Стек и Куча.  Куча – это разделяемая или общая память где создаются объекты на которые указывают ссылочные переменные.  Стек – это место где создаются локальные переменные. Локальные переменные-примитивы  хранят свое значение  прямо в стеке, а локальные переменные-ссылочного типа будут хранить в стеке только  указатель на объект в Куче.  Любой поток имеет свой собственный стек, а куча одна для всех потоков смотрите рисунок.



Сделаем небольшое отступление, из предыдущего раздела можно сделать вывод, что ко всем ссылочным типам обязательно нужно применять синхронизацию доступа к объекту в Куче, т.е. если   доступ к одному и тому же объекту в Куче имеют разные потоки. Это правило не относится к локальным ссылочным объектам, т. к. область видимости локальных объектов в куче ограничивается только одним потоком, и по этому они не могут быть видны в другом потоке. В языке java  Кучей называется  Java Heap.  И так Java Heap – это память jvm  выделяемая во время запуска, где хранятся и живут все создаваемые нами объекты, все нелокальные переменные, массивы и т. д. Обрабатывается он, специальным менеджером памяти автоматически, который называют garbage collector (GC) - сборщиком мусора. Вы можете попросить GC только выделить память, когда вы создаете объект   ключевым словом  new,  а  сборку мусора - освобождение памяти он делает автоматически. Хотя есть два метода  System.gc() и Runtime.gc() которые, просят “GC  запуститься”, но на них нельзя полагаться, поскольку другие более приоритетные потоки могут отложить их выполнение.  JVM  поставляется с несколькими типами GC, которые можно выбрать и настроить под свои нужды посредством параметров  которые передаются при запуске jvm. Бывают случаи когда не хватает памяти процессу JVM,  тогда  возникает ошибка java.lang.OutOfMemoryError, об этой ошибке можно подробней почитать здесь, так же я вам советую прочитать статью о виртуальной машине Java,   хотя информация в них уже немного устарела. А устарела она в том, что  с 8 версии jvm, PermSize уже не используется и его заменили областью памяти которая называется class metadata. До 8 версии jvm, PermSize была составной частью Java Heap.  И как сказано в статьях, там хранятся все метаданные загруженные  ClassLoader’ом , еще эту память называют  словом  Method Area. Пожалуйста ознакомтесь с моей статьей о памяти которой обладает процесс jvm.  Я думаю с вас достаточно пока этой информации. У меня есть планы написать  в следующих статьях о разных типах GC и их настройке. А на сегодня я думаю вам хватит этой информации которую я вам дал по ссылкам, не спеша переварите её.

10.04.2016

Web Service – JAX-WS, SOAP message handler, SOAP logical handler



Веб сервисам и их клиентам может понадобиться доступ к обработке SOAP сообщения, для этого создаются SOAP  перехватчики (SOAP message handler). SOAP handler  вызываются автоматически, для входного (Outbound Message) сообщения, которое приходит к нам и выходного (Inbound Message) сообщения, уходящего от нас.  JAX-WS handler делятся на два типа обработчиков: SOAP handler  и logical handler.   Различие  в том что, SOAP handler  имеет больше возможностей, а именно имеет доступ к SOAP  сообщению полностью, к заголовкам(Headers) и телу(Body), а  logical handler только к телу(Body).  Для того что бы написать обработчики вам нужно реализовать интерфейсы для SOAP handler - javax.xml.ws.handler.soap.SOAPHandler, а для  logical handler - javax.xml.ws.handler.LogicalHandler.  У этих  интерфейсов есть три одинаковых метода, которые реализуются в перехватчиках и ведут себя одинаково, рассмотрим их подробно. Первый метод который вам нужно реализовать это public boolean handleMessage(“Context” context), тип  “Context”  принимает значение или LogicalMessageContext или SOAPMessageContext, в зависимости от реализации интерфейса. Здесь нужно сказать про цепочку вызовов (handler-chain), у вас может быть множество перехватчиков (handler), которые выполняются друг за другом.  Тип Boolean который возвращает метод  handleMessage, говорит что нужно  делать дальше, если true -  после завершения метода, переходим к вызову следующего перехватчика, а точнее метода handleMessage следующего обработчика. Если вы передаете false, то прерывается всякая последующая обработка и выполнение операции веб сервиса. Представте себе такую ситуацию, что вы проверяете права доступа, и если нет у пользователя прав на вызов операции web  сервиса то вы прерываете вызов, вернув параметр false, все  просто. Если происходит любая исключительная ситуация, то опять же прерывается обработка цепочки вызовов и выполняется второй метод   public boolean handleFault(Context context),  в зависимсти от того что вы вернете в методе обработчика зависит поведение всего вызова операции. Если вы вернете true после обработки ошибки, то будет продолжена цепочка вызовов  и  выполнение  перейдет следующему  обработчику в цепочке. Если передатите false,  то клиенту который вызвал операцию web сервиса вернется ошибка и прекратится вся последующая обработка вызовов. Следующий метод это public void close(), этим методом завершается работа обработчика, если вы открывали какие-нибудь ресурсы то в этом месте нужно их закрыть и освободить.   Обработчики  могут регистрироваться как на клиенте, так и на web сервисе. Есть у обработчиков последовательность выполнения, сначала выполняются все Logical Handlers, затем Message Handlers на клиенте, потом пройдя по транспортному протоколу, в нашем случае HTTP  опять выполняются Message Handlers на web  сервисе, потом Logical Handlers и только после этого вызывается операция web сервиса. Ответ web  сервиса проходит тот же путь, в той же последовательности  через все обработчики, смотрите рисунок.



Давайте напишем пример:  два обработчика Logical Handler, по одному для клиента и для web сервиса. И два обработчика Message Handler, опять же один пусть будет на стороне клиента, один на стороне web сервиса.  Потом я вам покажу как подключить обработчики клиенту и web  сервису, использовать будем пример из прошлой статьи. Обрабочик SOAPHandlerClient.java:

package org.lopanov;

import javax.xml.namespace.QName;
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;
import java.util.Set;

public class SOAPHandlerClient implements SOAPHandler<SOAPMessageContext> {
    @Override
    public Set<QName> getHeaders() {
        return null;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        Boolean b = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (b) {
            System.out.printf("%s","Исходящее сообщение SOAPHandlerClient");
        } else {
            System.out.printf("%s","Входящее сообщение SOAPHandlerClient");
        }
        return true;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return false;
    }

    @Override
    public void close(MessageContext context) { }
}

Обрабочик LogicalHandlerClient.java:

package org.lopanov;

import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;

public class LogicalHandlerClient implements LogicalHandler<LogicalMessageContext> {
    @Override
    public boolean handleMessage(LogicalMessageContext context) {
        Boolean b = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (b) {
            System.out.printf("%s", "Исходящее сообщение LogicalHandlerClient");
        } else {
            System.out.printf("%s", "Входящее сообщение LogicalHandlerClient");
        }
        return true;
    }

    @Override
    public boolean handleFault(LogicalMessageContext context) {
        return false;
    }

    @Override
    public void close(MessageContext context) {
    }
}

Обрабочик SOAPHandlerWebService.java:

package org.lopanov;

import javax.xml.namespace.QName;
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;
import java.util.Set;

public class SOAPHandlerWebService implements SOAPHandler<SOAPMessageContext> {
    @Override
    public Set<QName> getHeaders() {
        return null;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        Boolean b = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (b) {
            System.out.printf("%s", "Исходящее сообщение SOAPHandlerWebService");
        } else {
            System.out.printf("%s", "Входящее сообщение SOAPHandlerWebService");
        }
        return true;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return false;
    }

    @Override
    public void close(MessageContext context) { }
}

Обрабочик LogicalHandlerWebService.java:

package org.lopanov;

import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;

public class LogicalHandlerWebService implements LogicalHandler<LogicalMessageContext> {
    @Override
    public boolean handleMessage(LogicalMessageContext context) {
        Boolean b = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (b) {
            System.out.printf("%s", "Исходящее сообщение LogicalHandlerWebService");
        } else {
            System.out.printf("%s", "Входящее сообщение LogicalHandlerWebService");
        }
        return true;
    }

    @Override
    public boolean handleFault(LogicalMessageContext context) {
        return false;
    }

    @Override
    public void close(MessageContext context) { }
}

Два обработчика SOAPHandlerClient.java и  LogicalHandlerClient.java  зарегистрируем на  клиенте. Для этого мы должны реализовать интерфейс HandlerResolver и в его методе public List<Handler> getHandlerChain(PortInfo portInfo), добавить в цепочку обработчиков два наших класса. На клиенте после создания сервиса добавить  класс реализующий наш интерфейс, перед вызовом любых операций web  сервиса. Смотрим  пример:

package org.lopanov;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class Main1 {
    private static final QName SERVICE_NAME = new QName("http://lopanov.org/", "HelloWorldService");
    private static final QName PORT_NAME = new QName("http://lopanov.org/", "HelloWorldPort");

    public static void main(String args[]) throws Exception {
        Service service = Service.create(new URL("http://169.254.201.140:7001/hello-ws/HelloWorldService?WSDL"), SERVICE_NAME);
        service.setHandlerResolver(//устанавливае цепочку вызовов
                new HandlerResolver() {
                    @Override
                    public List<Handler> getHandlerChain(PortInfo portInfo) {
                        List<Handler> list = new ArrayList<Handler>();
                        Handler handler = new SOAPHandlerClient();
                        list.add(handler);
                        Handler handler1 = new LogicalHandlerClient();
                        list.add(handler1);
                        return list;
                    }
                }
        );     
        Hello hw = service.getPort(PORT_NAME, Hello.class);       
        System.out.println(hw.sayHello("World"));
        System.exit(0);
    }
}

Получили вывод лога:

Исходящее сообщение LogicalHandlerClient
Исходящее сообщение SOAPHandlerClient
---[HTTP request - http://169.254.201.140:7001/hello-ws/HelloWorldService]---
….
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<S:Body>
                           <ns0:sayHelloResponse xmlns:ns0="http://lopanov.org/">
                               <return>Hello, World!</return>
                           </ns0:sayHelloResponse>
</S:Body>
</S:Envelope>
Входящее сообщение SOAPHandlerClient
Входящее сообщение LogicalHandlerClient
Hello, World!

Подключим обработчики на стороне web сервиса,  делается это посредством аннотации @HandlerChain(), передав в атрибуте file  имя файла где описаны обработчики. Файл представляет собой xml  документ с пространством имен xmlns="http://java.sun.com/xml/ns/javaee", для начало опишем MyHandlers.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
    <handler-chain>
        <handler>
            <handler-name>SOAPHandlerWebService</handler-name>
            <handler-class>org.lopanov.SOAPHandlerWebService</handler-class> указываем подключаемы обработчик
        </handler>
    </handler-chain>
    <handler-chain>
        <handler>
            <handler-name>LogicalHandlerWebService</handler-name>
            <handler-class>org.lopanov.LogicalHandlerWebService</handler-class> указываем подключаемы обработчик
        </handler>
    </handler-chain>
</handler-chains>
 
Добавим аннотацию в наш web  сервис:

package org.lopanov;

import weblogic.jws.Policies;
import weblogic.jws.Policy;
import javax.jws.HandlerChain;
import javax.jws.WebService;

@WebService( endpointInterface = "org.lopanov.Hello")
@Policies( { @Policy(uri = "policy:Mtom.xml",attachToWsdl = true,direction = Policy.Direction.both) } )
@HandlerChain(file = "../../MyHandlers.xml")
public class HelloWorld implements Hello {

    public String sayHello(String name){
        String result = String.format("Hello, %s!", name);
        System.out.println(result);
        return result;
    }
….
}

На выходе у сервера  Weblogic получим такой лог:

Исходящее сообщение LogicalHandlerWebService
Исходящее сообщение SOAPHandlerWebService
Hello, World!
Входящее сообщение SOAPHandlerWebService
Входящее сообщение LogicalHandlerWebService

Давайте я вам раскажу как можно получить доступ к телу сообщения в LogicalHandler, это делается двумя способами посредством доступа к xml  содержимому тела или посредством доступа к объекту JAXB. И так в методе handleMessage() передается нам LogicalMessageContext context,   посредством его мы можем получить доступ к свойсвам контекста с помощью метода get(). Вызов context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)   в примере нам возвращал переменую которая подсказывала исходящее или входящее пришло к нам сообщение. Полный перечень свойств вы можете посмотреть в документации перейдя по ссылке http://docs.oracle.com/javaee/6/api/javax/xml/ws/handler/MessageContext.html. Продолжим,  посредством вызова метода   context.getMessage()  мы можем получить доступ к LogicalMessage message. У объекта LogicalMessage есть метод getPayload(), если его вызвать без параметров, то он возвращает нам объект класса javax.xml.transform.Source, который дает возможность работать с телом сообщения как с xml  документом, читать его и писать в него. Расмотрим пример:

package org.lopanov;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.activation.DataHandler;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.LogicalMessage;
import javax.xml.ws.ProtocolException;
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;
import java.util.List;
import java.util.Map;


public class LogicalHandlerClient implements LogicalHandler<LogicalMessageContext> {
    @Override
    public boolean handleMessage(LogicalMessageContext context) {
        Boolean b = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (b) {
            System.out.printf("%s", "Исходящее сообщение LogicalHandlerClient\n");
            System.out.println("Response code " + context.get(MessageContext.HTTP_RESPONSE_CODE));
            try {
                LogicalMessage message = context.getMessage();//получили сообщение
                Source source = message.getPayload();//получили источник
                Transformer xFormer = TransformerFactory.newInstance().newTransformer();
                xFormer.setOutputProperty("omit-xml-declaration", "yes");
                DOMResult result = new DOMResult();
                xFormer.transform(source, result);//преобразовали источник к DOM  объекту, можно работать как с xml документом
                Node node = result.getNode();
                NodeList nodes = node.getChildNodes();
                for (int i = 0; i < nodes.getLength(); i++) {
                    Node current = nodes.item(i);
                    System.out.printf("\n-=%s=-\n", current.getLocalName().toString());
                    if ("sayHello".equals(current.getLocalName())) {//нашли тег
                        current.getFirstChild().setTextContent("XXX1"); //установили значение тега
                    }
                }
                source = new DOMSource(node);
                message.setPayload(source);//нужно вернуть изменненный нами объект обратно впоток передачи
            } catch (Exception ex) {
                throw new ProtocolException(ex);
            }
        } else {
            System.out.printf("%s", "Входящее сообщение LogicalHandlerClient");
           
        }

        return true;
    }

    @Override
    public boolean handleFault(LogicalMessageContext context) {
        return false;
    }

    @Override
    public void close(MessageContext context) { }
}

Был такой лог,  до вызова нашего перехвачика LogicalHandlerClient:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<S:Body>
<ns0:sayHello xmlns:ns0="http://lopanov.org/">
                           <arg0>World</arg0>
</ns0:sayHello>
</S:Body></S:Envelope>

Получили такой лог после вызова нашего перехвачика LogicalHandlerClient:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<S:Body>
<ns0:sayHello xmlns:ns0="http://lopanov.org/">
                           <arg0>XXX1</arg0>
</ns0:sayHello>
</S:Body></S:Envelope>

Можно заменить любой тег или добавить новый, делать с телом xml  документа что угодно. Перейдем к работе с JAXB  объектом, для него нужно сделать еще одну дополнительную вещь, создать ObjectFactory.java, делается это путем вызова команды:

C:\jboss-4.2.0.GA\vit\hello-ws\target>wsimport.exe HelloWorldService.xml -s src
parsing WSDL...
Generating code...
Compiling code...  

На выходе мы получаем  набор классов и фабрику которая поможет нам создать наш JAXBContext.  HelloWorldService.xml – это wsdl документ который вы можете получить перейдя на свой развернутый web  сервис, я его получил по адресу http://169.254.201.140:7001/hello-ws/HelloWorldService?WSDL. wsimport.exe - это утилита входит в поставку jdk, генерирует классы по wsdl  документу, src  каталог куда будут сгенерированы наши классы, он должен существовать перед запуском утилиты, иначе будет ошибка генерации. Привожу полный список классов которые были сгерерированы:

package-info.java
ObjectFactory.java
Person.java
SayHello.java
SayHelloResponse.java
SayHi.java
SayHiResponse.java
GetBinary.java
GetBinaryResponse.java
Hello.java
HelloWorldService.java

Их я перекинул к себе в проект, кроме помеченных красным цветом, т. к. эти классы я писал вручную. Продолжим,  посредством вызова метода   context.getMessage()  мы можем получить доступ к LogicalMessage message. У объекта LogicalMessage есть метод getPayload(), если его вызвать с  параметром ObjectFactory.class, то он возвращает нам объекты класса JAXBElement, которые дают возможность работать с телом сообщения как с объектом JAXB, читать его свойства и писать в  его свойства, работать будем с JAXB объектом SayHello. Расмотрим пример:

package org.lopanov;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.activation.DataHandler;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.ws.LogicalMessage;
import javax.xml.ws.ProtocolException;
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;
import java.util.List;
import java.util.Map;


public class LogicalHandlerClient implements LogicalHandler<LogicalMessageContext> {
    @Override
    public boolean handleMessage(LogicalMessageContext context) {
        Boolean b = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (b) {
            System.out.printf("%s", "Исходящее сообщение LogicalHandlerClient\n");
            try {
                   LogicalMessage message = context.getMessage();//получаем сообщение
                   JAXBContext jaxbContext = JAXBContext.newInstance(org.lopanov.ObjectFactory.class);//создаем контекст с нашей фабрикой
                   Object payload = message.getPayload(jaxbContext);
                   Object value = payload;
                   if (payload instanceof JAXBElement) { //проверяем вернули нам  JAXBElement
                       value = ((JAXBElement<?>)payload).getValue();
                   }
                   if (value instanceof SayHello) {//Наш JAXB  объект?
                       SayHello req = (SayHello)value;// получаем его
                       req.setArg0("Hello JAXB!");//производим измениния
                       message.setPayload(payload, jaxbContext);// Сохраняем наши измения и отправляем запрос дальше в web сервис.
                   }

            } catch(JAXBException e) {
                e.printStackTrace();
            }
        } else {
            System.out.printf("%s", "Входящее сообщение LogicalHandlerClient");        
                    }
        return true;
    }

    @Override
    public boolean handleFault(LogicalMessageContext context) {
        return false;
    }

    @Override
    public void close(MessageContext context) {
    }
}

Был такой лог,  до вызова нашего перехвачика LogicalHandlerClient:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<S:Body>
<ns0:sayHello xmlns:ns0="http://lopanov.org/">
                           <arg0>World</arg0>
</ns0:sayHello>
</S:Body></S:Envelope>

Получили такой лог после вызова нашего перехвачика LogicalHandlerClient:


<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<S:Body>
<ns0:sayHello xmlns:ns0="http://lopanov.org/">
                           <arg0>Hello JAXB!</arg0>
</ns0:sayHello>
</S:Body></S:Envelope>

Приведу наш JAXB  объект и фабрику классов:

package org.lopanov;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "sayHello", propOrder = {
    "arg0"
})
public class SayHello {
    protected String arg0;

    public String getArg0() {
        return arg0;
    }

    public void setArg0(String value) {
        this.arg0 = value;
    }
}

package org.lopanov;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;

@XmlRegistry
public class ObjectFactory {
    private final static QName _GetBinary_QNAME = new QName("http://lopanov.org/", "getBinary");
    private final static QName _SayHelloResponse_QNAME = new QName("http://lopanov.org/", "sayHelloResponse");
    private final static QName _SayHi_QNAME = new QName("http://lopanov.org/", "sayHi");
    private final static QName _GetBinaryResponse_QNAME = new QName("http://lopanov.org/", "getBinaryResponse");
    private final static QName _SayHello_QNAME = new QName("http://lopanov.org/", "sayHello");
    private final static QName _SayHiResponse_QNAME = new QName("http://lopanov.org/", "sayHiResponse");
    private final static QName _Person_QNAME = new QName("http://lopanov.org/", "person");
    private final static QName _GetBinaryArg0_QNAME = new QName("", "arg0");

    public ObjectFactory() {}

    public SayHello createSayHello() {
        return new SayHello();
    }

    public SayHiResponse createSayHiResponse() {
        return new SayHiResponse();
    }

    public Person createPerson() {
        return new Person();
    }

    public GetBinary createGetBinary() {
        return new GetBinary();
    }

    public SayHelloResponse createSayHelloResponse() {
        return new SayHelloResponse();
    }

    public SayHi createSayHi() {
        return new SayHi();
    }

    public GetBinaryResponse createGetBinaryResponse() {
        return new GetBinaryResponse();
    }

    @XmlElementDecl(namespace = "http://lopanov.org/", name = "getBinary")
    public JAXBElement<GetBinary> createGetBinary(GetBinary value) {
        return new JAXBElement<GetBinary>(_GetBinary_QNAME, GetBinary.class, null, value);
    }

    @XmlElementDecl(namespace = "http://lopanov.org/", name = "sayHelloResponse")
    public JAXBElement<SayHelloResponse> createSayHelloResponse(SayHelloResponse value) {
        return new JAXBElement<SayHelloResponse>(_SayHelloResponse_QNAME, SayHelloResponse.class, null, value);
    }

    @XmlElementDecl(namespace = "http://lopanov.org/", name = "sayHi")
    public JAXBElement<SayHi> createSayHi(SayHi value) {
        return new JAXBElement<SayHi>(_SayHi_QNAME, SayHi.class, null, value);
    }

    @XmlElementDecl(namespace = "http://lopanov.org/", name = "getBinaryResponse")
    public JAXBElement<GetBinaryResponse> createGetBinaryResponse(GetBinaryResponse value) {
        return new JAXBElement<GetBinaryResponse>(_GetBinaryResponse_QNAME, GetBinaryResponse.class, null, value);
    }

    @XmlElementDecl(namespace = "http://lopanov.org/", name = "sayHello")
    public JAXBElement<SayHello> createSayHello(SayHello value) {
        return new JAXBElement<SayHello>(_SayHello_QNAME, SayHello.class, null, value);
    }

    @XmlElementDecl(namespace = "http://lopanov.org/", name = "sayHiResponse")
    public JAXBElement<SayHiResponse> createSayHiResponse(SayHiResponse value) {
        return new JAXBElement<SayHiResponse>(_SayHiResponse_QNAME, SayHiResponse.class, null, value);
    }

    @XmlElementDecl(namespace = "http://lopanov.org/", name = "person")
    public JAXBElement<Person> createPerson(Person value) {
        return new JAXBElement<Person>(_Person_QNAME, Person.class, null, value);
    }

    @XmlElementDecl(namespace = "", name = "arg0", scope = GetBinary.class)
    public JAXBElement<byte[]> createGetBinaryArg0(byte[] value) {
        return new JAXBElement<byte[]>(_GetBinaryArg0_QNAME, byte[].class, GetBinary.class, ((byte[]) value));
    }
}


Перейдем теперь к обработчику SOAPHandler, он возвращает нам SOAPMessageContext context   который дает нам возвожнось вызвав метод context.getMessage(), получить объект SOAPMessage message, а это уже доступ к технологии SAAJ которая уже позволяет дотянуться до тела сообщения(Body), до его заголовков (Headers) и к присоединенным объектам(Attachments) – объектам MTOM. Если не вкурсе что за объекты MTOM,  то читайте мои предыдущие статьи.  Приведу пример как добавить два тега в заголовок(Headers):

package org.lopanov;

import javax.xml.namespace.QName;
import javax.xml.soap.*;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.util.Set;

public class SOAPHandlerClient implements SOAPHandler<SOAPMessageContext> {
    @Override
    public Set<QName> getHeaders() {
        return null;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        Boolean b = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        SOAPMessage message = context.getMessage();
        if (b) {
            System.out.printf("%s","Исходящее сообщение SOAPHandlerClient");
            try {
                SOAPElement element1= SOAPFactory.newInstance().createElement(new QName("http://lopanov.org/","test"));//создаю 1-й SOAPElement – тег test
                element1.addTextNode("Vit");// устанавливаю значение
                SOAPElement element2= SOAPFactory.newInstance().createElement(new QName("http://lopanov.org/","test"));//создаю 1-й SOAPElement – тег test
                element2.addTextNode("Hello");// устанавливаю значение
                message.getSOAPPart().getEnvelope().getHeader().addChildElement(element1);//дотягиваюсь посредством технологии SAAJ до заголовка и устанавливаю 1-й тег
                message.getSOAPPart().getEnvelope().getHeader().addChildElement(element2);//устанавливаю 2-й тег
            } catch (SOAPException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        } else {
            System.out.printf("%s","Входящее сообщение SOAPHandlerClient");
        }
        return true;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return false;
    }

    @Override
    public void close(MessageContext context) { }
}

Был такой лог,  до вызова нашего перехвачика SOAPHandlerClient:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<S:Body>
<ns0:sayHello xmlns:ns0="http://lopanov.org/">
                           <arg0>World</arg0>
</ns0:sayHello>
</S:Body></S:Envelope>

Получили такой лог после вызова нашего перехвачика SOAPHandlerClient:

<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
                           <test xmlns="http://lopanov.org/">Vit</test>
                           <test xmlns="http://lopanov.org/">Hello</test>
</env:Header>
<S:Body>
<ns0:sayHello xmlns:ns0="http://lopanov.org/">
                           <arg0>World</arg0>
</ns0:sayHello>
</S:Body></S:Envelope>

Вот и все о перехватчиках.