Как использовать DocumentBuilder для анализа XML-документа через Socket InputStream без закрытия потока? - PullRequest
0 голосов
/ 19 сентября 2018

Есть ли способ проанализировать документ Xml из Socket InputStream, не закрывая поток на стороне клиента?Я имею контроль только на стороне сервера, получающей XML, и сокет останется открытым, поскольку сервер будет отправлять ответ клиенту.

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

Xml, который я посылаю , правильно сформирован и правильно анализируется из FileInputStream, так как имеет четкий EOF, но зависает припроанализирован из Socket InputStream, который не закрывается.

Клиент не закрывает поток после отправки Xml, потому что он ожидает ответа через сокет.

Вот мой код:

try (
    ServerSocket server = new ServerSocket(port);
    Socket sock = server.accept();
    InputStream in = sock.getInputStream(); ) {

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    db.setErrorHandler(MyErrorHandler);
    db.setEntityResolver(MyEntityResolver);
    // below hangs, waiting for stream to close I think
    Document doc = db.parse(in);

    // .. process document
    // .. send response
}   

Вот трассировка стека того, где он висит:

SocketInputStream.socketRead0(FileDescriptor, byte[], int, int, int) line: not available [native method]    
SocketInputStream.socketRead(FileDescriptor, byte[], int, int, int) line: 116   
SocketInputStream.read(byte[], int, int, int) line: 171 
SocketInputStream.read(byte[], int, int) line: 141  
XMLEntityManager$RewindableInputStream.read(byte[], int, int) line: 2919    
UTF8Reader.read(char[], int, int) line: 302 
XMLEntityScanner.load(int, boolean, boolean) line: 1895 
XMLEntityScanner.skipSpaces() line: 1685    
XMLDocumentScannerImpl$TrailingMiscDriver.next() line: 1371 
XMLDocumentScannerImpl.next() line: 602 
XMLDocumentScannerImpl(XMLDocumentFragmentScannerImpl).scanDocument(boolean) line: 505  
XIncludeAwareParserConfiguration(XML11Configuration).parse(boolean) line: 841   
XIncludeAwareParserConfiguration(XML11Configuration).parse(XMLInputSource) line: 770    
DOMParser(XMLParser).parse(XMLInputSource) line: 141    
DOMParser.parse(InputSource) line: 243  
DocumentBuilderImpl.parse(InputSource) line: 339    
DocumentBuilderImpl(DocumentBuilder).parse(InputStream) line: 121   

Спасибо за любые предложения.

Ответы [ 2 ]

0 голосов
/ 06 августа 2019

Я думаю, что я нашел хорошее решение и решил поделиться с кем-нибудь еще в подобной лодке.

Вместо того, чтобы использовать необработанный Socket, я бы использовал Netty для создания своего протокола Socket и использования XmlFrameDecoder для кадрирования сообщений и анализа байтов этого кадра в документе.

public class Main {
    private static class MyXmlHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            try (InputStream in = new ByteBufInputStream((ByteBuf) msg, true)) {
                Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in);

                // prove that we got the document
                Transformer transformer = TransformerFactory.newInstance().newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                StringWriter writer = new StringWriter();
                transformer.transform(new DOMSource(doc), new StreamResult(writer));
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        final int PORT = 8080;

        EventLoopGroup parentGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap server = new ServerBootstrap();
            server.group(parentGroup, childGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new XmlFrameDecoder(Integer.MAX_VALUE),
                                    new MyXmlHandler());
                        }
                    }).childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture channel = server.bind(PORT).sync();
            channel.channel().closeFuture().sync();
        } finally {
            childGroup.shutdownGracefully();
            parentGroup.shutdownGracefully();
        }
    }
}
0 голосов
/ 19 сентября 2018

Если поток достаточно мал, чтобы поместиться в памяти, вы можете также прочитать байты в байтовом массиве.Если он большой и вы хотите работать с потоками, взгляните на Apache Commons IOUtils , который эффективно дает вам способы скопировать InputStream в OutputStream и обработать его позже.Таким образом, поток сокетов должен оставаться открытым.

...