package org.jacorb.orb.miop; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.DatagramPacket; import java.net.MulticastSocket; import java.net.SocketException; import org.jacorb.config.Configurable; import org.jacorb.config.Configuration; import org.jacorb.config.ConfigurationException; import org.jacorb.orb.CDROutputStream; import org.omg.ETF.Profile; import org.omg.MIOP.PacketHeader_1_0; import org.omg.MIOP.PacketHeader_1_0Helper; /** * A client side one way miop connection. Send data via multicast sockets. * * @author Alysson Neves Bessani * @author Nick Cross * @version 1.0 * @see MIOPConnection MIOPFactories */ public class ClientMIOPConnection extends MIOPConnection implements Configurable { private static short nextMessage = 0; private MulticastSocket socket; private DatagramPacket packet; /** connection's configuration parameters */ private short packetDataMaxSize; public ClientMIOPConnection () { super (); } /** * Connect with the specified profile. * * @param profile a group MIOP profile. * @param timeout unused. */ @Override public void connect (Profile profile, long timeout) { if (is_connected ()) { close (); } this.profile = (MIOPProfile)profile; try { socket = new MulticastSocket (); } catch (IOException ioe) { throw new RuntimeException ("Error while creating and setting multicast socket " + profile); } } /** * Test if this connection is connected. * * @return true if connected and false otherwise. */ @Override public boolean is_connected () { return (socket != null) && socket.isConnected (); } /** * Close this connection (if it is opened). */ @Override public void close () { if (socket != null) { socket.close (); } } @Override public void flush () { super.flush(); byte data[] = ((ByteArrayOutputStream)out_stream).toByteArray (); ((ByteArrayOutputStream)out_stream).reset(); int length = data.length; int offset = 0; // handle the size of the last packet that is rest of the division between // the message's size and the maximum packet's size short lastPacketSize = (short)(length % packetDataMaxSize); // number of packages filled: the message size divided by the maximum size of a package int numberOfFullPackets = length / packetDataMaxSize; // number of packages: number of packages filled + (1 or 0) int numberOfPackets = numberOfFullPackets + ((lastPacketSize > 0) ? 1 : 0); // creates an id byte[] messageId = generateNewId (); // Here is the case (very rare) when size of data to be sent fit to // a collection of full packets without debris. But in any case we need // a final packet. if (lastPacketSize == 0) { numberOfFullPackets--; lastPacketSize = packetDataMaxSize; } PacketHeader_1_0 header; // Send all the packets except the last // int i = 0; // is out because it will be used later for (; i < numberOfFullPackets; i++) { header = new PacketHeader_1_0 (MulticastUtil.MAGIC, MulticastUtil.HDR_VERSION, MulticastUtil.BIG_ENDIAN, packetDataMaxSize, i, numberOfPackets, messageId); sendMIOPPacket (header, data, offset + i * packetDataMaxSize, packetDataMaxSize); } // now the last ... header = new PacketHeader_1_0 (MulticastUtil.MAGIC, MulticastUtil.HDR_VERSION, (byte)(MulticastUtil.BIG_ENDIAN | MulticastUtil.STOP_FLAG), lastPacketSize, i, numberOfPackets, messageId); sendMIOPPacket (header, data, offset + i * packetDataMaxSize, lastPacketSize); } /** * Send a MIOP packet given the header and the bytes to send. * * @param header the MIOP packet header. * @param data the packet data array. * @param offset the data array offset. * @param length the amount of data to send. */ private void sendMIOPPacket (PacketHeader_1_0 header, byte[] data, int offset, int length) { CDROutputStream os = new CDROutputStream (orb); PacketHeader_1_0Helper.write (os, header); os.check (length, MulticastUtil.BOUNDARY); os.write_octet_array (data, offset, length); try { byte[] buffer = os.getBufferCopy (); if (packet == null) { packet = new DatagramPacket ( buffer, 0, buffer.length, ((MIOPProfile)profile).getGroupInetAddress (), ((MIOPProfile)profile).getUIPMCProfile ().the_port ); } else { packet.setData (buffer, 0, buffer.length); } socket.send (packet); } catch (IOException se) { if (logger.isDebugEnabled()) { logger.debug("Transport to " + connection_info + ": stream closed " + se.getMessage() ); } throw to_COMM_FAILURE (se); } } /** * Generates an Id for a message. * * @return the 12 byte Id */ private byte[] generateNewId () { byte[] id = new byte[MulticastUtil.ID_SIZE]; byte serverId[] = orb.getServerId (); // copy the key to the current host System.arraycopy (serverId, 0, id, 0, serverId.length); // number of message synchronized (ClientMIOPConnection.class) { id[10] = (byte)((nextMessage >> 8) & 0xFF); id[11] = (byte)(nextMessage & 0xFF); nextMessage++; } return id; } /** * @see org.jacorb.orb.etf.ConnectionBase#getTimeout() */ @Override protected int getTimeout () { try { return socket.getSoTimeout(); } catch (SocketException se) { throw to_COMM_FAILURE (se); } } /** * @see org.jacorb.orb.etf.ConnectionBase#setTimeout(int) */ @Override protected void setTimeout (int timeout) { if (socket != null) { try { if (logger.isInfoEnabled()) { logger.info ("Socket timeout set to " + timeout + " ms"); } socket.setSoTimeout(timeout); } catch( SocketException se ) { if (logger.isInfoEnabled()) { logger.info("SocketException", se); } } } } @Override public void configure (Configuration config) throws ConfigurationException { super.configure (config); int packetMax = config.getAttributeAsInteger ("jacorb.miop.packet_max_size", MulticastUtil.PACKET_MAX_SIZE); packetDataMaxSize = (short)(packetMax - MulticastUtil.PACKET_DATA_MAX_SIZE); } }