StreamingDatahandler에 출력 스트림을 어떻게 파이프 할 수 있습니까?
문제
JAX-WS에는 다른 방법에서 출력 스트림을 반환하는 Java 웹 서비스가 있습니다. OutputStream을 반환 된 Datahandler로 스트리밍하는 방법을 알아내는 방법을 알 수없는 것 같습니다. 임시 파일을 작성하고 작성한 다음 다시 입력 스트림으로 다시 열어줍니다. 예는 다음과 같습니다.
@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;
}
}
주요 문제는 WriteToout () 메소드가 컴퓨터의 메모리보다 훨씬 큰 데이터를 반환 할 수 있다는 것입니다. 그렇기 때문에 메소드가 먼저 MTOM을 사용하여 데이터를 스트리밍하는 이유입니다. 반환 된 Datahandler (및 궁극적으로 StreamingDatahandler를받는 클라이언트)에게 제공 해야하는 출력 스트림에서 데이터를 직접 스트리밍하는 방법을 고려할 수없는 것 같습니다.
나는 PipedInputStream과 PipedOutputStream과 함께 놀아 보았지만 PipedOutputStream이 작성된 후 Datahandler를 반환해야하기 때문에 필요한 것 같지는 않습니다.
어떤 아이디어?
해결책
나는 Christian이 말한 선을 따라 답을 알아 냈습니다 (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;
}
}
그로 인해 더 복잡합니다 final
변수이지만, 내가 말할 수있는 한, 이것은 정확합니다. 스레드가 시작되면 처음으로 호출하려고 할 때 차단됩니다. out.write()
; 동시에, 입력 스트림은 클라이언트로 되돌아 가서 데이터를 읽음으로써 쓰기를 차단 해제합니다. (이 솔루션의 이전 구현의 문제는 스트림을 제대로 닫지 않고 오류가 발생한다는 것입니다.)
다른 팁
죄송합니다. Java가 아닌 C#에 대해서만이 작업을 수행했지만 "writeToout (out)"를 실행하기위한 스레드를 시작해야한다고 생각합니다. Parralel에서. 특수 스트림을 만들어 새 스레드로 전달하여 해당 스트림을 WriteToout에 제공합니다. 스레드를 시작한 후 해당 스트림 객체를 발신자에게 반환합니다.
스트림에 쓰고 나중에 반환하는 메소드와 스트림을 소비하고 나중에 반환하는 다른 방법 만 있으면 다른 방법이 없습니다.
까다로운 부분은 그러한 무형의 금고를 잡는 것입니다. 내부 버퍼가 너무 가득 차면 각 측면을 차단해야합니다.
Java-Pipe-stream이 효과가 있는지 모르겠습니다.
래퍼 패턴? :-).
custom javax.activation.datasource 구현 (4 가지 방법 만)이 작업을 수행 할 수 있습니까?
return new DataHandler(new DataSource() {
// implement getOutputStream to return the stream used inside writeToOut()
...
});
이것을 테스트 할 수있는 IDE가 없으므로 제안 만 수행합니다. 또한 writeToout 일반 레이아웃이 필요합니다 :-).
내 애플리케이션에서는 FiledAtasource의 파일 대신 생성자 인수로 입력을 취하는 InputStreamDataSource 구현을 사용합니다. 지금까지 작동합니다.
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");
}
}