Pergunta

Eu tenho um serviço web Java em JAX-WS que retorna um OutputStream de outro método. Eu não consigo descobrir como para transmitir o OutputStream para o DataHandler voltou qualquer outra maneira do que para criar um arquivo temporário, escrever para ele, em seguida, abri-lo novamente para cima como um InputStream. Aqui está um exemplo:

@MTOM
@WebService
class Example {
    @WebMethod
    public @XmlMimeType("application/octet-stream") DataHandler service() {
        // Create a temporary file to write to
        File fTemp = File.createTempFile("my", "tmp");
        OutputStream out = new FileOutputStream(fTemp);

        // Method takes an output stream and writes to it
        writeToOut(out);
        out.close();

        // Create a data source and data handler based on that temporary file
        DataSource ds = new FileDataSource(fTemp);
        DataHandler dh = new DataHandler(ds);
        return dh;
    }
}

A questão principal é que o método writeToOut () pode retornar dados que são muito maiores do que a memória do computador. É por isso que o método está usando MTOM em primeiro lugar - para transmitir os dados. Eu não consigo envolver minha cabeça em torno de como transmitir os dados diretamente do OutputStream que eu preciso para fornecer ao DataHandler retornado (e, finalmente, o cliente, que recebe o StreamingDataHandler).

Eu tentei brincar com PipedInputStream e PipedOutputStream, mas esses não parecem ser bastante o que eu preciso, porque o DataHandler precisaria ser devolvido após o PipedOutputStream está escrito.

Todas as idéias?

Foi útil?

Solução

Eu descobri a resposta, ao longo das linhas que Christian estava falando (criando um novo segmento para executar writeToOut ()):

@MTOM
@WebService
class Example {
    @WebMethod
    public @XmlMimeType("application/octet-stream") DataHandler service() {
        // Create piped output stream, wrap it in a final array so that the
        // OutputStream doesn't need to be finalized before sending to new Thread.
        PipedOutputStream out = new PipedOutputStream();
        InputStream in = new PipedInputStream(out);
        final Object[] args = { out };

        // Create a new thread which writes to out.
        new Thread(
            new Runnable(){
                public void run() {
                    writeToOut(args);
                    ((OutputStream)args[0]).close();
                }
            }
        ).start();

        // Return the InputStream to the client.
        DataSource ds = new ByteArrayDataSource(in, "application/octet-stream");
        DataHandler dh = new DataHandler(ds);
        return dh;
    }
}

É um pouco mais complexo devido a variáveis ??final, mas tanto quanto eu posso dizer isso é correto. Quando o segmento é iniciado, ele bloqueia quando se tenta primeiro out.write() chamada; ao mesmo tempo, o fluxo de entrada é devolvido ao cliente, que desbloqueia a escrita através da leitura dos dados. (O problema com as minhas implementações anteriores desta solução foi que eu não estava fechando corretamente o fluxo e, assim, correr em erros.)

Outras dicas

Desculpe, eu só fiz isso para C # e não java, mas eu acho que o seu método deve lançar uma linha para executar "writeToOut (sai);" em parralel. Você precisa criar um fluxo especial e passá-lo para o novo segmento que dá esse fluxo para writeToOut. Depois de iniciar o fio você retornar esse fluxo-objeto com o seu interlocutor.

Se você só tem um método que grava em um fluxo e retorna depois e um outro método que consome um fluxo e retorna depois, não há outro caminho.

De coure a parte difícil é se apossar de tal -multithreading fluxo safe:. Deve bloquear cada lado, se um buffer interno está muito cheia

Não sei se um Java-pipe-stream funciona para isso.

padrão

Wrapper? : -).

implementação personalizada javax.activation.DataSource (apenas 4 métodos) para ser capaz de fazer isso?

return new DataHandler(new DataSource() { 
  // implement getOutputStream to return the stream used inside writeToOut() 
  ... 
});   

Eu não tenho o IDE disponível para testar isso, então eu só estou fazendo uma sugestão. Eu também preciso do layout geral writeToOut :-).

Na minha aplicação eu uso implementação InputStreamDataSource que tomar InputStream como argumento construtor em vez de arquivo no FileDataSource. Ele funciona até agora.

public class InputStreamDataSource implements DataSource {

ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private final String name;

public InputStreamDataSource(InputStream inputStream, String name) {
    this.name = name;
    try {
        int nRead;
        byte[] data = new byte[16384];
        while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, nRead);
        }

        buffer.flush();
        inputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

@Override
public String getContentType() {
    return new MimetypesFileTypeMap().getContentType(name);
}

@Override
public InputStream getInputStream() throws IOException {
    return new ByteArrayInputStream(buffer.toByteArray());
}

@Override
public String getName() {
    return name;
}

@Override
public OutputStream getOutputStream() throws IOException {
    throw new IOException("Read-only data");
}

}

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top