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 }