001: // $Id: AUTOCONF.java,v 1.15.10.1 2007/04/27 08:03:51 belaban Exp $
002:
003: package org.jgroups.protocols;
004:
005: import org.jgroups.Event;
006: import org.jgroups.stack.Protocol;
007:
008: import java.io.IOException;
009: import java.net.DatagramPacket;
010: import java.net.DatagramSocket;
011: import java.net.InetAddress;
012: import java.util.HashMap;
013: import java.util.Properties;
014:
015: /**
016: * Senses the network configuration when it is initialized (in init()) and sends a CONFIG event up
017: * and down the stack. The CONFIG event contains a hashmap, with strings as keys (e.g. "frag_size")
018: * and Objects as values. Certain protocols can set some of their properties when receiving the CONFIG
019: * event.
020: * <p>
021: * This protocol should be placed above the transport protocol (e.g. UDP). It is not needed for TCP.
022: * <p>
023: * Example: senses the network send and receive buffers, plus the max size of a message to be sent and
024: * generates a CONFIG event containing "frag_size", "send_buf_size" and "receive_buf_size" keys.
025: *
026: * @author Bela Ban
027: */
028: public class AUTOCONF extends Protocol {
029: final HashMap config = new HashMap();
030: static int num_iterations = 10; // to find optimal frag_size
031:
032: /** Number of bytes to subtract from computed fragmentation size, due to (a) headers and
033: * (b) serialization overhead */
034: static int frag_overhead = 1000;
035:
036: public String getName() {
037: return "AUTOCONF";
038: }
039:
040: public void init() throws Exception {
041: senseNetworkConfiguration();
042: if (log.isDebugEnabled())
043: log.debug("configuration is\n" + config);
044: }
045:
046: public void start() throws Exception {
047: if (config != null && config.size() > 0) {
048: Event config_evt = new Event(Event.CONFIG, config);
049: passDown(config_evt);
050: passUp(config_evt);
051: }
052: }
053:
054: /**
055: * Setup the Protocol instance acording to the configuration string
056: */
057: public boolean setProperties(Properties props) {
058: String str;
059:
060: super .setProperties(props);
061: str = props.getProperty("num_iterations");
062: if (str != null) {
063: num_iterations = Integer.parseInt(str);
064: props.remove("num_iterations");
065: }
066:
067: str = props.getProperty("frag_overhead");
068: if (str != null) {
069: frag_overhead = Integer.parseInt(str);
070: props.remove("frag_overhead");
071: }
072:
073: if (props.size() > 0) {
074: log
075: .error("AUTOCONF.setProperties(): the following properties are not recognized: "
076: + props);
077: return false;
078: }
079: return true;
080: }
081:
082: /**
083: * Leave empty: no up_thread will be created, but the up_thread of the neighbor below us will be used
084: */
085: public void startUpHandler() {
086: }
087:
088: /**
089: * Leave empty: no down_thread will be created, but the down_thread of the neighbor above us will be used
090: */
091: public void startDownHandler() {
092: }
093:
094: /* -------------------------------------- Private metods ------------------------------------------- */
095: void senseNetworkConfiguration() {
096: int max_frag_size = senseMaxFragSize();
097: if (max_frag_size <= 0) {
098: if (log.isErrorEnabled())
099: log.error("max_frag_size is invalid: " + max_frag_size);
100: } else
101: config.put("frag_size", new Integer(max_frag_size));
102: senseMaxSendBufferSize(config);
103: senseMaxReceiveBufferSize(config);
104: }
105:
106: public static int senseMaxFragSizeStatic() {
107: return new AUTOCONF().senseMaxFragSize();
108: }
109:
110: /**
111: * Tries to find out the max number of bytes in a DatagramPacket we can send by sending increasingly
112: * larger packets, until there is an exception (e.g., java.io.IOException: message too long).
113: */
114: public int senseMaxFragSize() {
115: int max_send = 32000;
116: int upper;
117: int lower = 0;
118: int highest_failed = -1;
119: DatagramSocket sock;
120: byte[] buf;
121: DatagramPacket packet;
122: InetAddress local_addr;
123:
124: try {
125: sock = new DatagramSocket();
126: local_addr = InetAddress.getLocalHost();
127: } catch (Exception ex) {
128: if (log.isWarnEnabled())
129: log.warn("failed creating DatagramSocket: " + ex);
130: return 0;
131: }
132:
133: try {
134: upper = max_send;
135: for (int i = 0; i < num_iterations && lower < upper; i++) { // iterations to approximate frag_size
136: try {
137: buf = new byte[upper];
138: // System.out.println("** upper=" + upper + " (lower=" + lower + ")");
139: packet = new DatagramPacket(buf, buf.length,
140: local_addr, 9);
141: sock.send(packet);
142: lower = Math.max(lower, upper);
143: upper = upper * 2;
144: if (highest_failed > -1)
145: upper = Math.min(highest_failed, upper);
146: } catch (IOException io_ex) {
147: if (highest_failed > -1)
148: highest_failed = Math
149: .min(highest_failed, upper); // never exceed max_upper
150: else
151: highest_failed = upper;
152: upper = (upper + lower) / 2;
153: } catch (Throwable ex) {
154: if (log.isWarnEnabled())
155: log.warn("exception=" + ex);
156: break;
157: }
158: }
159:
160: /** Reduce the frag_size a bit to prevent packets that are too large (see bug #854887) */
161: lower -= frag_overhead;
162: if (log.isDebugEnabled())
163: log.debug("frag_size=" + lower);
164: return lower;
165: } finally {
166: if (sock != null)
167: sock.close();
168: }
169: }
170:
171: void senseMaxSendBufferSize(HashMap map) {
172: DatagramSocket sock;
173: int max_size = 4096, retval = max_size;
174:
175: if (map != null && map.containsKey("frag_size)"))
176: max_size = ((Integer) map.get("frag_size")).intValue();
177:
178: try {
179: sock = new DatagramSocket();
180: while (max_size < 1000000) {
181: sock.setSendBufferSize(max_size);
182: if ((retval = sock.getSendBufferSize()) < max_size)
183: return;
184: max_size *= 2;
185: }
186: } catch (Throwable ex) {
187: if (log.isErrorEnabled())
188: log.error("failed getting the max send buffer size: "
189: + ex + ". Defaulting to " + retval);
190: } finally {
191: map.put("send_buf_size", new Integer(retval));
192: }
193: }
194:
195: void senseMaxReceiveBufferSize(HashMap map) {
196: DatagramSocket sock;
197: int max_size = 4096, retval = max_size;
198:
199: try {
200: sock = new DatagramSocket();
201: while (max_size < 1000000) {
202: sock.setReceiveBufferSize(max_size);
203: if ((retval = sock.getReceiveBufferSize()) < max_size)
204: return;
205: max_size *= 2;
206: }
207: } catch (Throwable ex) {
208: if (log.isErrorEnabled())
209: log.error("failed getting the max send buffer size: "
210: + ex + ". Defaulting to " + retval);
211: } finally {
212: map.put("recv_buf_size", new Integer(retval));
213: }
214: }
215:
216: /* ----------------------------------- End of Private metods --------------------------------------- */
217:
218: public static void main(String[] args) {
219: int frag_size = new AUTOCONF().senseMaxFragSize();
220: System.out.println("frag_size: " + frag_size);
221: }
222:
223: }
|