package org.jacorb.orb.giop; /* * JacORB - a free Java ORB * * Copyright (C) 1997-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.jacorb.orb.CDROutputStream; import org.jacorb.orb.ORBConstants; import org.omg.CORBA.MARSHAL; import org.omg.CORBA.ORB; import org.omg.IOP.ServiceContext; import org.omg.IOP.ServiceContextHelper; /** * ServiceContextTransportingOutputStream.java * * * Created: Sat Aug 18 12:12:22 2002 * * @author Nicolas Noffke */ public class ServiceContextTransportingOutputStream extends MessageOutputStream { /** * <code>padding_ctx</code> is used if ServiceContexts are actually added. * This will be the last context, and the context_data is used to fill up * to the next 8 byte boundary */ private static ServiceContext padding_ctx = new ServiceContext (ORBConstants.SERVICE_PADDING_CONTEXT, new byte[0]); /** * <code>header_end</code> represents the end of the GIOP message header. * Only valid if header_padding != 0 */ protected int header_end = -1; /** * <code>header_padding</code> represents the number of bytes used for padding * between header and body */ protected int header_padding = 0; protected List contexts; public ServiceContextTransportingOutputStream(ORB orb) { super(orb); } /** * GIOP 1.2 requires the message body to start on an 8 byte * border, while 1.0/1.1 does not. Additionally, this padding shall * only be performed, if the body is not empty (which we don't * know at this stage. */ protected void markHeaderEnd() { header_end = size(); header_padding = 8 - (size() % 8); //difference to next 8 byte border header_padding = (header_padding == 8)? 0 : header_padding; skip( header_padding ); } private int getHeaderEnd() { return header_end; } public int getBodyBegin() { return header_end + header_padding; } private int getHeaderPadding() { return header_padding; } private boolean hasBody() { return size() > getBodyBegin(); } public void insertMsgSize() { if( header_padding == 0 ) { insertMsgSize( size() - Messages.MSG_HEADER_SIZE ); } else { if( size() > header_end + header_padding) { //has a body, so include padding by not removing it :-) insertMsgSize( size() - Messages.MSG_HEADER_SIZE ); } else { //no body written, so remove padding insertMsgSize( size() - header_padding - Messages.MSG_HEADER_SIZE ); reduceSize( header_padding ); } } } public void write_to( GIOPConnection conn ) throws IOException { CDROutputStream ctx_out = null; if( contexts == null || contexts.size() == 0 ) { //no additional service contexts present, so buffer can be //sent as a whole insertMsgSize(); write( conn, 0, size() ); } else { switch( giop_minor ) { case 0 : { // GIOP 1.0 (== GIOP 1.1, fall through) } case 1 : { //GIOP 1.1 //First of all, we need to know the the length of //the service context array //For GIOP 1.1, we have to add a padding context contexts.add( padding_ctx ); ctx_out = createContextStream(); //difference to next 8 byte border // Need to calculate whether the service context needs any // padding. This is the difference. To calculate this need // to add the new CDRStream and the header size - this // should end on an 8 byte boundary. int difference = (8 - ((Messages.MSG_HEADER_SIZE + ctx_out.size ()) % 8)); difference = (difference == 8)? 0 : difference; if( difference > 0 ) { //the last padding context has a 0 length data //part. Therefore, the last data is a ulong //with value 0 (the length of the array). To //increase the data part, we have to increase //the size and add the actual data. //"unwrite" the last ulong ctx_out.reduceSize( 4 ); //write new length ctx_out.write_ulong( difference ); //add "new" data (by just increasing the size //of the stream and not actually writing //anything). ctx_out.increaseSize( difference ); } //Then, we have to update the message size in the GIOP //message header. The new size is the size of the //"original" message minus the length ulong (4 bytes) of //the original empty ServiceContext array plus the length //of the new service context array insertMsgSize( size() - Messages.MSG_HEADER_SIZE - 4 + ctx_out.size() ); //The ServiceContexts are the first attribute in //the RequestHeader struct. Therefore firstly, we //have to write the GIOP message header... write( conn, 0, Messages.MSG_HEADER_SIZE ); //... then add the contexts ... ctx_out.write( conn, 0, ctx_out.size() ); //... and finally the rest of the message //(omitting the empty original context array). write( conn, Messages.MSG_HEADER_SIZE + 4, size() - (Messages.MSG_HEADER_SIZE + 4) ); break; } case 2 : { //GIOP 1.2 //First of all, we need to know the the length of //the service context array //For GIOP 1.2, the header is padded per spec, so //no additional context is needed ctx_out = createContextStream(); //the new header end is the old header end minus //the length ulong of the context array plus the //length of the context array (wich contains its //own length ulong) int new_header_end = getHeaderEnd() - 4 + ctx_out.size(); //difference to next 8 byte border int difference = 8 - (new_header_end % 8); difference = (difference == 8)? 0 : difference; if( difference > 0 && hasBody() ) { //add padding bytes (by just increasing the //size of the stream and not actually writing //anything). If no body is present, no padding //has to be inserted ctx_out.increaseSize( difference ); } //Then, we have to update the message size in the //GIOP message header. The new size is the size of //the "original" message minus the length ulong (4 //bytes) of the original empty ServiceContext //array minus the "original" header padding plus //the length of the new service context array //(containing the new padding) insertMsgSize( size() - Messages.MSG_HEADER_SIZE - 4 - getHeaderPadding() + ctx_out.size() ); //The GIOP message and request header (up until //the ServiceContexts) stay unmanipulated. We also //have to remove the length ulong of the //"original" empty service context array, because //the new one has its own length attribute write( conn, 0, getHeaderEnd() - 4 ); //... then add the contexts ... ctx_out.write( conn, 0, ctx_out.size()); //... and finally the rest of the message //(omitting the empty original context array). write( conn, getBodyBegin(), size() - getBodyBegin() ); break; } default : { throw new MARSHAL( "Unknown GIOP minor: " + giop_minor ); } } } close(); if ( ctx_out != null ) { ctx_out.close(); ctx_out = null; } } public void addServiceContext( ServiceContext ctx ) { if( contexts == null ) { contexts = new ArrayList(); } contexts.add( ctx ); } private CDROutputStream createContextStream() { final CDROutputStream out = new CDROutputStream( orb ); //write the length of the service context array. out.write_ulong( contexts.size() ); for( int i = 0; i < contexts.size(); i++ ) { ServiceContextHelper.write( out, (ServiceContext) contexts.get( i )); } return out; } }// ServiceContextTransportingOutputStream