Project

General

Profile

5776-4.diff

Tomasz Domin, 10/17/2022 03:08 AM

Download (9.01 KB)

View differences:

new/src/com/goldencode/p2j/net/Protocol.java 2022-10-17 06:51:14 +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-10-17 06:53:45 +0000
9 9
** 002 ECF 20171029          Made write(byte[], int, int) more efficient; implemented close to close
10 10
**                           underlying stream.
11 11
** 003 ME  20210322          Proxy flush to the underlying stream.
12
**     TJD 20220608          Chunked write implementation
12 13
**     OM  20221003          Allow the wrapper to be tenmporarily unclosable.
13 14
*/
14 15

  
......
81 82
   /** Flag which allow the wrapped stream to be closed. */
82 83
   private boolean closable = true;
83 84
   
85
   /** 
86
    * The chunk to write at once to the remote {@link #stream}, via in the {@link #write(byte[], int, int)} 
87
    * implementation. 
88
    */
89
   private static final int WRITE_CHUNK_SIZE = 1024 * 1024; // 1MB
90
   
84 91
   /**
85 92
    * Creates an instance with the associated stream. The created wrapper is closable.
86 93
    */
......
117 124
   public void write(int b)
118 125
   throws IOException
119 126
   {
127
      if (out == null)
128
      {
129
         throw new IOException("Stream is not open.");
130
      }
120 131
      out.writeByte((byte) (b & 0xFF));
121 132
   }
122 133
   
......
138 149
   throws IOException,
139 150
          NullPointerException
140 151
   {
152
      if (out == null)
153
      {
154
         throw new IOException("Stream is not open.");
155
      }
141 156
      write(b, 0, b.length);
142 157
   }
143 158
   
......
168 183
          NullPointerException,
169 184
          IndexOutOfBoundsException
170 185
   {
171
      out.write(b, off, len);
186
      if (out == null)
187
      {
188
         throw new IOException("Stream is not open.");
189
      }
190
      // Bounds check
191
      if ((off + len) > b.length || off < 0 || len < 0)
192
      {
193
         throw new IndexOutOfBoundsException();
194
      }
195
      int remaining = len - off;
196
      if (remaining == 0)
197
      {
198
         return;
199
      }
200
      byte[] buffer = new byte[Math.min(remaining, WRITE_CHUNK_SIZE)];
201
      
202
      while (remaining > 0)
203
      {
204
         System.arraycopy(b, off, buffer, 0, Math.min(remaining, WRITE_CHUNK_SIZE));
205
         // write at most WRITE_CHUNK_SIZE of data and at most the remaining bytes
206
         out.write(buffer, 0, Math.min(remaining, WRITE_CHUNK_SIZE));
207
         remaining -= buffer.length;
208
         off += buffer.length;
209
      }
172 210
   }
173 211
   
174 212
   /**
......
186 224
      }
187 225
   }
188 226
   
227
   /**
228
    * Flushes the underlying stream.
229
    */
189 230
   @Override
190 231
   public void flush() throws IOException
191 232
   {
new/src/com/goldencode/p2j/util/TargetLobFile.java 2022-10-17 06:51:14 +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
}