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