View Javadoc

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