1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
|
23 | |
|
24 | |
|
25 | |
|
26 | |
package com.aragost.javahg.internals; |
27 | |
|
28 | |
import java.io.ByteArrayOutputStream; |
29 | |
import java.io.IOException; |
30 | |
import java.io.InputStream; |
31 | |
|
32 | |
import com.aragost.javahg.commands.CancelledExecutionException; |
33 | |
import com.aragost.javahg.internals.AbstractCommand.State; |
34 | |
import com.aragost.javahg.log.Logger; |
35 | |
import com.aragost.javahg.log.LoggerFactory; |
36 | |
|
37 | |
|
38 | |
|
39 | |
|
40 | |
|
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
public class OutputChannelInputStream extends InputStream { |
52 | |
|
53 | 1 | private static final Logger LOG = LoggerFactory.getLogger(OutputChannelInputStream.class); |
54 | |
|
55 | |
private final InputStream channelStream; |
56 | |
|
57 | |
private final AbstractCommand cmd; |
58 | |
|
59 | |
private final Server server; |
60 | |
|
61 | |
|
62 | |
|
63 | |
|
64 | |
|
65 | 3808 | private BlockInputStream currentOutputChannelBlock = BlockInputStream.EMPTY; |
66 | |
|
67 | |
|
68 | |
|
69 | |
|
70 | |
|
71 | |
|
72 | |
|
73 | 3808 | OutputChannelInputStream(InputStream channelStream, Server server, AbstractCommand cmd) throws IOException { |
74 | 3808 | this.channelStream = channelStream; |
75 | 3808 | this.cmd = cmd; |
76 | 3808 | this.server = server; |
77 | |
try { |
78 | 3808 | findNextOutputChannelBlock(); |
79 | 18 | } catch (RuntimeException e) { |
80 | 18 | verifyServerProcess(e); |
81 | 17 | throw e; |
82 | 11 | } catch (IOException e) { |
83 | 11 | verifyServerProcess(e); |
84 | 0 | throw e; |
85 | 3779 | } |
86 | 3779 | } |
87 | |
|
88 | |
@Override |
89 | |
public int read() throws IOException { |
90 | 1 | if (this.currentOutputChannelBlock == null) { |
91 | 0 | return -1; |
92 | |
} |
93 | |
try { |
94 | 1 | int ch = this.currentOutputChannelBlock.read(); |
95 | 0 | if (ch == -1) { |
96 | 0 | findNextOutputChannelBlock(); |
97 | 0 | return read(); |
98 | |
} |
99 | 0 | return ch; |
100 | 0 | } catch (RuntimeException e) { |
101 | 0 | verifyServerProcess(e); |
102 | 0 | throw e; |
103 | 1 | } catch (IOException e) { |
104 | 1 | verifyServerProcess(e); |
105 | 1 | throw e; |
106 | |
} |
107 | |
} |
108 | |
|
109 | |
@Override |
110 | |
public int read(byte[] b, int off, int len) throws IOException { |
111 | 16118 | if (this.currentOutputChannelBlock == null) { |
112 | 5634 | return -1; |
113 | |
} |
114 | |
try { |
115 | 10484 | int n = this.currentOutputChannelBlock.read(b, off, len); |
116 | 10482 | if (n > 0) { |
117 | 5916 | return n; |
118 | 4566 | } else if (n == -1) { |
119 | 4566 | findNextOutputChannelBlock(); |
120 | 4563 | return read(b, off, len); |
121 | |
} else { |
122 | 0 | return 0; |
123 | |
} |
124 | 3 | } catch (RuntimeException e) { |
125 | 3 | verifyServerProcess(e); |
126 | 3 | throw e; |
127 | 2 | } catch (IOException e) { |
128 | 2 | verifyServerProcess(e); |
129 | 1 | throw e; |
130 | |
} |
131 | |
} |
132 | |
|
133 | |
@Override |
134 | |
public int available() throws IOException { |
135 | 5592 | if (this.currentOutputChannelBlock == null) { |
136 | 0 | return 0; |
137 | |
} else { |
138 | 5592 | return this.currentOutputChannelBlock.available(); |
139 | |
} |
140 | |
} |
141 | |
|
142 | |
|
143 | |
|
144 | |
|
145 | |
|
146 | |
|
147 | |
|
148 | |
|
149 | |
|
150 | |
private void findNextOutputChannelBlock() throws IOException { |
151 | 8377 | if (this.currentOutputChannelBlock == null) { |
152 | 0 | throw new IllegalStateException(); |
153 | |
} |
154 | 8377 | if (this.currentOutputChannelBlock.getBytesLeft() > 0) { |
155 | 0 | throw new IllegalStateException("Still bytes available in currentOutputChannelBlock"); |
156 | |
} |
157 | 8377 | this.currentOutputChannelBlock = null; |
158 | 8377 | BlockInputStream blockInputStream = null; |
159 | |
while (true) { |
160 | 8769 | if (cmd.getState() == State.CANCELING) { |
161 | 1 | throw new CancelledExecutionException(cmd); |
162 | |
} |
163 | |
|
164 | 8768 | blockInputStream = new BlockInputStream(this.channelStream); |
165 | 8756 | char channel = blockInputStream.getChannel(); |
166 | 8756 | switch (channel) { |
167 | |
case 'o': |
168 | 4570 | this.currentOutputChannelBlock = blockInputStream; |
169 | 4570 | return; |
170 | |
case 'e': |
171 | 391 | this.cmd.addToError(blockInputStream); |
172 | 391 | break; |
173 | |
case 'r': |
174 | 3789 | if (blockInputStream.getLength() != 4) { |
175 | 0 | throw new IllegalStateException("Length 4 expected for channel 'r'"); |
176 | |
} |
177 | 3789 | int returnCode = blockInputStream.readInt(); |
178 | 3789 | LOG.debug("Command '{}' gave return code: {}", this.cmd.getCommandName(), returnCode); |
179 | 3789 | this.cmd.handleReturnCode(returnCode); |
180 | 3772 | return; |
181 | |
case 'L': |
182 | 3 | this.cmd.setLineChannelLength(blockInputStream.getLength()); |
183 | 3 | return; |
184 | |
default: |
185 | 3 | if (Character.isLowerCase(channel)) { |
186 | |
|
187 | 1 | Utils.consumeAll(blockInputStream); |
188 | |
} else { |
189 | |
|
190 | 2 | throw new IllegalStateException("Unknown channel: " + channel); |
191 | |
} |
192 | |
} |
193 | 392 | } |
194 | |
} |
195 | |
|
196 | |
|
197 | |
|
198 | |
|
199 | |
|
200 | |
|
201 | |
|
202 | |
|
203 | |
public void reopen() { |
204 | 3 | this.currentOutputChannelBlock = BlockInputStream.EMPTY; |
205 | |
try { |
206 | 3 | findNextOutputChannelBlock(); |
207 | 0 | } catch (IOException e) { |
208 | 0 | throw new RuntimeIOException(e); |
209 | 3 | } |
210 | 3 | } |
211 | |
|
212 | |
|
213 | |
|
214 | |
|
215 | |
private void verifyServerProcess(Exception e) { |
216 | 35 | this.server.verifyServerProcess(e); |
217 | 22 | } |
218 | |
} |