1 /*
2 * Copyright 2012 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package org.jboss.netty.bootstrap;
17
18 import org.jboss.netty.channel.Channel;
19 import org.jboss.netty.channel.ChannelFactory;
20 import org.jboss.netty.channel.ChannelHandler;
21 import org.jboss.netty.channel.ChannelPipeline;
22 import org.jboss.netty.channel.ChannelPipelineFactory;
23 import org.jboss.netty.util.ExternalResourceReleasable;
24
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.LinkedHashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.TreeMap;
32
33 import static org.jboss.netty.channel.Channels.*;
34
35 /**
36 * A helper class which initializes a {@link Channel}. This class provides
37 * the common data structure for its subclasses which actually initialize
38 * {@link Channel}s and their child {@link Channel}s using the common data
39 * structure. Please refer to {@link ClientBootstrap}, {@link ServerBootstrap},
40 * and {@link ConnectionlessBootstrap} for client side, server-side, and
41 * connectionless (e.g. UDP) channel initialization respectively.
42 *
43 * @apiviz.uses org.jboss.netty.channel.ChannelFactory
44 */
45 public class Bootstrap implements ExternalResourceReleasable {
46
47 private volatile ChannelFactory factory;
48 private volatile ChannelPipeline pipeline = pipeline();
49 private volatile ChannelPipelineFactory pipelineFactory = pipelineFactory(pipeline);
50 private volatile Map<String, Object> options = new HashMap<String, Object>();
51
52 /**
53 * Creates a new instance with no {@link ChannelFactory} set.
54 * {@link #setFactory(ChannelFactory)} must be called at once before any
55 * I/O operation is requested.
56 */
57 protected Bootstrap() {
58 }
59
60 /**
61 * Creates a new instance with the specified initial {@link ChannelFactory}.
62 */
63 protected Bootstrap(ChannelFactory channelFactory) {
64 setFactory(channelFactory);
65 }
66
67 /**
68 * Returns the {@link ChannelFactory} that will be used to perform an
69 * I/O operation.
70 *
71 * @throws IllegalStateException
72 * if the factory is not set for this bootstrap yet.
73 * The factory can be set in the constructor or
74 * {@link #setFactory(ChannelFactory)}.
75 */
76 public ChannelFactory getFactory() {
77 ChannelFactory factory = this.factory;
78 if (factory == null) {
79 throw new IllegalStateException(
80 "factory is not set yet.");
81 }
82 return factory;
83 }
84
85 /**
86 * Sets the {@link ChannelFactory} that will be used to perform an I/O
87 * operation. This method can be called only once and can't be called at
88 * all if the factory was specified in the constructor.
89 *
90 * @throws IllegalStateException
91 * if the factory is already set
92 */
93 public void setFactory(ChannelFactory factory) {
94 if (factory == null) {
95 throw new NullPointerException("factory");
96 }
97 if (this.factory != null) {
98 throw new IllegalStateException(
99 "factory can't change once set.");
100 }
101 this.factory = factory;
102 }
103
104 /**
105 * Returns the default {@link ChannelPipeline} which is cloned when a new
106 * {@link Channel} is created. {@link Bootstrap} creates a new pipeline
107 * which has the same entries with the returned pipeline for a new
108 * {@link Channel}.
109 * <p>
110 * Please note that this method is a convenience method that works only
111 * when <b>1)</b> you create only one channel from this bootstrap (e.g.
112 * one-time client-side or connectionless channel) or <b>2)</b> all handlers
113 * in the pipeline is stateless. You have to use
114 * {@link #setPipelineFactory(ChannelPipelineFactory)} if <b>1)</b> your
115 * pipeline contains a stateful {@link ChannelHandler} and <b>2)</b> one or
116 * more channels are going to be created by this bootstrap (e.g. server-side
117 * channels).
118 *
119 * @return the default {@link ChannelPipeline}
120 *
121 * @throws IllegalStateException
122 * if {@link #setPipelineFactory(ChannelPipelineFactory)} was
123 * called by a user last time.
124 */
125 public ChannelPipeline getPipeline() {
126 ChannelPipeline pipeline = this.pipeline;
127 if (pipeline == null) {
128 throw new IllegalStateException(
129 "getPipeline() cannot be called " +
130 "if setPipelineFactory() was called.");
131 }
132 return pipeline;
133 }
134
135 /**
136 * Sets the default {@link ChannelPipeline} which is cloned when a new
137 * {@link Channel} is created. {@link Bootstrap} creates a new pipeline
138 * which has the same entries with the specified pipeline for a new channel.
139 * <p>
140 * Calling this method also sets the {@code pipelineFactory} property to an
141 * internal {@link ChannelPipelineFactory} implementation which returns
142 * a shallow copy of the specified pipeline.
143 * <p>
144 * Please note that this method is a convenience method that works only
145 * when <b>1)</b> you create only one channel from this bootstrap (e.g.
146 * one-time client-side or connectionless channel) or <b>2)</b> all handlers
147 * in the pipeline is stateless. You have to use
148 * {@link #setPipelineFactory(ChannelPipelineFactory)} if <b>1)</b> your
149 * pipeline contains a stateful {@link ChannelHandler} and <b>2)</b> one or
150 * more channels are going to be created by this bootstrap (e.g. server-side
151 * channels).
152 */
153 public void setPipeline(ChannelPipeline pipeline) {
154 if (pipeline == null) {
155 throw new NullPointerException("pipeline");
156 }
157 this.pipeline = pipeline;
158 pipelineFactory = pipelineFactory(pipeline);
159 }
160
161 /**
162 * Dependency injection friendly convenience method for
163 * {@link #getPipeline()} which returns the default pipeline of this
164 * bootstrap as an ordered map.
165 * <p>
166 * Please note that this method is a convenience method that works only
167 * when <b>1)</b> you create only one channel from this bootstrap (e.g.
168 * one-time client-side or connectionless channel) or <b>2)</b> all handlers
169 * in the pipeline is stateless. You have to use
170 * {@link #setPipelineFactory(ChannelPipelineFactory)} if <b>1)</b> your
171 * pipeline contains a stateful {@link ChannelHandler} and <b>2)</b> one or
172 * more channels are going to be created by this bootstrap (e.g. server-side
173 * channels).
174 *
175 * @throws IllegalStateException
176 * if {@link #setPipelineFactory(ChannelPipelineFactory)} was
177 * called by a user last time.
178 */
179 public Map<String, ChannelHandler> getPipelineAsMap() {
180 ChannelPipeline pipeline = this.pipeline;
181 if (pipeline == null) {
182 throw new IllegalStateException("pipelineFactory in use");
183 }
184 return pipeline.toMap();
185 }
186
187 /**
188 * Dependency injection friendly convenience method for
189 * {@link #setPipeline(ChannelPipeline)} which sets the default pipeline of
190 * this bootstrap from an ordered map.
191 * <p>
192 * Please note that this method is a convenience method that works only
193 * when <b>1)</b> you create only one channel from this bootstrap (e.g.
194 * one-time client-side or connectionless channel) or <b>2)</b> all handlers
195 * in the pipeline is stateless. You have to use
196 * {@link #setPipelineFactory(ChannelPipelineFactory)} if <b>1)</b> your
197 * pipeline contains a stateful {@link ChannelHandler} and <b>2)</b> one or
198 * more channels are going to be created by this bootstrap (e.g. server-side
199 * channels).
200 *
201 * @throws IllegalArgumentException
202 * if the specified map is not an ordered map
203 */
204 public void setPipelineAsMap(Map<String, ChannelHandler> pipelineMap) {
205 if (pipelineMap == null) {
206 throw new NullPointerException("pipelineMap");
207 }
208
209 if (!isOrderedMap(pipelineMap)) {
210 throw new IllegalArgumentException(
211 "pipelineMap is not an ordered map. " +
212 "Please use " +
213 LinkedHashMap.class.getName() + '.');
214 }
215
216 ChannelPipeline pipeline = pipeline();
217 for (Map.Entry<String, ChannelHandler> e: pipelineMap.entrySet()) {
218 pipeline.addLast(e.getKey(), e.getValue());
219 }
220
221 setPipeline(pipeline);
222 }
223
224 /**
225 * Returns the {@link ChannelPipelineFactory} which creates a new
226 * {@link ChannelPipeline} for each new {@link Channel}.
227 *
228 * @see #getPipeline()
229 */
230 public ChannelPipelineFactory getPipelineFactory() {
231 return pipelineFactory;
232 }
233
234 /**
235 * Sets the {@link ChannelPipelineFactory} which creates a new
236 * {@link ChannelPipeline} for each new {@link Channel}. Calling this
237 * method invalidates the current {@code pipeline} property of this
238 * bootstrap. Subsequent {@link #getPipeline()} and {@link #getPipelineAsMap()}
239 * calls will raise {@link IllegalStateException}.
240 *
241 * @see #setPipeline(ChannelPipeline)
242 * @see #setPipelineAsMap(Map)
243 */
244 public void setPipelineFactory(ChannelPipelineFactory pipelineFactory) {
245 if (pipelineFactory == null) {
246 throw new NullPointerException("pipelineFactory");
247 }
248 pipeline = null;
249 this.pipelineFactory = pipelineFactory;
250 }
251
252 /**
253 * Returns the options which configures a new {@link Channel} and its
254 * child {@link Channel}s. The names of the child {@link Channel} options
255 * are prepended with {@code "child."} (e.g. {@code "child.keepAlive"}).
256 */
257 public Map<String, Object> getOptions() {
258 return new TreeMap<String, Object>(options);
259 }
260
261 /**
262 * Sets the options which configures a new {@link Channel} and its child
263 * {@link Channel}s. To set the options of a child {@link Channel}, prepend
264 * {@code "child."} to the option name (e.g. {@code "child.keepAlive"}).
265 */
266 public void setOptions(Map<String, Object> options) {
267 if (options == null) {
268 throw new NullPointerException("options");
269 }
270 this.options = new HashMap<String, Object>(options);
271 }
272
273 /**
274 * Returns the value of the option with the specified key. To retrieve
275 * the option value of a child {@link Channel}, prepend {@code "child."}
276 * to the option name (e.g. {@code "child.keepAlive"}).
277 *
278 * @param key the option name
279 *
280 * @return the option value if the option is found.
281 * {@code null} otherwise.
282 */
283 public Object getOption(String key) {
284 if (key == null) {
285 throw new NullPointerException("key");
286 }
287 return options.get(key);
288 }
289
290 /**
291 * Sets an option with the specified key and value. If there's already
292 * an option with the same key, it is replaced with the new value. If the
293 * specified value is {@code null}, an existing option with the specified
294 * key is removed. To set the option value of a child {@link Channel},
295 * prepend {@code "child."} to the option name (e.g. {@code "child.keepAlive"}).
296 *
297 * @param key the option name
298 * @param value the option value
299 */
300 public void setOption(String key, Object value) {
301 if (key == null) {
302 throw new NullPointerException("key");
303 }
304 if (value == null) {
305 options.remove(key);
306 } else {
307 options.put(key, value);
308 }
309 }
310
311 /**
312 * This method simply delegates the call to
313 * {@link ChannelFactory#releaseExternalResources()}.
314 */
315 public void releaseExternalResources() {
316 ChannelFactory factory = this.factory;
317 if (factory != null) {
318 factory.releaseExternalResources();
319 }
320 }
321
322 /**
323 * Returns {@code true} if and only if the specified {@code map} is an
324 * ordered map, like {@link LinkedHashMap} is.
325 */
326 @SuppressWarnings({ "unchecked", "rawtypes" })
327 static boolean isOrderedMap(Map<?, ?> map) {
328 Class<?> mapType = map.getClass();
329 if (LinkedHashMap.class.isAssignableFrom(mapType)) {
330 // LinkedHashMap is an ordered map.
331 return true;
332 }
333
334 // Not a LinkedHashMap - start autodetection.
335
336 // Detect Apache Commons Collections OrderedMap implementations.
337 Class<?> type = mapType;
338 while (type != null) {
339 for (Class<?> i: type.getInterfaces()) {
340 if (i.getName().endsWith("OrderedMap")) {
341 // Seems like it's an ordered map - guessed from that
342 // it implements OrderedMap interface.
343 return true;
344 }
345 }
346 type = type.getSuperclass();
347 }
348
349 // Does not implement OrderedMap interface. As a last resort, try to
350 // create a new instance and test if the insertion order is maintained.
351 Map newMap;
352 try {
353 newMap = (Map) mapType.newInstance();
354 } catch (Exception e) {
355 // No default constructor - cannot proceed anymore.
356 return false;
357 }
358
359 // Run some tests.
360 List<String> expectedKeys = new ArrayList<String>();
361 String dummyValue = "dummyValue";
362 for (short element: ORDER_TEST_SAMPLES) {
363 String key = String.valueOf(element);
364 newMap.put(key, dummyValue);
365 expectedKeys.add(key);
366
367 Iterator<String> it = expectedKeys.iterator();
368 for (Object actualKey: newMap.keySet()) {
369 if (!it.next().equals(actualKey)) {
370 // Did not pass the test.
371 return false;
372 }
373 }
374 }
375
376 // The specified map passed the insertion order test.
377 return true;
378 }
379
380 private static final short[] ORDER_TEST_SAMPLES = {
381 682, 807, 637, 358, 570, 828, 407, 319,
382 105, 41, 563, 544, 518, 298, 418, 50,
383 156, 769, 984, 503, 191, 578, 309, 710,
384 327, 720, 591, 939, 374, 707, 43, 463,
385 227, 174, 30, 531, 135, 930, 190, 823,
386 925, 835, 328, 239, 415, 500, 144, 460,
387 83, 774, 921, 4, 95, 468, 687, 493,
388 991, 436, 245, 742, 149, 821, 142, 782,
389 297, 918, 917, 424, 978, 992, 79, 906,
390 535, 515, 850, 80, 125, 378, 307, 883,
391 836, 160, 27, 630, 668, 226, 560, 698,
392 467, 829, 476, 163, 977, 367, 325, 184,
393 204, 312, 486, 53, 179, 592, 252, 750,
394 893, 517, 937, 124, 148, 719, 973, 566,
395 405, 449, 452, 777, 349, 761, 167, 783,
396 220, 802, 117, 604, 216, 363, 120, 621,
397 219, 182, 817, 244, 438, 465, 934, 888,
398 628, 209, 631, 17, 870, 679, 826, 945,
399 680, 848, 974, 573, 626, 865, 109, 317,
400 91, 494, 965, 473, 725, 388, 302, 936,
401 660, 150, 122, 949, 295, 392, 63, 634,
402 772, 143, 990, 895, 538, 59, 541, 32,
403 669, 321, 811, 756, 82, 955, 953, 636,
404 390, 162, 688, 444, 70, 590, 183, 745,
405 543, 666, 951, 642, 747, 765, 98, 469,
406 884, 929, 178, 721, 994, 840, 353, 726,
407 940, 759, 624, 919, 667, 629, 272, 979,
408 326, 608, 453, 11, 322, 347, 647, 354,
409 381, 746, 472, 890, 249, 536, 733, 404,
410 170, 959, 34, 899, 195, 651, 140, 856,
411 201, 237, 51, 933, 268, 849, 294, 115,
412 157, 14, 854, 373, 186, 872, 71, 523,
413 931, 952, 655, 561, 607, 862, 554, 661,
414 313, 909, 511, 752, 986, 311, 287, 775,
415 505, 878, 422, 103, 299, 119, 107, 344,
416 487, 776, 445, 218, 549, 697, 454, 6,
417 462, 455, 52, 481, 594, 126, 112, 66,
418 877, 172, 153, 912, 834, 741, 610, 915,
419 964, 831, 575, 714, 250, 461, 814, 913,
420 369, 542, 882, 851, 427, 838, 867, 507,
421 434, 569, 20, 950, 792, 605, 798, 962,
422 923, 258, 972, 762, 809, 843, 674, 448,
423 280, 495, 285, 822, 283, 147, 451, 993,
424 794, 982, 748, 189, 274, 96, 73, 810,
425 401, 261, 277, 346, 527, 645, 601, 868,
426 248, 879, 371, 428, 559, 278, 265, 62,
427 225, 853, 483, 771, 9, 8, 339, 653,
428 263, 28, 477, 995, 208, 880, 292, 480,
429 516, 457, 286, 897, 21, 852, 971, 658,
430 623, 528, 316, 471, 860, 306, 638, 711,
431 875, 671, 108, 158, 646, 24, 257, 724,
432 193, 341, 902, 599, 565, 334, 506, 684,
433 960, 780, 429, 801, 910, 308, 383, 901,
434 489, 81, 512, 164, 755, 514, 723, 141,
435 296, 958, 686, 15, 799, 579, 598, 558,
436 414, 64, 420, 730, 256, 131, 45, 129,
437 259, 338, 999, 175, 740, 790, 324, 985,
438 896, 482, 841, 606, 377, 111, 372, 699,
439 988, 233, 243, 203, 781, 969, 903, 662,
440 632, 301, 44, 981, 36, 412, 946, 816,
441 284, 447, 214, 672, 758, 954, 804, 2,
442 928, 886, 421, 596, 574, 16, 892, 68,
443 546, 522, 490, 873, 656, 696, 864, 130,
444 40, 393, 926, 394, 932, 876, 664, 293,
445 154, 916, 55, 196, 842, 498, 177, 948,
446 540, 127, 271, 113, 844, 576, 132, 943,
447 12, 123, 291, 31, 212, 529, 547, 171,
448 582, 609, 793, 830, 221, 440, 568, 118,
449 406, 194, 827, 360, 622, 389, 800, 571,
450 213, 262, 403, 408, 881, 289, 635, 967,
451 432, 376, 649, 832, 857, 717, 145, 510,
452 159, 980, 683, 580, 484, 379, 246, 88,
453 567, 320, 643, 7, 924, 397, 10, 787,
454 845, 779, 670, 716, 19, 600, 382, 0,
455 210, 665, 228, 97, 266, 90, 304, 456,
456 180, 152, 425, 310, 768, 223, 702, 997,
457 577, 663, 290, 537, 416, 426, 914, 691,
458 23, 281, 497, 508, 48, 681, 581, 728,
459 99, 795, 530, 871, 957, 889, 206, 813,
460 839, 709, 805, 253, 151, 613, 65, 654,
461 93, 639, 784, 891, 352, 67, 430, 754,
462 76, 187, 443, 676, 362, 961, 874, 330,
463 331, 384, 85, 217, 855, 818, 738, 361,
464 314, 3, 615, 520, 355, 920, 689, 22,
465 188, 49, 904, 935, 136, 475, 693, 749,
466 519, 812, 100, 207, 963, 364, 464, 572,
467 731, 230, 833, 385, 499, 545, 273, 232,
468 398, 478, 975, 564, 399, 504, 35, 562,
469 938, 211, 26, 337, 54, 614, 586, 433,
470 450, 763, 238, 305, 941, 370, 885, 837,
471 234, 110, 137, 395, 368, 695, 342, 907,
472 396, 474, 176, 737, 796, 446, 37, 894,
473 727, 648, 431, 1, 366, 525, 553, 704,
474 329, 627, 479, 33, 492, 260, 241, 86,
475 185, 491, 966, 247, 13, 587, 602, 409,
476 335, 650, 235, 611, 470, 442, 597, 254,
477 343, 539, 146, 585, 593, 641, 770, 94,
478 976, 705, 181, 255, 315, 718, 526, 987,
479 692, 983, 595, 898, 282, 133, 439, 633,
480 534, 861, 269, 619, 677, 502, 375, 224,
481 806, 869, 417, 584, 612, 803, 58, 84,
482 788, 797, 38, 700, 751, 603, 652, 57,
483 240, 947, 350, 270, 333, 116, 736, 69,
484 74, 104, 767, 318, 735, 859, 357, 555,
485 411, 267, 712, 675, 532, 825, 496, 927,
486 942, 102, 46, 192, 114, 744, 138, 998,
487 72, 617, 134, 846, 166, 77, 900, 5,
488 303, 387, 400, 47, 729, 922, 222, 197,
489 351, 509, 524, 165, 485, 300, 944, 380,
490 625, 778, 685, 29, 589, 766, 161, 391,
491 423, 42, 734, 552, 215, 824, 908, 229,
492 89, 251, 199, 616, 78, 644, 242, 722,
493 25, 437, 732, 956, 275, 200, 970, 753,
494 791, 336, 556, 847, 703, 236, 715, 75,
495 863, 713, 785, 911, 786, 620, 551, 413,
496 39, 739, 820, 808, 764, 701, 819, 173,
497 989, 345, 690, 459, 60, 106, 887, 996,
498 365, 673, 968, 513, 18, 419, 550, 588,
499 435, 264, 789, 340, 659, 466, 356, 288,
500 56, 708, 557, 488, 760, 332, 402, 168,
501 202, 521, 757, 205, 706, 441, 773, 231,
502 583, 386, 678, 618, 815, 279, 87, 533,
503 61, 548, 92, 169, 694, 905, 198, 121,
504 410, 139, 657, 640, 743, 128, 458, 866,
505 501, 348, 155, 276, 101, 858, 323, 359,
506 };
507 }