Как я могу поручить JAXB Marshaller очищать base64Binary данные только для аудита? - PullRequest
2 голосов
/ 18 января 2012

Я работаю в среде веб-службы JAXWS / JAXB. JAXWS «из коробки» использует JAXB для маршалинга / деинсталляции полезных нагрузок веб-сервиса.

У меня также есть требование проверять все полезные данные запросов и ответов.

Мне нужно компактное и сжатое маршалированное представление полезной нагрузки для аудита (в качестве неуместного примечания - я выполняю аудит с использованием java.util.concurrent.BlockingQueue и некоторых потребительских потоков для помещения пакетов данных аудита в источник данных аудита ).

У меня есть двоичный контент (mtom), включенный в некоторые полезные нагрузки ответов веб-службы, но я НЕ хочу проводить их аудит, потому что сериализованный base64 будет слишком большим.

Таким образом, мне нужно создать маршаллер (исключительно для аудита), который во всех случаях будет очищать двоичный контент, но затем НЕ очищать для основной цели распределения полезных нагрузок ответа веб-службы. Я делаю XSD для Java XJC. Мне нужно использовать одно и то же пространство имен XSD / JAXB для обоих контекстов / маршаллеров.

Преобразователь типов Java:

<jxb:javaType name=""
        parseMethod="com.xxx.xxx.ws.converter.XXXLongConverter.parseXXXLong" 
            printMethod="com.xxx.xxx.ws.converter.XXXLongConverter.printXXXLong" />

не будет работать, потому что 1. Мне нужно отменить регистрацию адаптера http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/Marshaller.html#setAdapter%28java.lang.Class,%20A%29 для маршаллера, и я не думаю, что у меня есть крюк для JAXWS. 2. Я не могу гарантировать имя класса, которое JAXB решит создать, чтобы отменить его регистрацию.

Я создал свой собственный XMLAdapter и использовал плагин annox jaxb но на самом деле это не сработало по тем же причинам, что и вышесказанное.

Обновление: теперь я попытался вручную и рефлексивно пройтись по полезной нагрузке (подлежащей аудиту) перед сортировкой, чтобы очистить двоичные данные, но это должно быть слишком большой болью для того, что оно того стоило.

Следует также отметить, что для краткости аудита я использую джерси Сериализация JSON с поддержкой JAXB но я не думаю, что это убирает или увеличивает мою базовую проблему:

Как я могу очистить данные в одном маршаллере / демаршаллере, но не в другом, но в обоих источниках с одинаковым контекстом JAXB?

ОБНОВЛЕНИЕ: Никогда не придумал элегантного способа сделать это. На самом деле не возможно на данном этапе с такими структурами, как они есть. ОБНОВЛЕНИЕ: Не правда. Расширение AttachmentMarshaller (мне это очень нравится и я буду его использовать) или создание XmlAdapter с учетом потребностей будет работать для конкретного маршаллера аудита, как ответит @Blaise ниже.

ОБНОВЛЕНИЕ: Если я могу сделать еще один шаг, чтобы завершить мой вариант использования ... Я упомянул выше, что для краткости аудита я хотел бы выполнить серию Json для JSONJAXBContext с использованием трикотажа apis, в частности, используя JSONMarshaller, но интерфейс не определяет setAdapter и setAttachmentMarshaller. Выход JSONJAXBContext.createJSONMarshaller () является реализацией JSONMarshallerImpl , которая определяет эти методы. Я буду неохотно приводить в impl, чтобы я мог установить свой собственный маршаллер вложения.

Ответы [ 2 ]

2 голосов
/ 22 декабря 2012

Как я могу очистить данные в одном маршаллере / демаршаллере, но не в другом, но оба, чье происхождение совпадает с контекстом JAXB?


Вы можете установить собственную реализацию AttachemntMarshaller и установите его на Marshaller, который вы используете для аудита.

Root

Ниже приведен пример объекта домена со свойством byte[], которое по умолчаниюбудет представлен как элемент типа base64Binary.

package forum8914008;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    byte[] bytes;

}

Demo

Демонстрационный код ниже сначала маршалирует объект в XML, а затем марширует егово второй раз с пользовательской реализацией AttachmentMarshaller set.

package forum8914008;

import javax.activation.DataHandler;
import javax.xml.bind.*;
import javax.xml.bind.attachment.AttachmentMarshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Root root = new Root();
        root.bytes = "Hello World".getBytes();

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);

        marshaller.setAttachmentMarshaller(new AttachmentMarshaller() {

            @Override
            public boolean isXOPPackage() {
               return true;
            }

            @Override
            public String addMtomAttachment(DataHandler arg0, String arg1,
                    String arg2) {
                return "fake";
            }

            @Override
            public String addMtomAttachment(byte[] arg0, int arg1, int arg2,
                    String arg3, String arg4, String arg5) {
                return "fake";
            }

            @Override
            public String addSwaRefAttachment(DataHandler arg0) {
                return "fake";
            }

        });
        marshaller.marshal(root, System.out);
    }

}

Output

Ниже приведен выход из запуска демонстрационного кода.Первый XML-документ может вырасти до довольно большого размера, если byte [] будет большим.Второй XML-документ будет иметь тот же размер.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <bytes>SGVsbG8gV29ybGQ=</bytes>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <bytes>
        <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="fake"/>
    </bytes>
</root>
0 голосов
/ 22 декабря 2012

Как я могу очистить данные в одном маршаллере / демаршаллере, но не в другом но оба, чье происхождение является одним и тем же контекстом JAXB?


Вы можете поддержать этот вариант использования с XmlAdapter.

XmlAdapter (ByteArrayAdapter)

Следующая XmlAdapter используется для преобразования byte[] в byte[]. В своем состоянии по умолчанию он вернет исходный byte[], он также имеет состояние аудита, где он вернет пустой байт [].

package forum8914008;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class ByteArrayAdapter extends XmlAdapter<byte[], byte[]> {

    private boolean audit;

    public ByteArrayAdapter() {
        this(false);
    }

    public ByteArrayAdapter(boolean audit) {
        this.audit = audit;
    }

    @Override
    public byte[] marshal(byte[] bytes) throws Exception {
        if(audit) {
            return new byte[0];
        }
        return bytes;
    }

    @Override
    public byte[] unmarshal(byte[] bytes) throws Exception {
        return bytes;
    }

}

пакет-информация

Аннотация @XmlJavaTypeAdapter используется для регистрации XmlAdapter. При использовании на уровне пакета он будет применяться ко всем свойствам указанного типа в этом пакете (см .: http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html).

@XmlJavaTypeAdapter(value=ByteArrayAdapter.class, type=byte[].class)
package forum8914008;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

Root

Ниже приведен пример объекта домена со свойством byte [], которое по умолчанию будет представлено как элемент типа base64Binary.

package forum8914008;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    byte[] bytes;

}

Демо

Демонстрационный код ниже сначала маршалирует объект с состоянием по умолчанию ByteArrayAdapter, которое будет возвращать действительное byte[], и маршалирует объект с набором ByteArrayAdapter с состоянием, который преобразует все byte[] значения в пустое byte[].

package forum8914008;

import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Root root = new Root();
        root.bytes = "Hello World".getBytes();

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);

        marshaller.setAdapter(new ByteArrayAdapter(true));
        marshaller.marshal(root, System.out);
    }

}

выход

Ниже приведен результат запуска демонстрационного кода. XmlAdapter будет применяться ко всем сопоставленным полям / свойствам типа byte[].

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <bytes>SGVsbG8gV29ybGQ=</bytes>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <bytes></bytes>
</root>
...