Создание больших сообщений MTOM / XOP с помощью JAX-WS - PullRequest
6 голосов
/ 16 декабря 2009

У меня есть вопрос об использовании MTOM / XOP с JAX-WS. Я пишу веб-сервис, который отправляет большие объемы двоичных данных. Клиент запрашивает несколько файлов, а сервер возвращает файлы в ответе.

Я могу заставить его правильно построить ответ, чтобы он правильно реализовывал XOP, но я сталкиваюсь с проблемами, связанными с памятью, потому что он сохраняет ответ весь в памяти перед отправкой. Файлы, которые отправляет этот веб-сервис, могут иметь размер очень большой (например, гигабайтный размер), поэтому сохранение ответа в памяти не вариант.

Этот веб-сайт Oracle (и наряду с этот ), похоже, решают эту проблему, но я просто не понимаю ее. Я думаю, они используют объект DataHandler для потоковой передачи запроса / ответа, но я не могу понять, как они его создают.

Я генерирую мои файлы класса JAX-WS из существующего WSDL, используя wsimport. Я использую JAX-WS RI 2.1.6 с Java 6.

Как отправить ответ, когда я его создаю, без необходимости сначала сохранять его во всей памяти?

Заранее спасибо за помощь.


ОБНОВЛЕНИЕ 12/17: Я добавил следующие атрибуты в элемент схемы в WSDL, который содержит двоичные данные. Это заставляет wsimport добавить объект DataHandler в класс JAXB. Затем к ответу можно добавить FileDataHandler вместо того, чтобы добавлять все содержимое файла, позволяя серверу передавать содержимое каждого файла, вместо того, чтобы хранить их все в памяти:

xmlns:xmime="http://www.w3.org/2005/05/xmlmime" 
xmime:expectedContentTypes="application/octet-stream"

Итак, сервер теперь правильно строит ответ, и клиент правильно сохраняет каждый файл на диск при получении запроса. Однако клиент по какой-то причине все еще читает весь ответ в память.


Код сервера (SIB):

@MTOM
@StreamingAttachment(parseEagerly = true, memoryThreshold = 4000000L) 
@WebService(...)
public class DownloadFilesPortTypeImpl implements DownloadFilesPortType {
 @Override
 public FileSetResponseType downloadFileSet(FileSetRequestType body) {
        FileSetResponseType response = new FileSetResponseType();
        for (FileRequest freq : body.getFileRequest()){
            try{
                //find the file on disk
                File file = findFile(freq.getFileId());

                //read the file data into memory
                byte[] fileData;
                {
                    FileInputStream in = new FileInputStream(file);
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    byte buf[] = new byte[8192];
                    int read;
                    while ((read = in.read(buf)) != -1){
                         out.write(buf, 0, read);
                    }
                    in.close();
                    out.close();
                    fileData = out.toByteArray();
                }

                //add the file to the response
                FileResponse fresp = new FileResponse();
                fresp.setFileId(freq.getFileId());
                fresp.setData(fileData); //<-- type "xs:base64Binary"
                response.getFileResponse().add(fresp);
            }
            catch (IOException e){
            }
        }

        return response;
 }
}

Код клиента:

DownloadFilesService service = new DownloadFilesService();
MTOMFeature mtomFeature = new MTOMFeature();
StreamingAttachmentFeature stf = new StreamingAttachmentFeature(null, true, 4000000L);
DownloadFilesPortType port = service.getDownloadFilesPortSoap12(mtomFeature, stf);

FileSetRequestType request = new FileSetRequestType();

FileRequest freq = new FileRequest();
freq.setFileId("1234");
request.getFileRequest().add(freq);

freq = new FileRequest();
freq.setFileId("9876");
request.getFileRequest().add(freq);

//...

FileSetResponseType response = port.downloadFileSet(request); //reads entire response into memory
for (FileResponse fres : response.getFileResponse()){
    byte[] data = fres.getFileData();
    //...
}

Ответы [ 2 ]

2 голосов
/ 19 июля 2011

С JAX-WS RI, включенным в Java 6 (Metro или его часть), клиентский код должен установить размер фрагмента HTTP, чтобы разрешить потоковую передачу больших вложений MTOM через DataHandler InputStream.

((BindingProvider)port).getRequestContext()
   .put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);

НО на данный момент это просто не работает из-за ошибки JAX_WS-936

В результате даже при правильном распределении по частям и использовании DataHandler вложение полностью загружается в память сразу при вызове службы. Я проверил сетевой трафик: полный контент перед передачей чтения входного потока DataHandler и использование памяти кучи Java: он увеличился, чтобы содержать как минимум три копии вложения.

2 голосов
/ 16 декабря 2009

Вы создаете свой собственный класс, реализующий DataSource, и создаете DataHandler, передавая его. Он может даже быть анонимным.

В Apache CXF мы делаем это намного проще. Вы можете просто получить 'getter', который возвращает DataSource или DataHandler. Сложная схема в выложенном вами коде мне не знакома.

Я думаю, что те же методы работают и с JDK JAX-WS + JAXB. Смотрите это .

...