Pregunta

Tengo un servicio web Java en JAX-WS que devuelve un OutputStream de otro método. Me parece que no puede encontrar la manera de transmitir los OutputStream en el DataHandler devuelto ninguna otra manera que para crear un archivo temporal, escribir en él, a continuación, abra de nuevo otra vez como un InputStream. He aquí un ejemplo:

@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;
    }
}

El principal problema es que el método writeToOut () puede devolver los datos que son mucho más grandes que la memoria del ordenador. Es por eso que el método está utilizando masa máxima de despegue en el primer lugar - para transmitir los datos. Parece que no puedo envolver mi cabeza en torno a cómo se debe transmitir los datos directamente desde el OutputStream que tengo que proporcionar a la DataHandler vuelto (y en última instancia el cliente, que recibe el StreamingDataHandler).

He intentado jugar con PipedInputStream y PipedOutputStream, pero los que no parecen ser bastante lo que necesito, porque tendría que ser devuelto después de la PipedOutputStream se escribe en el DataHandler.

¿Alguna idea?

¿Fue útil?

Solución

Me di cuenta de la respuesta, a lo largo de las líneas que Christian estaba hablando (la creación de un nuevo hilo para ejecutar 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;
    }
}

Es un poco más complejo debido a final las variables, pero en lo que puedo decir que esto es correcto. Cuando el hilo se pone en marcha, bloquea cuando primero intenta llamar out.write(); al mismo tiempo, el flujo de entrada se devuelve al cliente, que desbloquea la escritura mediante la lectura de los datos. (El problema con mis implementaciones anteriores de esta solución era que no estaba cerrando adecuadamente la corriente, y lo que se corre en errores.)

Otros consejos

En este momento, lo único que hice esto para C # y Java no, pero creo que el método debe lanzar un hilo para funcionar "writeToOut (hacia fuera);" en parralel. Es necesario crear una corriente especial y pasarlo al nuevo hilo que da a esa corriente writeToOut. Después de iniciar el hilo que regrese esa corriente a objetos con su interlocutor.

Si sólo tiene un método que escribe a un arroyo y vuelve después y otro método que consume una corriente y devuelve después, no hay otra manera.

Por Coure la parte difícil es conseguir el asimiento de una corriente Safe tales -multithreading:. Se procederá al bloqueo de cada lado, si un buffer interno está demasiado lleno

No sé si una aplicación Java-pipe-stream funciona para eso.

patrón de la envoltura? : -).

javax.activation.DataSource aplicación personalizada (sólo 4 métodos) para ser capaz de hacer esto?

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

No tengo el IDE disponibles para probar esto, así que sólo estoy haciendo una sugerencia. También me necesita el writeToOut :-) disposición general.

En mi solicitud utilizo aplicación InputStreamDataSource que toma como argumento del constructor InputStream en lugar de Archivo en FileDataSource. Funciona hasta ahora.

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top