##### Coverage Report - org.apache.commons.configuration.beanutils.BeanHelper

Classes in this File | Line Coverage | Branch Coverage | Complexity

BeanHelper

90% | 60/67

94% | 15/16

3.3333333333333335;3,333

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.beanutils;

18

19

     import java.lang.reflect.InvocationTargetException;

20

     import java.util.HashMap;

21

     import java.util.Iterator;

22

     import java.util.Map;

23

     import java.util.Set;

24

25

     import org.apache.commons.beanutils.BeanUtils;

26

     import org.apache.commons.beanutils.PropertyUtils;

27

     import org.apache.commons.configuration.ConfigurationRuntimeException;

28

29

     /**

30

      * <p>

31

      * A helper class for creating bean instances that are defined in configuration

32

      * files.

33

      * </p>

34

      * <p>

35

      * This class provides static utility methods related to bean creation

36

      * operations. These methods simplify such operations because a client need not

37

      * deal with all involved interfaces. Usually, if a bean declaration has already

38

      * been obtained, a single method call is necessary to create a new bean

39

      * instance.

40

      * </p>

41

      * <p>

42

      * This class also supports the registration of custom bean factories.

43

      * Implementations of the <code>{@link BeanFactory}</code> interface can be

44

      * registered under a symbolic name using the <code>registerBeanFactory()</code>

45

      * method. In the configuration file the name of the bean factory can be

46

      * specified in the bean declaration. Then this factory will be used to create

47

      * the bean.

48

      * </p>

49

      *

50

      * @since 1.3

51

      * @author Oliver Heger

52

      * @version $Id: BeanHelper.java 439648 2006-09-02 20:42:10Z oheger $

53

      */

54

     public class BeanHelper

