java - jax - ¿Cómo se puede canalizar un OutputStream a un StreamingDataHandler?
jax ws ri (4)
Tengo un servicio web Java en JAX-WS que devuelve un OutputStream desde otro método. Parece que no puedo entender cómo transmitir el OutputStream al DataHandler devuelto de otra manera que no sea crear un archivo temporal, escribir en él y luego abrirlo de nuevo como un InputStream. Aquí hay 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 problema principal es que el método writeToOut () puede devolver datos que son mucho más grandes que la memoria de la computadora. Es por eso que el método está utilizando MTOM en primer lugar: para transmitir los datos. Parece que no puedo entender cómo transmitir los datos directamente desde el OutputStream que debo proporcionar al DataHandler devuelto (y en última instancia al cliente, que recibe el StreamingDataHandler).
He intentado jugar con PipedInputStream y PipedOutputStream, pero parece que no son todo lo que necesito, porque el DataHandler debería devolverse después de que se haya escrito en PipedOutputStream.
¿Algunas ideas?
Lo siento, solo hice esto para C # y no para Java, pero creo que su método debería iniciar un hilo para ejecutar "writeToOut (out);" en parralel. Necesita crear una secuencia especial y pasarla al nuevo subproceso que le da esa secuencia a writeToOut. Después de iniciar el hilo, devuelve ese objeto de flujo a su interlocutor.
Si solo tiene un método que escribe en una secuencia y luego regresa y otro método que consume una secuencia y luego regresa, no hay otra manera.
Por supuesto, la parte más difícil es agarrar una corriente segura de este tipo: bloqueará cada lado si un búfer interno está demasiado lleno.
No sé si un Java-pipe-stream funciona para eso.
Patrón de envoltura? :-).
Implementación personalizada de javax.activation.DataSource (solo 4 métodos) para poder hacer esto?
return new DataHandler(new DataSource() {
// implement getOutputStream to return the stream used inside writeToOut()
...
});
No tengo el IDE disponible para probar esto, así que solo estoy haciendo una sugerencia. También necesitaría el diseño general writeToOut :-).
Descubrí la respuesta, en la misma línea que Christian (creando 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 las variables final
, pero hasta donde sé, esto es correcto. Cuando se inicia el subproceso, se bloquea cuando intenta llamar a out.write()
; al mismo tiempo, la secuencia de entrada se devuelve al cliente, quien desbloquea la escritura leyendo los datos. (El problema con mis implementaciones anteriores de esta solución era que no estaba cerrando correctamente la secuencia y, por lo tanto, se producían errores).
En mi aplicación utilizo la implementación InputStreamDataSource que toma InputStream como argumento de constructor en lugar de File 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");
}
}