5776-3.diff
new/src/com/goldencode/p2j/net/Protocol.java 2022-06-09 16:22:23 +0000 | ||
---|---|---|
2 | 2 |
** Module : Protocol.java |
3 | 3 |
** Abstract : low level driver of the P2J protocol |
4 | 4 |
** |
5 |
** Copyright (c) 2005-2021, Golden Code Development Corporation.
|
|
5 |
** Copyright (c) 2005-2022, Golden Code Development Corporation.
|
|
6 | 6 |
** |
7 | 7 |
** -#- -I- --Date-- --JPRM-- ----------------Description----------------- |
8 | 8 |
** 001 SIY 20050120 @19501 Created initial version. |
... | ... | |
76 | 76 |
** 026 IAS 20200930 Added JMX counters. |
77 | 77 |
** 027 IAS 20201001 Ged rid of byte arrays allocations in objToByteArray(). |
78 | 78 |
** IAS 20210325 Added SSLException catch |
79 |
** TJD 20220607 Writer/ByteArrayOutputStream memory leak fix for LOBs |
|
79 | 80 |
*/ |
80 | 81 |
/* |
81 | 82 |
** This program is free software: you can redistribute it and/or modify |
... | ... | |
464 | 465 |
// form) |
465 | 466 |
Message msg = byteArrayToObj(data); |
466 | 467 |
|
468 |
// we can already release data memory here |
|
469 |
data = null; |
|
470 |
|
|
467 | 471 |
// hand the message to the dispatcher for processing |
468 | 472 |
queue.enqueueInbound(msg); |
469 | 473 |
} |
... | ... | |
597 | 601 |
private class Writer |
598 | 602 |
implements Runnable |
599 | 603 |
{ |
600 |
private final ByteArrayOutputStream2 baos = new ByteArrayOutputStream2(8192); |
|
601 |
private final ByteArrayOutputStream res = new ByteArrayOutputStream(8192); |
|
604 |
/** |
|
605 |
* Default size of buffer for object flattening |
|
606 |
*/ |
|
607 |
private static final int BUFFER_SIZE = 8192; |
|
608 | ||
609 |
/** |
|
610 |
* Pre-allocated helper buffers for object flattening |
|
611 |
*/ |
|
612 |
private ByteArrayOutputStream2 baos = new ByteArrayOutputStream2(BUFFER_SIZE); |
|
613 |
private ByteArrayOutputStream res = new ByteArrayOutputStream(BUFFER_SIZE); |
|
614 | ||
615 |
/** |
|
616 |
* Holder for data to be written out |
|
617 |
*/ |
|
602 | 618 |
private byte[] data; |
603 | 619 |
|
604 | 620 |
/** |
... | ... | |
640 | 656 |
OUTGOING.update(data.length); |
641 | 657 |
} |
642 | 658 |
queue.write(data); |
659 | ||
660 |
// allow GC to collect memory allocated for data |
|
661 |
data = null; |
|
643 | 662 |
} |
644 | 663 |
} |
645 | 664 |
|
... | ... | |
723 | 742 |
res.write(obj.getClass() == Message.class ? TYPE_MESSAGE : TYPE_SYNC_MESSAGE); |
724 | 743 |
res.write(baos.buf(), 0, baos.size()); |
725 | 744 |
|
726 |
return res.toByteArray(); |
|
745 |
// in case baos grows too big -> reallocate it |
|
746 |
if (baos.size() > BUFFER_SIZE) |
|
747 |
{ |
|
748 |
baos = new ByteArrayOutputStream2(BUFFER_SIZE); |
|
749 |
} |
|
750 |
|
|
751 |
byte[] result = res.toByteArray(); |
|
752 |
|
|
753 |
// in case res grows too big -> reallocate it |
|
754 |
if (res.size() > BUFFER_SIZE) |
|
755 |
{ |
|
756 |
res = new ByteArrayOutputStream(BUFFER_SIZE); |
|
757 |
} |
|
758 |
|
|
759 |
return result; |
|
727 | 760 |
} |
728 | 761 |
} |
729 | 762 |
} |
new/src/com/goldencode/p2j/util/OutputStreamWrapper.java 2022-06-09 17:13:08 +0000 | ||
---|---|---|
2 | 2 |
** Module : OutputStreamWrapper.java |
3 | 3 |
** Abstract : provides an external OutputStream wrapper for a P2J Stream |
4 | 4 |
** |
5 |
** Copyright (c) 2006-2017, Golden Code Development Corporation.
|
|
5 |
** Copyright (c) 2006-2022, Golden Code Development Corporation.
|
|
6 | 6 |
** |
7 | 7 |
** -#- -I- --Date-- --JPRM-- ----------------------------Description----------------------------- |
8 | 8 |
** 001 GES 20060215 @24675 First version which provides an external |
... | ... | |
10 | 10 |
** 002 ECF 20171029 Made write(byte[], int, int) more efficient; implemented close to |
11 | 11 |
** close underlying stream. |
12 | 12 |
** 003 ME 20210322 Proxy flush to the underlying stream. |
13 |
** TJD 20220608 Chunked write implementation |
|
13 | 14 |
*/ |
14 | 15 |
/* |
15 | 16 |
** This program is free software: you can redistribute it and/or modify |
... | ... | |
80 | 81 |
/** The wrapped stream. */ |
81 | 82 |
private Stream out = null; |
82 | 83 |
|
84 |
/** |
|
85 |
* The chunk to write at once to the remote {@link #stream}, via in the {@link #write(byte[], int, int)} |
|
86 |
* implementation. |
|
87 |
*/ |
|
88 |
private static final int WRITE_CHUNK_SIZE = 1024 * 1024; // 1MB |
|
89 |
|
|
83 | 90 |
/** |
84 | 91 |
* Creates an instance with the associated stream. |
85 | 92 |
*/ |
... | ... | |
103 | 110 |
public void write(int b) |
104 | 111 |
throws IOException |
105 | 112 |
{ |
113 |
if (out == null) |
|
114 |
{ |
|
115 |
throw new IOException("Stream is not open."); |
|
116 |
} |
|
106 | 117 |
out.writeByte((byte) (b & 0xFF)); |
107 | 118 |
} |
108 | 119 |
|
... | ... | |
124 | 135 |
throws IOException, |
125 | 136 |
NullPointerException |
126 | 137 |
{ |
138 |
if (out == null) |
|
139 |
{ |
|
140 |
throw new IOException("Stream is not open."); |
|
141 |
} |
|
127 | 142 |
write(b, 0, b.length); |
128 | 143 |
} |
129 | 144 |
|
... | ... | |
154 | 169 |
NullPointerException, |
155 | 170 |
IndexOutOfBoundsException |
156 | 171 |
{ |
157 |
out.write(b, off, len); |
|
172 |
if (out == null) |
|
173 |
{ |
|
174 |
throw new IOException("Stream is not open."); |
|
175 |
} |
|
176 |
// Bounds check |
|
177 |
if ((off + len) > b.length || off < 0 || len < 0) |
|
178 |
{ |
|
179 |
throw new IndexOutOfBoundsException(); |
|
180 |
} |
|
181 |
int remaining = len - off; |
|
182 |
if (remaining == 0) |
|
183 |
{ |
|
184 |
return; |
|
185 |
} |
|
186 |
byte[] buffer = new byte[Math.min(remaining, WRITE_CHUNK_SIZE)]; |
|
187 |
|
|
188 |
while (remaining > 0) |
|
189 |
{ |
|
190 |
System.arraycopy(b, off, buffer, 0, Math.min(remaining, WRITE_CHUNK_SIZE)); |
|
191 |
// write at most WRITE_CHUNK_SIZE of data and at most the remaining bytes |
|
192 |
out.write(buffer, 0, Math.min(remaining, WRITE_CHUNK_SIZE)); |
|
193 |
remaining -= buffer.length; |
|
194 |
off += buffer.length; |
|
195 |
} |
|
158 | 196 |
} |
159 | 197 |
|
160 | 198 |
/** |
... | ... | |
170 | 208 |
} |
171 | 209 |
} |
172 | 210 | |
211 |
/** |
|
212 |
* Flushes the underlying stream. |
|
213 |
*/ |
|
173 | 214 |
@Override |
174 | 215 |
public void flush() throws IOException |
175 | 216 |
{ |
new/src/com/goldencode/p2j/util/TargetLobFile.java 2022-06-09 16:22:23 +0000 | ||
---|---|---|
10 | 10 |
** 003 OM 20210328 Improved COPY-LOB engine and validations. |
11 | 11 |
** OM 20210404 Used a getter to expose the file name. |
12 | 12 |
** EVL 20220406 Catching more exceptions to inform the target file is missing or invalid. |
13 |
** TJD 20220607 Switch to OutputStreamWriter to allow chunked write |
|
13 | 14 |
*/ |
14 | 15 | |
15 | 16 |
/* |
... | ... | |
136 | 137 |
@Override |
137 | 138 |
public void write(byte[] data) |
138 | 139 |
{ |
139 |
Stream stream = null; |
|
140 |
try |
|
141 |
{ |
|
142 |
stream = StreamFactory.openFileStream(filename, true, append); |
|
143 |
} |
|
144 |
catch (DeferredLegacyErrorException dlee) |
|
145 |
{ |
|
146 |
ErrorManager.recordOrThrowError(43, filename, "3"); |
|
147 |
// ** Cannot find or open file <file-name>, errno = <number>. (43) |
|
148 |
return; |
|
149 |
} |
|
150 |
catch (ErrorConditionException ece) |
|
151 |
{ |
|
152 |
ErrorManager.recordOrThrowError(43, filename, "3"); |
|
153 |
// ** Cannot find or open file <file-name>, errno = <number>. (43) |
|
154 |
return; |
|
155 |
} |
|
156 |
|
|
157 | 140 |
if (data == null) |
158 | 141 |
{ |
159 | 142 |
// if the source is unknown (so the [data] is null) the file will still be created but empty |
160 | 143 |
data = new byte[0]; |
161 | 144 |
} |
162 |
|
|
163 |
try |
|
164 |
{ |
|
165 |
stream.write(data); |
|
145 |
Stream stream = openStream(); |
|
146 |
if (stream == null) |
|
147 |
{ |
|
148 |
return; |
|
149 |
} |
|
150 |
// underlying stream will be closed by wrapper at the end of write |
|
151 |
try (OutputStreamWrapper os = new OutputStreamWrapper(stream)) |
|
152 |
{ |
|
153 |
os.write(data); |
|
166 | 154 |
} |
167 | 155 |
catch (IOException exc) |
168 | 156 |
{ |
169 | 157 |
ErrorManager.recordOrThrowError(43, filename, "13"); |
170 | 158 |
// ** Cannot find or open file <file-name>, errno = <number>. (43) |
171 | 159 |
} |
172 |
finally |
|
173 |
{ |
|
174 |
stream.close(); |
|
175 |
} |
|
160 | ||
176 | 161 |
} |
177 | 162 |
|
178 | 163 |
/** |
... | ... | |
208 | 193 |
} |
209 | 194 |
} |
210 | 195 |
|
211 |
Stream stream = StreamFactory.openFileStream(filename, true, append); |
|
212 |
|
|
213 |
try |
|
196 |
Stream stream = openStream(); |
|
197 |
if (stream == null) |
|
198 |
{ |
|
199 |
return; |
|
200 |
} |
|
201 |
// underlying stream will be closed by wrapper at the end of write |
|
202 |
try (OutputStreamWrapper os = new OutputStreamWrapper(stream)) |
|
214 | 203 |
{ |
215 | 204 |
stream.write(data.getBytes(javaCS)); |
216 | 205 |
} |
... | ... | |
219 | 208 |
ErrorManager.recordOrThrowError(43, filename, "13"); |
220 | 209 |
// ** Cannot find or open file <file-name>, errno = <number>. (43) |
221 | 210 |
} |
222 |
finally |
|
223 |
{ |
|
224 |
stream.close(); |
|
225 |
} |
|
226 | 211 |
} |
227 | 212 |
|
228 | 213 |
/** |
... | ... | |
255 | 240 |
|
256 | 241 |
return filename.toStringMessage(); |
257 | 242 |
} |
243 |
|
|
244 |
/** |
|
245 |
* Open stream using StreamFactory for writing, optionally in append mode |
|
246 |
* @return Stream object instance or null in case of failure |
|
247 |
*/ |
|
248 |
private Stream openStream() |
|
249 |
{ |
|
250 |
/** File stream for output file, which is a remote resource */ |
|
251 |
Stream stream = null; |
|
252 |
|
|
253 |
try |
|
254 |
{ |
|
255 |
stream = StreamFactory.openFileStream(filename, true, append); |
|
256 |
} |
|
257 |
catch (DeferredLegacyErrorException dlee) |
|
258 |
{ |
|
259 |
ErrorManager.recordOrThrowError(43, filename, "2"); |
|
260 |
// ** Cannot find or open file <file-name>, errno = <number>. (43) |
|
261 |
} |
|
262 |
catch (ErrorConditionException ece) |
|
263 |
{ |
|
264 |
ErrorManager.recordOrThrowError(43, filename, "3"); |
|
265 |
// ** Cannot find or open file <file-name>, errno = <number>. (43) |
|
266 |
} |
|
267 |
return stream; |
|
268 |
} |
|
258 | 269 |
} |