1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 }