    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.configuration;
    18
    19  import java.util.Hashtable;
    20
    21  import javax.naming.Context;
    22  import javax.naming.NameClassPair;
    23  import javax.naming.NameNotFoundException;
    24  import javax.naming.NamingEnumeration;
    25  import javax.naming.NamingException;
    26  import javax.naming.spi.InitialContextFactory;
    27
    28  import com.mockobjects.dynamic.C;
    29  import com.mockobjects.dynamic.Mock;
    30
    31  /***
    32   * A mock implementation of the <code>InitialContextFactory</code> interface.
    33   * This implementation will return a mock context that contains some test data.
    34   *
    35   * @author <a
    36   * href="http://commons.apache.org/configuration/team-list.html">Commons
    37   * Configuration team</a>
    38   * @version $Id: MockInitialContextFactory.java 561230 2007-07-31 04:17:09Z rahul $
    39   */
    40  public class MockInitialContextFactory implements InitialContextFactory
    41  {
    42      /***
    43       * Constant for the use cycles environment property. If this property is
    44       * present in the environment, a cyclic context will be created.
    45       */
    46      public static final String PROP_CYCLES = "useCycles";
    47
    48      /*** Constant for the lookup method. */
    49      private static final String METHOD_LOOKUP = "lookup";
    50
    51      /*** Constant for the list method. */
    52      private static final String METHOD_LIST = "list";
    53
    54      /*** Constant for the close method.*/
    55      private static final String METHOD_CLOSE = "close";
    56
    57      /*** Constant for the name of the missing property. */
    58      private static final String MISSING_PROP = "/missing";
    59
    60      /*** Constant for the name of the prefix. */
    61      private static final String PREFIX = "test/";
    62
    63      /*** An array with the names of the supported properties. */
    64      private static final String[] PROP_NAMES =
    65      { "key", "key2", "short", "boolean", "byte", "double", "float", "integer",
    66              "long", "onlyinjndi" };
    67
    68      /*** An array with the values of the supported properties. */
    69      private static final String[] PROP_VALUES =
    70      { "jndivalue", "jndivalue2", "1", "true", "10", "10.25", "20.25", "10",
    71              "1000000", "true" };
    72
    73      /*** An array with properties that are requested, but are not in the context. */
    74      private static final String[] MISSING_NAMES =
    75      { "missing/list", "test/imaginarykey", "foo/bar" };
    76
    77      /***
    78       * Creates a <code>Context</code> object that is backed by a mock object.
    79       * The mock context can be queried for the values of certain test
    80       * properties. It also supports listing the contained (sub) properties.
    81       *
    82       * @param env the environment
    83       * @return the context mock
    84       */
    85      public Context getInitialContext(Hashtable env) throws NamingException
    86      {
    87          boolean useCycles = env.containsKey(PROP_CYCLES);
    88
    89          Mock mockTopCtx = createCtxMock(PREFIX);
    90          Mock mockCycleCtx = createCtxMock("");
    91          Mock mockPrfxCtx = createCtxMock("");
    92          Mock mockBaseCtx = new Mock(Context.class);
    93          mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq(""), mockTopCtx.proxy());
    94          mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx
    95                  .proxy());
    96          mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx
    97                  .proxy());
    98          mockPrfxCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
    99                  mockPrfxCtx, PROP_NAMES, PROP_VALUES).proxy());
    100
    101         if (useCycles)
    102         {
    103             mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("cycle"),
    104                     mockCycleCtx.proxy());
    105             mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
    106                     mockTopCtx, new String[]
    107                     { "test", "cycle" }, new Object[]
    108                     { mockPrfxCtx.proxy(), mockCycleCtx.proxy() }).proxy());
    109             Mock mockEnum = createEnumMock(mockCycleCtx, PROP_NAMES,
    110                     PROP_VALUES, false);
    111             addEnumPair(mockEnum, "cycleCtx", mockCycleCtx.proxy());
    112             closeEnum(mockEnum);
    113             mockCycleCtx
    114                     .matchAndReturn(METHOD_LIST, C.eq(""), mockEnum.proxy());
    115             mockCycleCtx.matchAndReturn(METHOD_LOOKUP, C.eq("cycleCtx"),
    116                     mockCycleCtx.proxy());
    117         }
    118         else
    119         {
    120             mockTopCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock(
    121                     mockTopCtx, new String[]
    122                     { "test" }, new Object[]
    123                     { mockPrfxCtx.proxy() }).proxy());
    124         }
    125         return (Context) mockBaseCtx.proxy();
    126     }
    127
    128     /***
    129      * Creates a mock for a Context with the specified prefix.
    130      *
    131      * @param prefix the prefix
    132      * @return the mock for the context
    133      */
    134     private Mock createCtxMock(String prefix)
    135     {
    136         Mock mockCtx = new Mock(Context.class);
    137         for (int i = 0; i < PROP_NAMES.length; i++)
    138         {
    139             bind(mockCtx, prefix + PROP_NAMES[i], PROP_VALUES[i]);
    140             String errProp = (prefix.length() > 0) ? PROP_NAMES[i] : PREFIX
    141                     + PROP_NAMES[i];
    142             bindError(mockCtx, errProp);
    143         }
    144         for (int i = 0; i < MISSING_NAMES.length; i++)
    145         {
    146             bindError(mockCtx, MISSING_NAMES[i]);
    147         }
    148         mockCtx.matchAndReturn("hashCode", System.identityHashCode(mockCtx.proxy()));
    149
    150         return mockCtx;
    151     }
    152
    153     /***
    154      * Binds a property value to the mock context.
    155      *
    156      * @param mockCtx the context
    157      * @param name the name of the property
    158      * @param value the value of the property
    159      */
    160     private void bind(Mock mockCtx, String name, String value)
    161     {
    162         mockCtx.matchAndReturn(METHOD_LOOKUP, C.eq(name), value);
    163         bindError(mockCtx, name + MISSING_PROP);
    164     }
    165
    166     /***
    167      * Configures the mock to expect a call for a non existing property.
    168      *
    169      * @param mockCtx the mock
    170      * @param name the name of the property
    171      */
    172     private void bindError(Mock mockCtx, String name)
    173     {
    174         mockCtx.matchAndThrow(METHOD_LOOKUP, C.eq(name),
    175                 new NameNotFoundException("unknown property"));
    176     }
    177
    178     /***
    179      * Creates and initializes a mock for a naming enumeration.
    180      *
    181      * @param mockCtx the mock representing the context
    182      * @param names the names contained in the iteration
    183      * @param values the corresponding values
    184      * @param close a flag whether the enumeration should expect to be closed
    185      * @return the mock for the enumeration
    186      */
    187     private Mock createEnumMock(Mock mockCtx, String[] names, Object[] values,
    188             boolean close)
    189     {
    190         Mock mockEnum = new Mock(NamingEnumeration.class);
    191         for (int i = 0; i < names.length; i++)
    192         {
    193             addEnumPair(mockEnum, names[i], values[i]);
    194         }
    195         if (close)
    196         {
    197             closeEnum(mockEnum);
    198         }
    199         return mockEnum;
    200     }
    201
    202     /***
    203      * Creates and initializes a mock for a naming enumeration that expects to
    204      * be closed. This is a shortcut of createEnumMock(mockCtx, names, values,
    205      * true);
    206      *
    207      * @param mockCtx the mock representing the context
    208      * @param names the names contained in the iteration
    209      * @param values the corresponding values
    210      * @return the mock for the enumeration
    211      */
    212     private Mock createEnumMock(Mock mockCtx, String[] names, Object[] values)
    213     {
    214         return createEnumMock(mockCtx, names, values, true);
    215     }
    216
    217     /***
    218      * Adds a new name-and-value pair to an enum mock.
    219      *
    220      * @param mockEnum the enum mock
    221      * @param name the name
    222      * @param value the value
    223      */
    224     private void addEnumPair(Mock mockEnum, String name, Object value)
    225     {
    226         NameClassPair ncp = new NameClassPair(name, value.getClass().getName());
    227         mockEnum.expectAndReturn("hasMore", true);
    228         mockEnum.expectAndReturn("next", ncp);
    229     }
    230
    231     /***
    232      * Closes an enumeration mock.
    233      *
    234      * @param mockEnum the mock
    235      */
    236     private void closeEnum(Mock mockEnum)
    237     {
    238         mockEnum.expectAndReturn("hasMore", false);
    239         mockEnum.expect(METHOD_CLOSE);
    240     }
    241 }