Skip to content

Commit be236cc

Browse files
author
Marcus Linke
committed
Refact streaming
1 parent decd67c commit be236cc

File tree

13 files changed

+137
-56
lines changed

13 files changed

+137
-56
lines changed

src/main/java/com/github/dockerjava/api/async/ResultCallback.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
/**
66
* Result callback
77
*/
8-
public interface ResultCallback<RES_T> {
8+
public interface ResultCallback<RES_T> extends Closeable {
99
/** Called when the async processing starts. The passed {@link Closeable} can be used to close/interrupt the processing */
1010
void onStart(Closeable closeable);
1111
/** Called when an async result event occurs */
12-
void onResult(RES_T object);
12+
void onNext(RES_T object);
1313
/** Called when an exception occurs while processing */
1414
void onError(Throwable throwable);
1515
/** Called when processing was finished either by reaching the end or by aborting it */
16-
void onFinish();
16+
void onComplete();
1717

1818
}

src/main/java/com/github/dockerjava/api/model/StreamType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
public enum StreamType {
44
STDIN,
55
STDOUT,
6-
STDERR
6+
STDERR,
7+
RAW
78
}

src/main/java/com/github/dockerjava/core/async/FrameStreamProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public void processResponseStream(InputStream response, ResultCallback<Frame> re
2828
Frame frame = frameReader.readFrame();
2929
while (frame != null) {
3030
try {
31-
resultCallback.onResult(frame);
31+
resultCallback.onNext(frame);
3232
} catch (Exception e) {
3333
resultCallback.onError(e);
3434
} finally {
@@ -44,7 +44,7 @@ public void processResponseStream(InputStream response, ResultCallback<Frame> re
4444
} catch (IOException e) {
4545
resultCallback.onError(e);
4646
} finally {
47-
resultCallback.onFinish();
47+
resultCallback.onComplete();
4848
}
4949
}
5050
}

src/main/java/com/github/dockerjava/core/async/JsonStreamProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public void processResponseStream(InputStream response, ResultCallback<T> result
3838
JsonParser jp = JSON_FACTORY.createParser(response);
3939
while (!jp.isClosed() && jp.nextToken() != JsonToken.END_OBJECT) {
4040
try {
41-
resultCallback.onResult(OBJECT_MAPPER.readValue(jp, clazz));
41+
resultCallback.onNext(OBJECT_MAPPER.readValue(jp, clazz));
4242
} catch (Exception e) {
4343
resultCallback.onError(e);
4444
}
@@ -51,7 +51,7 @@ public void processResponseStream(InputStream response, ResultCallback<T> result
5151
} catch (IOException e) {
5252
resultCallback.onError(e);
5353
} finally {
54-
resultCallback.onFinish();
54+
resultCallback.onComplete();
5555
}
5656
}
5757

src/main/java/com/github/dockerjava/core/async/ResultCallbackTemplate.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* @author marcus
1717
*
1818
*/
19-
public class ResultCallbackTemplate<T> implements ResultCallback<T>, Closeable {
19+
public class ResultCallbackTemplate<T> implements ResultCallback<T> {
2020

2121
private final CountDownLatch finished = new CountDownLatch(1);
2222

@@ -28,7 +28,7 @@ public void onStart(Closeable stream) {
2828
}
2929

3030
@Override
31-
public void onResult(T object) {
31+
public void onNext(T object) {
3232
}
3333

3434
@Override
@@ -45,7 +45,7 @@ public void onError(Throwable throwable) {
4545
}
4646

4747
@Override
48-
public void onFinish() {
48+
public void onComplete() {
4949
try {
5050
close();
5151
} catch (IOException e) {
Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,88 @@
11
package com.github.dockerjava.core.command;
22

3-
import com.github.dockerjava.api.model.Frame;
4-
import com.github.dockerjava.api.model.StreamType;
5-
63
import java.io.IOException;
74
import java.io.InputStream;
5+
import java.util.Arrays;
6+
7+
import org.apache.commons.io.HexDump;
8+
9+
import com.github.dockerjava.api.model.Frame;
10+
import com.github.dockerjava.api.model.StreamType;
811

912
/**
1013
* Breaks the input into frame. Similar to how a buffered reader would readLies.
1114
* <p/>
12-
* See: {@link }http://docs.docker.com/v1.6/reference/api/docker_remote_api_v1.13/#attach-to-a-container}
15+
* See: {@link }http://docs.docker.com/v1.6/reference/api/docker_remote_api_v1.13/#attach-to-a-container}
1316
*/
1417
public class FrameReader implements AutoCloseable {
1518

19+
private static final int BUFFER_SIZE = 100;
20+
1621
private static final int HEADER_SIZE = 8;
22+
1723
private final InputStream inputStream;
1824

25+
private boolean rawDetected = false;
26+
1927
public FrameReader(InputStream inputStream) {
2028
this.inputStream = inputStream;
2129
}
2230

2331
private static StreamType streamType(byte streamType) {
2432
switch (streamType) {
25-
case 0:
26-
return StreamType.STDIN;
27-
case 1:
28-
return StreamType.STDOUT;
29-
case 2:
30-
return StreamType.STDERR;
31-
default:
32-
throw new IllegalArgumentException("invalid streamType");
33+
case 0:
34+
return StreamType.STDIN;
35+
case 1:
36+
return StreamType.STDOUT;
37+
case 2:
38+
return StreamType.STDERR;
39+
default:
40+
throw new IllegalArgumentException("invalid streamType");
3341
}
3442
}
3543

3644
/**
3745
* @return A frame, or null if no more frames.
3846
*/
3947
public Frame readFrame() throws IOException {
40-
byte[] header = new byte[HEADER_SIZE];
4148

42-
int actualHeaderSize = 0;
49+
byte[] buffer = new byte[BUFFER_SIZE];
4350

44-
do {
45-
int headerCount = inputStream.read(header, actualHeaderSize, HEADER_SIZE - actualHeaderSize);
51+
int readBytes = inputStream.read(buffer);
4652

47-
if (headerCount == -1) {
48-
return null;
49-
}
50-
actualHeaderSize += headerCount;
51-
} while (actualHeaderSize < HEADER_SIZE);
53+
if (readBytes == -1) {
54+
return null;
55+
}
56+
57+
if (rawDetected || readBytes != HEADER_SIZE) {
58+
rawDetected = true;
5259

53-
int payloadSize = ((header[4] & 0xff) << 24) + ((header[5] & 0xff) << 16) + ((header[6] & 0xff) << 8) + (header[7] & 0xff);
60+
byte[] read = Arrays.copyOfRange(buffer, 0, readBytes);
5461

55-
byte[] payload = new byte[payloadSize];
56-
int actualPayloadSize = 0;
62+
return new Frame(StreamType.RAW, read);
63+
} else {
5764

58-
do {
59-
int count = inputStream.read(payload, actualPayloadSize, payloadSize - actualPayloadSize);
65+
int payloadSize = ((buffer[4] & 0xff) << 24) + ((buffer[5] & 0xff) << 16) + ((buffer[6] & 0xff) << 8)
66+
+ (buffer[7] & 0xff);
6067

61-
if (count == -1) {
62-
if (actualPayloadSize != payloadSize) {
63-
throw new IOException(String.format("payload must be %d bytes long, but was %d", payloadSize, actualPayloadSize));
64-
}
65-
break;
68+
byte[] payload = new byte[payloadSize];
69+
int actualPayloadSize = inputStream.read(payload);
70+
if (actualPayloadSize != payloadSize) {
71+
throw new IOException(String.format("payload must be %d bytes long, but was %d", payloadSize,
72+
actualPayloadSize));
6673
}
67-
actualPayloadSize += count;
68-
} while (actualPayloadSize < payloadSize);
6974

70-
return new Frame(streamType(header[0]), payload);
75+
return new Frame(streamType(buffer[0]), payload);
76+
77+
}
78+
7179
}
7280

7381
@Override
7482
public void close() throws IOException {
7583
inputStream.close();
7684
}
85+
86+
87+
7788
}

src/test/java/com/github/dockerjava/client/AbstractDockerClientTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,9 @@ public void onError(Throwable throwable) {
200200
}
201201

202202
@Override
203-
public void onResult(Frame frame) {
203+
public void onNext(Frame frame) {
204204
frames.add(frame);
205-
log.append(new String(frame.getPayload()).trim());
205+
log.append(new String(frame.getPayload()));
206206
}
207207

208208
@Override

src/test/java/com/github/dockerjava/core/command/AttachContainerCmdImplTest.java

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package com.github.dockerjava.core.command;
22

33
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.containsString;
45
import static org.hamcrest.Matchers.endsWith;
56
import static org.hamcrest.Matchers.isEmptyString;
67
import static org.hamcrest.Matchers.not;
8+
import static org.hamcrest.Matchers.containsString;
79

10+
import java.io.File;
11+
import java.io.InputStream;
812
import java.lang.reflect.Method;
913
import java.util.concurrent.TimeUnit;
1014

15+
import org.apache.commons.io.HexDump;
16+
import org.apache.commons.lang.StringUtils;
1117
import org.testng.ITestResult;
1218
import org.testng.annotations.AfterMethod;
1319
import org.testng.annotations.AfterTest;
@@ -17,6 +23,8 @@
1723

1824
import com.github.dockerjava.api.DockerException;
1925
import com.github.dockerjava.api.command.CreateContainerResponse;
26+
import com.github.dockerjava.api.model.Frame;
27+
import com.github.dockerjava.api.model.StreamType;
2028
import com.github.dockerjava.client.AbstractDockerClientTest;
2129

2230
@Test(groups = "integration")
@@ -43,28 +51,79 @@ public void afterMethod(ITestResult result) {
4351
}
4452

4553
@Test
46-
public void attachContainer() throws Exception {
54+
public void attachContainerWithoutTTY() throws Exception {
4755

4856
String snippet = "hello world";
4957

50-
CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("top").exec();
58+
CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("echo", snippet)
59+
.withTty(false).exec();
5160

5261
LOG.info("Created container: {}", container.toString());
5362
assertThat(container.getId(), not(isEmptyString()));
5463

5564
dockerClient.startContainerCmd(container.getId()).exec();
5665

57-
CollectFramesCallback collectFramesCallback = new CollectFramesCallback();
66+
CollectFramesCallback collectFramesCallback = new CollectFramesCallback() {
67+
@Override
68+
public void onNext(Frame frame) {
69+
assertEquals(frame.getStreamType(), StreamType.RAW);
70+
super.onNext(frame);
71+
};
72+
};
5873

5974
dockerClient.attachContainerCmd(container.getId(), collectFramesCallback).withStdErr().withStdOut()
6075
.withFollowStream().withLogs().exec();
6176

77+
collectFramesCallback.awaitFinish(30, TimeUnit.SECONDS);
78+
79+
collectFramesCallback.close();
80+
81+
assertThat(collectFramesCallback.toString(), endsWith(snippet));
82+
}
83+
84+
85+
@Test
86+
public void attachContainerWithTTY() throws Exception {
87+
88+
File baseDir = new File(Thread.currentThread().getContextClassLoader()
89+
.getResource("attachContainerTestDockerfile").getFile());
90+
91+
InputStream response = dockerClient.buildImageCmd(baseDir).withNoCache().exec();
92+
93+
String fullLog = asString(response);
94+
assertThat(fullLog, containsString("Successfully built"));
95+
96+
String imageId = StringUtils.substringBetween(fullLog,
97+
"Successfully built ", "\\n\"}").trim();
98+
99+
CreateContainerResponse container = dockerClient.createContainerCmd(imageId)
100+
.withTty(true).exec();
101+
102+
LOG.info("Created container: {}", container.toString());
103+
assertThat(container.getId(), not(isEmptyString()));
104+
105+
dockerClient.startContainerCmd(container.getId()).exec();
106+
107+
CollectFramesCallback collectFramesCallback = new CollectFramesCallback() {
108+
@Override
109+
public void onNext(Frame frame) {
110+
assertEquals(frame.getStreamType(), StreamType.RAW);
111+
super.onNext(frame);
112+
};
113+
};
114+
115+
dockerClient.attachContainerCmd(container.getId(), collectFramesCallback).withStdErr().withStdOut()
116+
.withFollowStream().exec();
117+
62118
collectFramesCallback.awaitFinish(10, TimeUnit.SECONDS);
63119

64120
collectFramesCallback.close();
65121

66-
// LOG.info("resonse: " + log);
122+
System.out.println("log: " + collectFramesCallback.toString());
67123

68-
assertThat(collectFramesCallback.toString(), endsWith(snippet));
124+
125+
HexDump.dump(collectFramesCallback.toString().getBytes(), 0, System.out, 0);
126+
127+
assertThat(collectFramesCallback.toString(), containsString("stdout\r\nstderr"));
69128
}
70129
}

src/test/java/com/github/dockerjava/core/command/EventsCmdImplTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public EventCallbackTest(CountDownLatch countDownLatch) {
141141
this.countDownLatch = countDownLatch;
142142
}
143143

144-
public void onResult(Event event) {
144+
public void onNext(Event event) {
145145
LOG.info("Received event #{}: {}", countDownLatch.getCount(), event);
146146
countDownLatch.countDown();
147147
events.add(event);

src/test/java/com/github/dockerjava/core/command/LogContainerCmdImplTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ public void onError(Throwable throwable) {
9292
super.onError(throwable);
9393
}
9494

95-
public void onFinish() {
96-
super.onFinish();
95+
public void onComplete() {
96+
super.onComplete();
9797
fail("expected NotFoundException");
9898
};
9999
};

0 commit comments

Comments
 (0)