001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.configuration; 018 019 import java.util.Hashtable; 020 021 import javax.naming.Context; 022 import javax.naming.NameClassPair; 023 import javax.naming.NameNotFoundException; 024 import javax.naming.NamingEnumeration; 025 import javax.naming.NamingException; 026 import javax.naming.spi.InitialContextFactory; 027 028 import com.mockobjects.dynamic.C; 029 import com.mockobjects.dynamic.Mock; 030 031 /** 032 * A mock implementation of the {@code InitialContextFactory} interface. 033 * This implementation will return a mock context that contains some test data. 034 * 035 * @author <a 036 * href="http://commons.apache.org/configuration/team-list.html">Commons 037 * Configuration team</a> 038 * @version $Id: MockInitialContextFactory.java 1222455 2011-12-22 21:10:10Z oheger $ 039 */ 040 public class MockInitialContextFactory implements InitialContextFactory 041 { 042 /** 043 * Constant for the use cycles environment property. If this property is 044 * present in the environment, a cyclic context will be created. 045 */ 046 public static final String PROP_CYCLES = "useCycles"; 047 048 /** Constant for the lookup method. */ 049 private static final String METHOD_LOOKUP = "lookup"; 050 051 /** Constant for the list method. */ 052 private static final String METHOD_LIST = "list"; 053 054 /** Constant for the close method.*/ 055 private static final String METHOD_CLOSE = "close"; 056 057 /** Constant for the name of the missing property. */ 058 private static final String MISSING_PROP = "/missing"; 059 060 /** Constant for the name of the prefix. */ 061 private static final String PREFIX = "test/"; 062 063 /** An array with the names of the supported properties. */ 064 private static final String[] PROP_NAMES = 065 { "key", "key2", "short", "boolean", "byte", "double", "float", "integer", 066 "long", "onlyinjndi" }; 067 068 /** An array with the values of the supported properties. */ 069 private static final String[] PROP_VALUES = 070 { "jndivalue", "jndivalue2", "1", "true", "10", "10.25", "20.25", "10", 071 "1000000", "true" }; 072 073 /** An array with properties that are requested, but are not in the context. */ 074 private static final String[] MISSING_NAMES = 075 { "missing/list", "test/imaginarykey", "foo/bar" }; 076 077 /** 078 * Creates a {@code Context} object that is backed by a mock object. 079 * The mock context can be queried for the values of certain test 080 * properties. It also supports listing the contained (sub) properties. 081 * 082 * @param env the environment 083 * @return the context mock 084 */ 085 public Context getInitialContext(@SuppressWarnings("rawtypes") Hashtable env) throws NamingException 086 { 087 boolean useCycles = env.containsKey(PROP_CYCLES); 088 089 Mock mockTopCtx = createCtxMock(PREFIX); 090 Mock mockCycleCtx = createCtxMock(""); 091 Mock mockPrfxCtx = createCtxMock(""); 092 Mock mockBaseCtx = new Mock(Context.class); 093 mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq(""), mockTopCtx.proxy()); 094 mockBaseCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx 095 .proxy()); 096 mockTopCtx.matchAndReturn(METHOD_LOOKUP, C.eq("test"), mockPrfxCtx 097 .proxy()); 098 mockPrfxCtx.matchAndReturn(METHOD_LIST, C.eq(""), createEnumMock( 099 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 }