55

     {

56

         /** Stores a map with the registered bean factories. */

57

8

         private static Map beanFactories = new HashMap();

58

59

         /**

60

          * Stores the default bean factory, which will be used if no other factory

61

          * is provided.

62

          */

63

4

         private static BeanFactory defaultBeanFactory = DefaultBeanFactory.INSTANCE;

64

65

         /**

66

          * Private constructor, so no instances can be created.

67

          */

68

         private BeanHelper()

69

0

         {

70

0

         }

71

72

         /**

73

          * Register a bean factory under a symbolic name. This factory object can

74

          * then be specified in bean declarations with the effect that this factory

75

          * will be used to obtain an instance for the corresponding bean

76

          * declaration.

77

          *

78

          * @param name the name of the factory

79

          * @param factory the factory to be registered

80

          */

81

         public static void registerBeanFactory(String name, BeanFactory factory)

82

         {

83

13

             if (name == null)

84

             {

85

1

                 throw new IllegalArgumentException(

86

                         "Name for bean factory must not be null!");

87

             }

88

12

             if (factory == null)

89

             {

90

1

                 throw new IllegalArgumentException("Bean factory must not be null!");

91

             }

92

93

11

             beanFactories.put(name, factory);

94

11

         }

95

96

         /**

97

          * Deregisters the bean factory with the given name. After that this factory

98

          * cannot be used any longer.

99

          *

100

          * @param name the name of the factory to be deregistered

101

          * @return the factory that was registered under this name; <b>null</b> if

102

          * there was no such factory

103

          */

104

         public static BeanFactory deregisterBeanFactory(String name)

105

         {

106

11

             return (BeanFactory) beanFactories.remove(name);

107

         }

108

109

         /**

110

          * Returns a set with the names of all currently registered bean factories.

111

          *

112

          * @return a set with the names of the registered bean factories

113

          */

114

         public static Set registeredFactoryNames()

115

         {

116

42

             return beanFactories.keySet();

117

         }

118

119

         /**

120

          * Returns the default bean factory.

121

          *

122

          * @return the default bean factory

123

          */

124

         public static BeanFactory getDefaultBeanFactory()

125

         {

126

66

             return defaultBeanFactory;

127

         }

128

129

         /**

130

          * Sets the default bean factory. This factory will be used for all create

131

          * operations, for which no special factory is provided in the bean

132

          * declaration.

133

          *

134

          * @param factory the default bean factory (must not be <b>null</b>)

135

          */

136

         public static void setDefaultBeanFactory(BeanFactory factory)

137

         {

138

22

             if (factory == null)

139

             {

140

1

                 throw new IllegalArgumentException(

141

                         "Default bean factory must not be null!");

142

             }

143

21

             defaultBeanFactory = factory;

144

21

         }

145

146

         /**

147

          * Initializes the passed in bean. This method will obtain all the bean's

148

          * properties that are defined in the passed in bean declaration. These

149

          * properties will be set on the bean. If necessary, further beans will be

150

          * created recursively.

151

          *

152

          * @param bean the bean to be initialized

153

          * @param data the bean declaration

154

          * @throws ConfigurationRuntimeException if a property cannot be set

155

          */

156

         public static void initBean(Object bean, BeanDeclaration data)

157

                 throws ConfigurationRuntimeException

158

         {

159

102

             Map properties = data.getBeanProperties();

160

102

             if (properties != null)

161

             {

162

299

                 for (Iterator it = properties.keySet().iterator(); it.hasNext();)

163

                 {

164

98

                     String propName = (String) it.next();

165

98

                     initProperty(bean, propName, properties.get(propName));

166

                 }

167

             }

168

169

101

             Map nestedBeans = data.getNestedBeanDeclarations();

170

101

             if (nestedBeans != null)

171

             {

172

204

                 for (Iterator it = nestedBeans.keySet().iterator(); it.hasNext();)

173

                 {

174

18

                     String propName = (String) it.next();

175

18

                     initProperty(bean, propName, createBean(

176

                             (BeanDeclaration) nestedBeans.get(propName), null));

177

                 }

178

             }

179

101

         }

180

181

         /**

182

          * Sets a property on the given bean using Common Beanutils.

183

          *

184

          * @param bean the bean

185

          * @param propName the name of the property

186

          * @param value the property's value

187

          * @throws ConfigurationRuntimeException if the property is not writeable or

188

          * an error occurred

189

          */

190

         private static void initProperty(Object bean, String propName, Object value)

191

                 throws ConfigurationRuntimeException

192

         {

193

116

             if (!PropertyUtils.isWriteable(bean, propName))

194

             {

195

1

                 throw new ConfigurationRuntimeException("Property " + propName

196

                         + " cannot be set!");

197

             }

198

199

             try

200

             {

201

115

                 BeanUtils.setProperty(bean, propName, value);

202

115

             }

203

             catch (IllegalAccessException iaex)

204

             {

205

0

                 throw new ConfigurationRuntimeException(iaex);

206

             }

207

             catch (InvocationTargetException itex)

208

             {

209

0

                 throw new ConfigurationRuntimeException(itex);

210

             }

211

115

         }

212

213

         /**

214

          * The main method for creating and initializing beans from a configuration.

215

          * This method will return an initialized instance of the bean class

216

          * specified in the passed in bean declaration. If this declaration does not

217

          * contain the class of the bean, the passed in default class will be used.

218

          * From the bean declaration the factory to be used for creating the bean is

219

          * queried. The declaration may here return <b>null</b>, then a default

220

          * factory is used. This factory is then invoked to perform the create

221

          * operation.

222

          *

223

          * @param data the bean declaration

224

          * @param defaultClass the default class to use

225

          * @param param an additional parameter that will be passed to the bean

226

          * factory; some factories may support parameters and behave different

227

          * depending on the value passed in here

228

          * @return the new bean

229

          * @throws ConfigurationRuntimeException if an error occurs

230

          */

231

         public static Object createBean(BeanDeclaration data, Class defaultClass,

232

                 Object param) throws ConfigurationRuntimeException

233

         {

234

104

             if (data == null)

235

             {

236

1

                 throw new IllegalArgumentException(

237

                         "Bean declaration must not be null!");

238

             }

239

240

103

             BeanFactory factory = fetchBeanFactory(data);

241

             try

242

             {

243

102

                 return factory.createBean(fetchBeanClass(data, defaultClass,

244

                         factory), data, param);

245

             }

246

             catch (Exception ex)

247

             {

248

5

                 throw new ConfigurationRuntimeException(ex);

249

             }

250

         }

251

252

         /**

253

          * Returns a bean instance for the specified declaration. This method is a

254

          * short cut for <code>createBean(data, null, null);</code>.

255

          *

256

          * @param data the bean declaration

257

          * @param defaultClass the class to be used when in the declation no class

258

          * is specified

259

          * @return the new bean

260

          * @throws ConfigurationRuntimeException if an error occurs

261

          */

262

         public static Object createBean(BeanDeclaration data, Class defaultClass)

263

                 throws ConfigurationRuntimeException

264

         {

265

103

             return createBean(data, defaultClass, null);

266

         }

267

268

         /**

269

          * Returns a bean instance for the specified declaration. This method is a

270

          * short cut for <code>createBean(data, null);</code>.

271

          *

272

          * @param data the bean declaration

273

          * @return the new bean

274

          * @throws ConfigurationRuntimeException if an error occurs

275

          */

276

         public static Object createBean(BeanDeclaration data)

277

                 throws ConfigurationRuntimeException

278

         {

279

60

             return createBean(data, null);

280

         }

281

282

         /**

283

          * Returns a <code>java.lang.Class</code> object for the specified name.

284

          * This method and the helper method it invokes are very similar to code

285

          * extracted from the <code>ClassLoaderUtils</code> class of Commons

286

          * Jelly. It should be replaced if Commons Lang provides a generic version.

287

          *

288

          * @param name the name of the class to be loaded

289

          * @param callingClass the calling class

290

          * @return the class object for the specified name

291

          * @throws ClassNotFoundException if the class cannot be loaded

292

          */

293

         static Class loadClass(String name, Class callingClass)

294

                 throws ClassNotFoundException

295

         {

296

23

             ClassLoader loader = findClassLoader(callingClass);

297

23

             return Class.forName(name, true, loader);

298

         }

299

300

         /**

301

          * Determines which class loader should be used in the context of the given

302

          * class.

303

          *

304

          * @param callingClass the calling class

305

          * @return the class loader to be used

306

          */

307

         private static ClassLoader findClassLoader(Class callingClass)

308

         {

309

23

             ClassLoader loader = Thread.currentThread().getContextClassLoader();

310

23

             if (loader == null)

311

             {

312

0

                 loader = callingClass.getClassLoader();

313

0

                 if (loader == null)

314

                 {

315

0

                     loader = ClassLoader.getSystemClassLoader();

316

                 }

317

             }

318

23

             return loader;

319

         }

320

321

         /**

322

          * Determines the class of the bean to be created. If the bean declaration

323

          * contains a class name, this class is used. Otherwise it is checked

324

          * whether a default class is provided. If this is not the case, the

325

          * factory's default class is used. If this class is undefined, too, an

326

          * exception is thrown.

327

          *

328

          * @param data the bean declaration

329

          * @param defaultClass the default class

330

          * @param factory the bean factory to use

331

          * @return the class of the bean to be created

332

          * @throws ConfigurationRuntimeException if the class cannot be determined

333

          */

334

         private static Class fetchBeanClass(BeanDeclaration data,

335

                 Class defaultClass, BeanFactory factory)

336

                 throws ConfigurationRuntimeException

337

         {

338

102

             String clsName = data.getBeanClassName();

339

102

             if (clsName != null)

340

             {

341

                 try

342

                 {

343

23

                     return loadClass(clsName, factory.getClass());

344

                 }

345

                 catch (ClassNotFoundException cex)

346

                 {

347

1

                     throw new ConfigurationRuntimeException(cex);

348

                 }

349

             }

350

351

79

             if (defaultClass != null)

352

             {

353

18

                 return defaultClass;

354

             }

355

356

61

             Class clazz = factory.getDefaultBeanClass();

357

61

             if (clazz == null)

358

             {

359

1

                 throw new ConfigurationRuntimeException(

360

                         "Bean class is not specified!");

361

             }

362

60

             return clazz;

363

         }

364

365

         /**

366

          * Obtains the bean factory to use for creating the specified bean. This

367

          * method will check whether a factory is specified in the bean declaration.

368

          * If this is not the case, the default bean factory will be used.

369

          *

370

          * @param data the bean declaration

371

          * @return the bean factory to use

372

          * @throws ConfigurationRuntimeException if the factory cannot be determined

373

          */

374

         private static BeanFactory fetchBeanFactory(BeanDeclaration data)

375

                 throws ConfigurationRuntimeException

376

         {

377

103

             String factoryName = data.getBeanFactoryName();

378

103

             if (factoryName != null)

379

             {

380

67

                 BeanFactory factory = (BeanFactory) beanFactories.get(factoryName);

381

67

                 if (factory == null)

382

                 {

383

1

                     throw new ConfigurationRuntimeException(

384

                             "Unknown bean factory: " + factoryName);

385

                 }

386

                 else

387

                 {

388

66

                     return factory;

389

                 }

390

             }

391

             else

392

             {

393

36

                 return getDefaultBeanFactory();

394

             }

395

         }

396

     }