1/* 2 * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package com.sun.corba.se.impl.encoding; 26 27import org.omg.CORBA.BAD_PARAM; 28import org.omg.CORBA.INTERNAL; 29import org.omg.CORBA.CompletionStatus; 30import com.sun.corba.se.spi.ior.iiop.GIOPVersion; 31import com.sun.corba.se.impl.encoding.CodeSetConversion; 32import com.sun.corba.se.impl.orbutil.ORBConstants; 33 34public class CDROutputStream_1_2 extends CDROutputStream_1_1 35{ 36 // There's a situation with chunking with fragmentation 37 // in which the alignment for a primitive value is needed 38 // to fill fragment N, but the primitive won't fit so 39 // must go into fragment N + 1. The behavior is the same 40 // as that for specialChunks. 41 // 42 // Unfortunately, given the current code, we can't reuse 43 // specialChunk. If you wrap each of the following 44 // write calls with handleSpecialChunkBegin/End, you 45 // will lose your state because the primitive calls will 46 // change the variables, etc. 47 // 48 // All of the CDR code should be rewritten moving chunking 49 // to a different level, perhaps in the buffer managers. 50 // We want to move to a compositional model rather than 51 // using inheritance. 52 // 53 // Note that in the grow case, chunks are _NOT_ closed 54 // at grow points, now. 55 // 56 // **** NOTE **** 57 // Since we will not support valuetypes with GIOP 1.1, that 58 // also means we do not support chunking there. 59 // 60 protected boolean primitiveAcrossFragmentedChunk = false; 61 62 // Used in chunking. Here's how this works: 63 // 64 // When chunking and writing an array of primitives, a string, or a 65 // wstring, _AND_ it won't fit in the buffer do the following. (As 66 // you can see, this is a very "special" chunk.) 67 // 68 // 1. Write the length of the chunk including the array length 69 // 2. Set specialChunk to true 70 // 3 applies to ALL chunking: 71 // 3. In grow, if we need to fragment and specialChunk is false 72 // a) call end_block 73 // b) fragment 74 // Now back to the array only case: 75 // [write the data] 76 // 4. if specialChunk is true 77 // a) Close the chunk 78 // b) Set specialChunk to false 79 80 protected boolean specialChunk = false; 81 82 // Indicates whether the header should be padded. In GIOP 1.2 and above, the 83 // body must be aligned on a 8-octet boundary, and so the header needs to be 84 // padded appropriately. However, if there is no body to a request or reply 85 // message, there is no need to pad the header, in the unfragmented case. 86 private boolean headerPadding; 87 88 protected void handleSpecialChunkBegin(int requiredSize) 89 { 90 // If we're chunking and the item won't fit in the buffer 91 if (inBlock && requiredSize + bbwi.position() > bbwi.buflen) { 92 93 // Duplicating some code from end_block. Compute 94 // and write the total chunk length. 95 96 int oldSize = bbwi.position(); 97 bbwi.position(blockSizeIndex - 4); 98 99 //write_long(oldSize - blockSizeIndex); 100 writeLongWithoutAlign((oldSize - blockSizeIndex) + requiredSize); 101 bbwi.position(oldSize); 102 103 // Set the special flag so we don't end the chunk when 104 // we fragment 105 specialChunk = true; 106 } 107 } 108 109 protected void handleSpecialChunkEnd() 110 { 111 // If we're in a chunk and the item spanned fragments 112 if (inBlock && specialChunk) { 113 114 // This is unnecessary, but I just want to show that 115 // we're done with the current chunk. (the end_block 116 // call is inappropriate here) 117 inBlock = false; 118 blockSizeIndex = -1; 119 blockSizePosition = -1; 120 121 // Start a new chunk since we fragmented during the item. 122 // Thus, no one can go back to add more to the chunk length 123 start_block(); 124 125 // Now turn off the flag so we go back to the normal 126 // behavior of closing a chunk when we fragment and 127 // reopening afterwards. 128 specialChunk = false; 129 } 130 } 131 132 // Called after writing primitives 133 private void checkPrimitiveAcrossFragmentedChunk() 134 { 135 if (primitiveAcrossFragmentedChunk) { 136 primitiveAcrossFragmentedChunk = false; 137 138 inBlock = false; 139 140 // It would be nice to have a StreamPosition 141 // abstraction if we could avoid allocation 142 // overhead. 143 blockSizeIndex = -1; 144 blockSizePosition = -1; 145 146 // Start a new chunk 147 start_block(); 148 } 149 } 150 151 152 public void write_octet(byte x) { 153 super.write_octet(x); 154 checkPrimitiveAcrossFragmentedChunk(); 155 } 156 157 public void write_short(short x) { 158 super.write_short(x); 159 checkPrimitiveAcrossFragmentedChunk(); 160 } 161 162 public void write_long(int x) { 163 super.write_long(x); 164 checkPrimitiveAcrossFragmentedChunk(); 165 } 166 167 public void write_longlong(long x) { 168 super.write_longlong(x); 169 checkPrimitiveAcrossFragmentedChunk(); 170 } 171 172 // Called by RequestMessage_1_2 or ReplyMessage_1_2 classes only. 173 void setHeaderPadding(boolean headerPadding) { 174 this.headerPadding = headerPadding; 175 } 176 177 protected void alignAndReserve(int align, int n) { 178 179 // headerPadding bit is set by the write operation of RequestMessage_1_2 180 // or ReplyMessage_1_2 classes. When set, the very first body write 181 // operation (from the stub code) would trigger an alignAndReserve 182 // method call, that would in turn add the appropriate header padding, 183 // such that the body is aligned on a 8-octet boundary. The padding 184 // is required for GIOP versions 1.2 and above, only if body is present. 185 if (headerPadding == true) { 186 headerPadding = false; 187 alignOnBoundary(ORBConstants.GIOP_12_MSG_BODY_ALIGNMENT); 188 } 189 190 // In GIOP 1.2, we always end fragments at our 191 // fragment size, which is an "evenly divisible 192 // 8 byte boundary" (aka divisible by 16). A fragment can 193 // end with appropriate alignment padding, but no padding 194 // is needed with respect to the next GIOP fragment 195 // header since it ends on an 8 byte boundary. 196 197 bbwi.position(bbwi.position() + computeAlignment(align)); 198 199 if (bbwi.position() + n > bbwi.buflen) 200 grow(align, n); 201 } 202 203 protected void grow(int align, int n) { 204 205 // Save the current size for possible post-fragmentation calculation 206 int oldSize = bbwi.position(); 207 208 // See notes where specialChunk is defined, as well as the 209 // above notes for primitiveAcrossFragmentedChunk. 210 // 211 // If we're writing a primitive and chunking, we need to update 212 // the chunk length to include the length of the primitive (unless 213 // this complexity is handled by specialChunk). 214 // 215 // Note that this is wasted processing in the grow case, but that 216 // we don't actually close the chunk in that case. 217 boolean handleChunk = (inBlock && !specialChunk); 218 if (handleChunk) { 219 int oldIndex = bbwi.position(); 220 221 bbwi.position(blockSizeIndex - 4); 222 223 writeLongWithoutAlign((oldIndex - blockSizeIndex) + n); 224 225 bbwi.position(oldIndex); 226 } 227 228 bbwi.needed = n; 229 bufferManagerWrite.overflow(bbwi); 230 231 // At this point, if we fragmented, we should have a ByteBufferWithInfo 232 // with the fragment header already marshalled. The buflen and position 233 // should be updated accordingly, and the fragmented flag should be set. 234 235 // Note that fragmented is only true in the streaming and collect cases. 236 if (bbwi.fragmented) { 237 238 // Clear the flag 239 bbwi.fragmented = false; 240 241 // Update fragmentOffset so indirections work properly. 242 // At this point, oldSize is the entire length of the 243 // previous buffer. bbwi.position() is the length of the 244 // fragment header of this buffer. 245 fragmentOffset += (oldSize - bbwi.position()); 246 247 // We just fragmented, and need to signal that we should 248 // start a new chunk after writing the primitive. 249 if (handleChunk) 250 primitiveAcrossFragmentedChunk = true; 251 252 } 253 } 254 255 public GIOPVersion getGIOPVersion() { 256 return GIOPVersion.V1_2; 257 } 258 259 public void write_wchar(char x) 260 { 261 // In GIOP 1.2, a wchar is encoded as an unsigned octet length 262 // followed by the octets of the converted wchar. This is good, 263 // but it causes problems with our chunking code. We don't 264 // want that octet to get put in a different chunk at the end 265 // of the previous fragment. 266 // 267 // Ensure that this won't happen by overriding write_wchar_array 268 // and doing our own handleSpecialChunkBegin/End here. 269 CodeSetConversion.CTBConverter converter = getWCharConverter(); 270 271 converter.convert(x); 272 273 handleSpecialChunkBegin(1 + converter.getNumBytes()); 274 275 write_octet((byte)converter.getNumBytes()); 276 277 byte[] result = converter.getBytes(); 278 279 // Write the bytes without messing with chunking 280 // See CDROutputStream_1_0 281 internalWriteOctetArray(result, 0, converter.getNumBytes()); 282 283 handleSpecialChunkEnd(); 284 } 285 286 public void write_wchar_array(char[] value, int offset, int length) 287 { 288 if (value == null) { 289 throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); 290 } 291 292 CodeSetConversion.CTBConverter converter = getWCharConverter(); 293 294 // Unfortunately, because of chunking, we have to convert the 295 // entire char[] to a byte[] array first so we can know how 296 // many bytes we're writing ahead of time. You can't split 297 // an array of primitives into multiple chunks. 298 int totalNumBytes = 0; 299 300 // Remember that every wchar starts with an octet telling 301 // its length. The buffer size is an upper bound estimate. 302 int maxLength = (int)Math.ceil(converter.getMaxBytesPerChar() * length); 303 byte[] buffer = new byte[maxLength + length]; 304 305 for (int i = 0; i < length; i++) { 306 // Convert one wchar 307 converter.convert(value[offset + i]); 308 309 // Make sure to add the octet length 310 buffer[totalNumBytes++] = (byte)converter.getNumBytes(); 311 312 // Copy it into our buffer 313 System.arraycopy(converter.getBytes(), 0, 314 buffer, totalNumBytes, 315 converter.getNumBytes()); 316 317 totalNumBytes += converter.getNumBytes(); 318 } 319 320 // Now that we know the total length, we can deal with chunking. 321 // Note that we don't have to worry about alignment since they're 322 // just octets. 323 handleSpecialChunkBegin(totalNumBytes); 324 325 // Must use totalNumBytes rather than buffer.length since the 326 // buffer.length is only the upper bound estimate. 327 internalWriteOctetArray(buffer, 0, totalNumBytes); 328 329 handleSpecialChunkEnd(); 330 } 331 332 public void write_wstring(String value) { 333 if (value == null) { 334 throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE); 335 } 336 337 // In GIOP 1.2, wstrings are not terminated by a null. The 338 // length is the number of octets in the converted format. 339 // A zero length string is represented with the 4 byte length 340 // value of 0. 341 if (value.length() == 0) { 342 write_long(0); 343 return; 344 } 345 346 CodeSetConversion.CTBConverter converter = getWCharConverter(); 347 348 converter.convert(value); 349 350 handleSpecialChunkBegin(computeAlignment(4) + 4 + converter.getNumBytes()); 351 352 write_long(converter.getNumBytes()); 353 354 // Write the octet array without tampering with chunking 355 internalWriteOctetArray(converter.getBytes(), 0, converter.getNumBytes()); 356 357 handleSpecialChunkEnd(); 358 } 359} 360