##### Coverage Report - org.apache.commons.configuration.DefaultConfigurationBuilder

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

DefaultConfigurationBuilder

100% | 76/76

100% | 12/12

1.6666666666666667;1,667

DefaultConfigurationBuilder$ConfigurationBeanFactory

100% | 12/12

100% | 3/3

1.6666666666666667;1,667

DefaultConfigurationBuilder$ConfigurationBuilderProvider

100% | 5/5

100% | 1/1

1.6666666666666667;1,667

DefaultConfigurationBuilder$ConfigurationDeclaration

100% | 16/16

100% | 4/4

1.6666666666666667;1,667

DefaultConfigurationBuilder$ConfigurationProvider

100% | 9/9

N/A | N/A

1.6666666666666667;1,667

DefaultConfigurationBuilder$FileConfigurationProvider

100% | 11/11

N/A | N/A

1.6666666666666667;1,667

DefaultConfigurationBuilder$FileExtensionConfigurationProvider

100% | 9/9

100% | 1/1

1.6666666666666667;1,667

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.io.File;

20

     import java.net.URL;

21

     import java.util.HashMap;

22

     import java.util.Iterator;

23

     import java.util.List;

24

     import java.util.Map;

25

26

     import org.apache.commons.configuration.beanutils.BeanDeclaration;

27

     import org.apache.commons.configuration.beanutils.BeanFactory;

28

     import org.apache.commons.configuration.beanutils.BeanHelper;

29

     import org.apache.commons.configuration.beanutils.DefaultBeanFactory;

30

     import org.apache.commons.configuration.beanutils.XMLBeanDeclaration;

31

     import org.apache.commons.configuration.plist.PropertyListConfiguration;

32

     import org.apache.commons.configuration.plist.XMLPropertyListConfiguration;

33

     import org.apache.commons.configuration.tree.ConfigurationNode;

34

     import org.apache.commons.configuration.tree.DefaultExpressionEngine;

35

     import org.apache.commons.configuration.tree.OverrideCombiner;

36

     import org.apache.commons.configuration.tree.UnionCombiner;

37

     import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;

38

39

     /**

40

      * <p>

41

      * A factory class that creates a composite configuration from an XML based

42

      * <em>configuration definition file</em>.

43

      * </p>

44

      * <p>

45

      * This class provides an easy and flexible means for loading multiple

46

      * configuration sources and combining the results into a single configuration

47

      * object. The sources to be loaded are defined in an XML document that can

48

      * contain certain tags representing the different supported configuration

49

      * classes. If such a tag is found, the corresponding <code>Configuration</code>

50

      * class is instantiated and initialized using the classes of the

51

      * <code>beanutils</code> package (namely

52

      * <code>{@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}</code>

53

      * will be used to extract the configuration's initialization parameters, which

54

      * allows for complex initialization szenarios).

55

      * </p>

56

      * <p>

57

      * It is also possible to add custom tags to the configuration definition file.

58

      * For this purpose register your own <code>ConfigurationProvider</code>

59

      * implementation for your tag using the <code>addConfigurationProvider()</code>

60

      * method. This provider will then be called when the corresponding custom tag

61

      * is detected. For the default configuration classes providers are already

62

      * registered.

63

      * </p>

64

      * <p>

65

      * The configuration definition file has the following basic structure:

66

      * </p>

67

      * <p>

68

      *

69

      * <pre>

70

      * &lt;configuration&gt;

71

      *   &lt;header&gt;

72

      *     &lt;!-- Optional meta information about the composite configuration --&gt;

73

      *   &lt;/header&gt;

74

      *   &lt;override&gt;

75

      *     &lt;!-- Declarations for override configurations --&gt;

76

      *   &lt;/override&gt;

77

      *   &lt;additional&gt;

78

      *     &lt;!-- Declarations for union configurations --&gt;

79

      *   &lt;/additional&gt;

80

      * &lt;/configuration&gt;

81

      * </pre>

82

      *

83

      * </p>

84

      * <p>

85

      * The name of the root element (here <code>configuration</code>) is

86

      * arbitrary. There are two sections (both of them are optional) for declaring

87

      * <em>override</em> and <em>additional</em> configurations. Configurations

88

      * in the former section are evaluated in the order of their declaration, and

89

      * properties of configurations declared earlier hide those of configurations

90

      * declared later. Configurations in the latter section are combined to a union

91

      * configuration, i.e. all of their properties are added to a large hierarchical

92

      * configuration. Configuration declarations that occur as direct children of

93

      * the root element are treated as override declarations.

94

      * </p>

95

      * <p>

96

      * Each configuration declaration consists of a tag whose name is associated

97

      * with a <code>ConfigurationProvider</code>. This can be one of the

98

      * pre-defined tags like <code>properties</code>, or <code>xml</code>, or

99

      * a custom tag, for which a configuration provider was registered. Attributes

100

      * and sub elements with specific initialization parameters can be added. There

101

      * are some reserved attributes with a special meaning that can be used in every

102

      * configuration declaration:

103

      * </p>

104

      * <p>

105

      * <table border="1">

106

      * <tr>

107

      * <th>Attribute</th>

108

      * <th>Meaning</th>

109

      * </tr>

110

      * <tr>

111

      * <td valign="top"><code>config-name</code></td>

112

      * <td>Allows to specify a name for this configuration. This name can be used

113

      * to obtain a reference to the configuration from the resulting combined

114

      * configuration (see below).</td>

115

      * </tr>

116

      * <tr>

117

      * <td valign="top"><code>config-at</code></td>

118

      * <td>With this attribute an optional prefix can be specified for the

119

      * properties of the corresponding configuration.</td>

120

      * </tr>

121

      * <tr>

122

      * <td valign="top"><code>config-optional</code></td>

123

      * <td>Declares a configuration as optional. This means that errors that occur

124

      * when creating the configuration are silently ignored.</td>

125

      * </tr>

126

      * </table>

127

      * </p>

128

      * <p>

129

      * The optional <em>header</em> section can contain some meta data about the

130

      * created configuration itself. For instance, it is possible to set further

131

      * properties of the <code>NodeCombiner</code> objects used for constructing

132

      * the resulting configuration.

133

      * </p>

134

      * <p>

135

      * The configuration object returned by this builder is an instance of the

136

      * <code>{@link CombinedConfiguration}</code> class. The return value of the

137

      * <code>getConfiguration()</code> method can be casted to this type, and the

138

      * <code>getConfiguration(boolean)</code> method directly declares

139

      * <code>CombinedConfiguration</code> as return type. This allows for

140

      * convenient access to the configuration objects maintained by the combined

141

      * configuration (e.g. for updates of single configuration objects). It has also

142

      * the advantage that the properties stored in all declared configuration

143

      * objects are collected and transformed into a single hierarchical structure,

144

      * which can be accessed using different expression engines.

145

      * </p>

146

      * <p>

147

      * All declared override configurations are directly added to the resulting

148

      * combined configuration. If they are given names (using the

149

      * <code>config-name</code> attribute), they can directly be accessed using

150

      * the <code>getConfiguration(String)</code> method of

151

      * <code>CombinedConfiguration</code>. The additional configurations are

152

      * alltogether added to another combined configuration, which uses a union

153

      * combiner. Then this union configuration is added to the resulting combined

154

      * configuration under the name defined by the <code>ADDITIONAL_NAME</code>

155

      * constant.

156

      * </p>

157

      * <p>

158

      * Implementation note: This class is not thread-safe. Especially the

159

      * <code>getConfiguration()</code> methods should be called by a single thread

160

      * only.

161

      * </p>

162

      *

163

      * @since 1.3

164

      * @author <a

165

      * href="http://jakarta.apache.org/commons/configuration/team-list.html">Commons

166

      * Configuration team</a>

167

      * @version $Id: DefaultConfigurationBuilder.java 384601 2006-03-09 20:22:58Z

168

      * oheger $

169

      */

170

18

     public class DefaultConfigurationBuilder extends XMLConfiguration implements

171

             ConfigurationBuilder

172

     {

173

         /**

174

          * Constant for the name of the additional configuration. If the

175

          * configuration definition file contains an <code>additional</code>

176

          * section, a special union configuration is created and added under this

177

          * name to the resulting combined configuration.

178

          */

179

4

         public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class

180

                 .getName()

181

                 + "/ADDITIONAL_CONFIG";

182

183

         /** Constant for the expression engine used by this builder. */

184

2

         static final XPathExpressionEngine EXPRESSION_ENGINE = new XPathExpressionEngine();

185

186

         /** Constant for the name of the configuration bean factory. */

187

2

         static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class

188

                 .getName()

189

                 + ".CONFIG_BEAN_FACTORY_NAME";

190

191

         /** Constant for the reserved name attribute. */

192

         static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START

193

                 + XMLBeanDeclaration.RESERVED_PREFIX

194

                 + "name"

195

                 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;

196

197

         /** Constant for the name of the at attribute. */

198

         static final String ATTR_ATNAME = "at";

199

200

         /** Constant for the reserved at attribute. */

201

         static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START

202

                 + XMLBeanDeclaration.RESERVED_PREFIX

203

                 + ATTR_ATNAME

204

                 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;

205

206

         /** Constant for the at attribute without the reserved prefix. */

207

         static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START

208

                 + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;

209

210

         /** Constant for the name of the optional attribute. */

211

         static final String ATTR_OPTIONALNAME = "optional";

212

213

         /** Constant for the reserved optional attribute. */

214

         static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START

215

                 + XMLBeanDeclaration.RESERVED_PREFIX

216

                 + ATTR_OPTIONALNAME

217

                 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;

218

219

         /** Constant for the optional attribute without the reserved prefix. */

220

         static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START

221

                 + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;

222

223

         /** Constant for the file name attribute. */

224

         static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START

225

                 + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;

226

227

         /** Constant for the name of the header section. */

228

         static final String SEC_HEADER = "header";

229

230

         /** Constant for an expression that selects the union configurations. */

231

         static final String KEY_UNION = "/additional/*";

232

233

         /** Constant for an expression that selects override configurations. */

234

         static final String KEY_OVERRIDE1 = "/*[local-name() != 'additional' and "

235

                 + "local-name() != 'override' and local-name() != '"

236

                 + SEC_HEADER + "']";

237

238

         /**

239

          * Constant for an expression that selects override configurations in the

240

          * override section.

241

          */

242

         static final String KEY_OVERRIDE2 = "/override/*";

243

244

         /**

245

          * Constant for the key that points to the list nodes definition of the

246

          * override combiner.

247

          */

248

         static final String KEY_OVERRIDE_LIST = SEC_HEADER

249

                 + "/combiner/override/list-nodes/node";

250

251

         /**

252

          * Constant for the key that points to the list nodes definition of the

253

          * additional combiner.

254

          */

255

         static final String KEY_ADDITIONAL_LIST = SEC_HEADER

256

                 + "/combiner/additional/list-nodes/node";

257

258

         /**

259

          * Constant for the key of the result declaration. This key can point to a

260

          * bean declaration, which defines properties of the resulting combined

261

          * configuration.

262

          */

263

         static final String KEY_RESULT = SEC_HEADER + "/result";

264

265

         /** Constant for the key of the combiner in the result declaration.*/

266

         static final String KEY_COMBINER = KEY_RESULT + "/nodeCombiner";

267

268

         /** Constant for the XML file extension. */

269

         static final String EXT_XML = ".xml";

270

271

         /** Constant for the provider for properties files. */

272

2

         private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider(

273

                 XMLPropertiesConfiguration.class, PropertiesConfiguration.class,

274

                 EXT_XML);

275

276

         /** Constant for the provider for XML files. */

277

2

         private static final ConfigurationProvider XML_PROVIDER = new FileConfigurationProvider(

278

                 XMLConfiguration.class);

279

280

         /** Constant for the provider for JNDI sources. */

281

2

         private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider(

282

                 JNDIConfiguration.class);

283

284

         /** Constant for the provider for system properties. */

285

2

         private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider(

286

                 SystemConfiguration.class);

287

288

         /** Constant for the provider for plist files. */

289

2

         private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider(

290

                 XMLPropertyListConfiguration.class,

291

                 PropertyListConfiguration.class, EXT_XML);

292

293

         /** Constant for the provider for configuration definition files.*/

294

2

         private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider();

295

296

         /** An array with the names of the default tags. */

297

2

         private static final String[] DEFAULT_TAGS =

298

         {"properties", "xml", "hierarchicalXml", "jndi", "system", "plist", "configuration"};

299

300

         /** An array with the providers for the default tags. */

301

2

         private static final ConfigurationProvider[] DEFAULT_PROVIDERS =

302

         {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER,

303

                 SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER};

304

305

         /**

306

          * The serial version UID.

307

          */

308

         private static final long serialVersionUID = -3113777854714492123L;

309

310

         /** Stores the configuration that is currently constructed.*/

311

         private CombinedConfiguration constructedConfiguration;

312

313

         /** Stores a map with the registered configuration providers. */

314

         private Map providers;

315

316

         /** Stores the base path to the configuration sources to load. */

317

         private String configurationBasePath;

318

319

         /**

320

          * Creates a new instance of <code>DefaultConfigurationBuilder</code>. A

321

          * configuration definition file is not yet loaded. Use the diverse setter

322

          * methods provided by file based configurations to specify the

323

          * configuration definition file.

324

          */

325

         public DefaultConfigurationBuilder()

326

         {

327

31

             super();

328

31

             providers = new HashMap();

329

31

             setExpressionEngine(EXPRESSION_ENGINE);

330

31

             registerDefaultProviders();

331

31

         }

332

333

         /**

334

          * Creates a new instance of <code>DefaultConfigurationBuilder</code> and

335

          * sets the specified configuration definition file.

336

          *

337

          * @param file the configuration definition file

338

          */

339

         public DefaultConfigurationBuilder(File file)

340

         {

341

1

             this();

342

1

             setFile(file);

343

1

         }

344

345

         /**

346

          * Creates a new instance of <code>DefaultConfigurationBuilder</code> and

347

          * sets the specified configuration definition file.

348

          *

349

          * @param fileName the name of the configuration definition file

350

          * @throws ConfigurationException if an error occurs when the file is loaded

351

          */

352

         public DefaultConfigurationBuilder(String fileName)

353

                 throws ConfigurationException

354

         {

355

2

             this();

356

2

             setFileName(fileName);

357

2

         }

358

359

         /**

360

          * Creates a new instance of <code>DefaultConfigurationBuilder</code> and

361

          * sets the specified configuration definition file.

362

          *

363

          * @param url the URL to the configuration definition file

364

          * @throws ConfigurationException if an error occurs when the file is loaded

365

          */

366

         public DefaultConfigurationBuilder(URL url) throws ConfigurationException

367

         {

368

1

             this();

369

1

             setURL(url);

370

1

         }

371

372

         /**

373

          * Returns the base path for the configuration sources to load. This path is

374

          * used to resolve relative paths in the configuration definition file.

375

          *

376

          * @return the base path for configuration sources

377

          */

378

         public String getConfigurationBasePath()

379

         {

380

50

             return (configurationBasePath != null) ? configurationBasePath

381

                     : getBasePath();

382

         }

383

384

         /**

385

          * Sets the base path for the configuration sources to load. Normally a base

386

          * path need not to be set because it is determined by the location of the

387

          * configuration definition file to load. All relative pathes in this file

388

          * are resolved relative to this file. Setting a base path makes sense if

389

          * such relative pathes should be otherwise resolved, e.g. if the

390

          * configuration file is loaded from the class path and all sub

391

          * configurations it refers to are stored in a special config directory.

392

          *

393

          * @param configurationBasePath the new base path to set

394

          */

395

         public void setConfigurationBasePath(String configurationBasePath)

396

         {

397

1

             this.configurationBasePath = configurationBasePath;

398

1

         }

399

400

         /**

401

          * Adds a configuration provider for the specified tag. Whenever this tag is

402

          * encountered in the configuration definition file this provider will be

403

          * called to create the configuration object.

404

          *

405

          * @param tagName the name of the tag in the configuration definition file

406

          * @param provider the provider for this tag

407

          */

408

         public void addConfigurationProvider(String tagName,

409

                 ConfigurationProvider provider)

410

         {

411

222

             if (tagName == null)

412

             {

413

1

                 throw new IllegalArgumentException("Tag name must not be null!");

414

             }

415

221

             if (provider == null)

416

             {

417

1

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

418

             }

419

420

220

             providers.put(tagName, provider);

421

220

         }

422

423

         /**

424

          * Removes the configuration provider for the specified tag name.

425

          *

426

          * @param tagName the tag name

427

          * @return the removed configuration provider or <b>null</b> if none was

428

          * registered for that tag

429

          */

430

         public ConfigurationProvider removeConfigurationProvider(String tagName)

431

         {

432

3

             return (ConfigurationProvider) providers.remove(tagName);

433

         }

434

435

         /**

436

          * Returns the configuration provider for the given tag.

437

          *

438

          * @param tagName the name of the tag

439

          * @return the provider that was registered for this tag or <b>null</b> if

440

          * there is none

441

          */

442

         public ConfigurationProvider providerForTag(String tagName)

443

         {

444

62

             return (ConfigurationProvider) providers.get(tagName);

445

         }

446

447

         /**

448

          * Returns the configuration provided by this builder. Loads and parses the

449

          * configuration definition file and creates instances for the declared

450

          * configurations.

451

          *

452

          * @return the configuration

453

          * @throws ConfigurationException if an error occurs

454

          */

455

         public Configuration getConfiguration() throws ConfigurationException

456

         {

457

10

             return getConfiguration(true);

458

         }

459

460

         /**

461

          * Returns the configuration provided by this builder. If the boolean

462

          * parameter is <b>true</b>, the configuration definition file will be

463

          * loaded. It will then be parsed, and instances for the declared

464

          * configurations will be created.

465

          *

466

          * @param load a flag whether the configuration definition file should be

467

          * loaded; a value of <b>false</b> would make sense if the file has already

468

          * been created or its content was manipulated using some of the property

469

          * accessor methods

470

          * @return the configuration

471

          * @throws ConfigurationException if an error occurs

472

          */

473

         public CombinedConfiguration getConfiguration(boolean load)

474

                 throws ConfigurationException

475

         {

476

18

             if (load)

477

             {

478

15

                 load();

479

             }

480

481

17

             CombinedConfiguration result = createResultConfiguration();

482

17

             constructedConfiguration = result;

483

484

17

             List overrides = configurationsAt(KEY_OVERRIDE1);

485

17

             overrides.addAll(configurationsAt(KEY_OVERRIDE2));

486

17

             initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);

487

488

16

             List additionals = configurationsAt(KEY_UNION);

489

16

             if (!additionals.isEmpty())

490

             {

491

6

                 CombinedConfiguration addConfig = new CombinedConfiguration(

492

                         new UnionCombiner());

493

6

                 result.addConfiguration(addConfig, ADDITIONAL_NAME);

494

6

                 initCombinedConfiguration(addConfig, additionals,

495

                         KEY_ADDITIONAL_LIST);

496

             }

497

498

16

             return result;

499

         }

500

501

         /**

502

          * Creates the resulting combined configuration. This method is called by

503

          * <code>getConfiguration()</code>. It checks whether the

504

          * <code>header</code> section of the configuration definition file

505

          * contains a <code>result</code> element. If this is the case, it will be

506

          * used to initialize the properties of the newly created configuration

507

          * object.

508

          *

509

          * @return the resulting configuration object

510

          * @throws ConfigurationException if an error occurs

511

          */

512

         protected CombinedConfiguration createResultConfiguration()

513

                 throws ConfigurationException

514

         {

515

17

             XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);

516

17

             CombinedConfiguration result = (CombinedConfiguration) BeanHelper

517

                     .createBean(decl, CombinedConfiguration.class);

518

519

17

             if (getMaxIndex(KEY_COMBINER) < 0)

520

             {

521

                 // No combiner defined => set default

522

14

                 result.setNodeCombiner(new OverrideCombiner());

523

             }

524

525

17

             return result;

526

         }

527

528

         /**

529

          * Initializes a combined configuration for the configurations of a specific

530

          * section. This method is called for the override and for the additional

531

          * section (if it exists).

532

          *

533

          * @param config the configuration to be initialized

534

          * @param containedConfigs the list with the declaratinos of the contained

535

          * configurations

536

          * @param keyListNodes a list with the declaration of list nodes

537

          * @throws ConfigurationException if an error occurs

538

          */

539

         protected void initCombinedConfiguration(CombinedConfiguration config,

540

                 List containedConfigs, String keyListNodes) throws ConfigurationException

541

         {

542

23

             List listNodes = getList(keyListNodes);

543

52

             for (Iterator it = listNodes.iterator(); it.hasNext();)

544

             {

545

6

                 config.getNodeCombiner().addListNode((String) it.next());

546

             }

547

548

102

             for (Iterator it = containedConfigs.iterator(); it.hasNext();)

549

             {

550

57

                 HierarchicalConfiguration conf = (HierarchicalConfiguration) it

551

                         .next();

552

57

                 ConfigurationDeclaration decl = new ConfigurationDeclaration(this,

553

                         conf);

554

57

                 AbstractConfiguration newConf = createConfigurationAt(decl);

555

56

                 if (newConf != null)

556

                 {

557

48

                     config.addConfiguration(newConf, decl.getConfiguration()

558

                             .getString(ATTR_NAME), decl.getAt());

559

                 }

560

             }

561

22

         }

562

563

         /**

564

          * Registers the default configuration providers supported by this class.

565

          * This method will be called during initialization. It registers

566

          * configuration providers for the tags that are supported by default.

567

          */

568

         protected void registerDefaultProviders()

569

         {

570

248

             for (int i = 0; i < DEFAULT_TAGS.length; i++)

571

             {

572

217

                 addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);

573

             }

574

31

         }

575

576

         /**

577

          * Performs interpolation. This method will not only take this configuration

578

          * instance into account (which is the one that loaded the configuration

579

          * definition file), but also the so far constructed combined configuration.

580

          * So variables can be used that point to properties that are defined in

581

          * configuration sources loaded by this builder.

582

          *

583

          * @param value the value to be interpolated

584

          * @return the interpolated value

585

          */

586

         protected Object interpolate(Object value)

587

         {

588

309

             Object result = super.interpolate(value);

589

309

             if (constructedConfiguration != null)

590

             {

591

252

                 result = constructedConfiguration.interpolate(result);

592

             }

593

309

             return result;

594

         }

595

596

         /**

597

          * Creates a configuration object from the specified configuration

598

          * declaration.

599

          *

600

          * @param decl the configuration declaration

601

          * @return the new configuration object

602

          * @throws ConfigurationException if an error occurs

603

          */

604

         private AbstractConfiguration createConfigurationAt(

605

                 ConfigurationDeclaration decl) throws ConfigurationException

606

         {

607

             try

608

             {

609

57

                 return (AbstractConfiguration) BeanHelper.createBean(decl);

610

             }

611

             catch (Exception ex)

612

             {

613

                 // redirect to configuration exceptions

614

1

                 throw new ConfigurationException(ex);

615

             }

616

         }

617

618

         /**

619

          * <p>

620

          * A base class for creating and initializing configuration sources.

621

          * </p>

622

          * <p>

623

          * Concrete sub classes of this base class are responsible for creating

624

          * specific <code>Configuration</code> objects for the tags in the

625

          * configuration definition file. The configuration factory will parse the

626

          * definition file and try to find a matching

627

          * <code>ConfigurationProvider</code> for each encountered tag. This

628

          * provider is then asked to create a corresponding

629

          * <code>Configuration</code> object. It is up to a concrete

630

          * implementation how this object is created and initialized.

631

          * </p>

632

          * <p>

633

          * Note that at the moment only configuration classes derived from

634

          * <code>{@link AbstractConfiguration}</code> are supported.

635

          * </p>

636

          */

637

         public static class ConfigurationProvider extends DefaultBeanFactory

638

         {

639

             /** Stores the class of the configuration to be created. */

640

             private Class configurationClass;

641

642

             /**

643

              * Creates a new uninitialized instance of

644

              * <code>ConfigurationProvider</code>.

645

              */

646

             public ConfigurationProvider()

647

             {

648

7

                 this(null);

649

7

             }

650

651

             /**

652

              * Creates a new instance of <code>ConfigurationProvider</code> and

653

              * sets the class of the configuration created by this provider.

654

              *

655

              * @param configClass the configuration class

656

              */

657

             public ConfigurationProvider(Class configClass)

658

16

             {

659

16

                 setConfigurationClass(configClass);

660

16

             }

661

662

             /**

663

              * Returns the class of the configuration returned by this provider.

664

              *

665

              * @return the class of the provided configuration

666

              */

667

             public Class getConfigurationClass()

668

             {

669

58

                 return configurationClass;

670

             }

671

672

             /**

673

              * Sets the class of the configuration returned by this provider.

674

              *

675

              * @param configurationClass the configuration class

676

              */

677

             public void setConfigurationClass(Class configurationClass)

678

             {

679

16

                 this.configurationClass = configurationClass;

680

16

             }

681

682

             /**

683

              * Returns the configuration. This method is called to fetch the

684

              * configuration from the provider. This implementation will call the

685

              * inherited

686

              * <code>{@link org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object) createBean()}</code>

687

              * method to create a new instance of the configuration class.

688

              *

689

              * @param decl the bean declaration with initialization parameters for

690

              * the configuration

691

              * @return the new configuration object

692

              * @throws Exception if an error occurs

693

              */

694

             public AbstractConfiguration getConfiguration(

695

                     ConfigurationDeclaration decl) throws Exception

696

             {

697

58

                 return (AbstractConfiguration) createBean(getConfigurationClass(),

698

                         decl, null);

699

             }

700

         }

701

702

         /**

703

          * <p>

704

          * A specialized <code>BeanDeclaration</code> implementation that

705

          * represents the declaration of a configuration source.

706

          * </p>

707

          * <p>

708

          * Instances of this class are able to extract all information about a

709

          * configuration source from the configuration definition file. The

710

          * declaration of a configuration source is very similar to a bean

711

          * declaration processed by <code>XMLBeanDeclaration</code>. There are

712

          * very few differences, e.g. the two reserved attributes

713

          * <code>optional</code> and <code>at</code> and the fact that a bean

714

          * factory is never needed.

715

          * </p>

716

          */

717

2

         protected static class ConfigurationDeclaration extends XMLBeanDeclaration

718

         {

719

             /** Stores a reference to the associated configuration builder. */

720

             private DefaultConfigurationBuilder configurationBuilder;

721

722

             /**

723

              * Creates a new instance of <code>ConfigurationDeclaration</code> and

724

              * initializes it.

725

              *

726

              * @param builder the associated configuration builder

727

              * @param config the configuration this declaration is based onto

728

              */

729

             public ConfigurationDeclaration(DefaultConfigurationBuilder builder,

730

                     HierarchicalConfiguration config)

731

             {

732

63

                 super(config);

733

63

                 configurationBuilder = builder;

734

63

             }

735

736

             /**

737

              * Returns the associated configuration builder.

738

              *

739

              * @return the configuration builder

740

              */

741

             public DefaultConfigurationBuilder getConfigurationBuilder()

742

             {

743

166

                 return configurationBuilder;

744

             }

745

746

             /**

747

              * Returns the value of the <code>at</code> attribute.

748

              *

749

              * @return the value of the <code>at</code> attribute (can be <b>null</b>)

750

              */

751

             public String getAt()

752

             {

753

52

                 String result = this.getConfiguration().getString(ATTR_AT_RES);

754

52

                 return (result == null) ? this.getConfiguration().getString(ATTR_AT)

755

                         : result;

756

             }

757

758

             /**

759

              * Returns a flag whether this is an optional configuration.

760

              *

761

              * @return a flag if this declaration points to an optional

762

              * configuration

763

              */

764

             public boolean isOptional()

765

             {

766

14

                 Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,

767

                         null);

768

14

                 if (value == null)

769

                 {

770

11

                     value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,

771

                             Boolean.FALSE);

772

                 }

773

13

                 return value.booleanValue();

774

             }

775

776

             /**

777

              * Returns the name of the bean factory. For configuration source

778

              * declarations always a reserved factory is used. This factory's name

779

              * is returned by this implementation.

780

              *

781

              * @return the name of the bean factory

782

              */

783

             public String getBeanFactoryName()

784

             {

785

59

                 return CONFIG_BEAN_FACTORY_NAME;

786

             }

787

788

             /**

789

              * Returns the bean's class name. This implementation will always return

790

              * <b>null</b>.

791

              *

792

              * @return the name of the bean's class

793

              */

794

             public String getBeanClassName()

795

             {

796

59

                 return null;

797

             }

798

799

             /**

800

              * Checks whether the given node is reserved. This method will take

801

              * further reserved attributes into account

802

              *

803

              * @param nd the node

804

              * @return a flag whether this node is reserved

805

              */

806

             protected boolean isReservedNode(ConfigurationNode nd)

807

             {

808

102

                 if (super.isReservedNode(nd))

809

                 {

810

21

                     return true;

811

                 }

812

813

81

                 return nd.isAttribute()

814

                         && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()

815

                                 .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME

816

                                 .equals(nd.getName()) && nd.getParentNode()

817

                                 .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));

818

             }

819

820

             /**

821

              * Performs interpolation. This implementation will delegate

822

              * interpolation to the configuration builder, which takes care that the

823

              * currently constructed configuration is taken into account, too.

824

              *

825

              * @param value the value to be interpolated

826

              * @return the interpolated value

827

              */

828

             protected Object interpolate(Object value)

829

             {

830

57

                 return getConfigurationBuilder().interpolate(value);

831

             }

832

         }

833

834

         /**

835

          * A specialized <code>BeanFactory</code> implementation that handles

836

          * configuration declarations. This class will retrieve the correct

837

          * configuration provider and delegate the task of creating the

838

          * configuration to this object.

839

          */

840

2

         static class ConfigurationBeanFactory implements BeanFactory

841

         {

842

             /**

843

              * Creates an instance of a bean class. This implementation expects that

844

              * the passed in bean declaration is a declaration for a configuration.

845

              * It will determine the responsible configuration provider and delegate

846

              * the call to this instance. If creation of the configuration fails

847

              * and the <code>optional</code> attribute is set, the exception will

848

              * be ignored and <b>null</b> will be returned.

849

              *

850

              * @param beanClass the bean class (will be ignored)

851

              * @param data the declaration

852

              * @param param an additional parameter (will be ignored)

853

              * @return the newly created configuration

854

              * @throws Exception if an error occurs

855

              */

856

             public Object createBean(Class beanClass, BeanDeclaration data,

857

                     Object param) throws Exception

858

             {

859

59

                 ConfigurationDeclaration decl = (ConfigurationDeclaration) data;

860

59

                 String tagName = decl.getNode().getName();

861

59

                 ConfigurationProvider provider = decl.getConfigurationBuilder()

862

                         .providerForTag(tagName);

863

59

                 if (provider == null)

864

                 {

865

1

                     throw new ConfigurationRuntimeException(

866

                             "No ConfigurationProvider registered for tag "

867

                                     + tagName);

868

                 }

869

870

                 try

871

                 {

872

58

                     return provider.getConfiguration(decl);

873

                 }

874

                 catch (Exception ex)

875

                 {

876

                     // If this is an optional configuration, ignore the exception

877

9

                     if (!decl.isOptional())

878

                     {

879

1

                         throw ex;

880

                     }

881

                     else

882

                     {

883

8

                         return null;

884

                     }

885

                 }

886

             }

887

888

             /**

889

              * Returns the default class for this bean factory.

890

              *

891

              * @return the default class

892

              */

893

             public Class getDefaultBeanClass()

894

             {

895

                 // Here some valid class must be returned, otherwise BeanHelper

896

                 // will complain that the bean's class cannot be determined

897

59

                 return Configuration.class;

898

             }

899

         }

900

901

         /**

902

          * A specialized provider implementation that deals with file based

903

          * configurations. Ensures that the base path is correctly set and that the

904

          * load() method gets called.

905

          */

906

         public static class FileConfigurationProvider extends ConfigurationProvider

907

         {

908

             /**

909

              * Creates a new instance of <code>FileConfigurationProvider</code>.

910

              */

911

             public FileConfigurationProvider()

912

             {

913

4

                 super();

914

4

             }

915

916

             /**

917

              * Creates a new instance of <code>FileConfigurationProvider</code>

918

              * and sets the configuration class.

919

              *

920

              * @param configClass the class for the configurations to be created

921

              */

922

             public FileConfigurationProvider(Class configClass)

923

             {

924

2

                 super(configClass);

925

2

             }

926

927

             /**

928

              * Creates the configuration. After that <code>load()</code> will be

929

              * called. If this configuration is marked as optional, exceptions will

930

              * be ignored.

931

              *

932

              * @param decl the declaration

933

              * @return the new configuration

934

              * @throws Exception if an error occurs

935

              */

936

             public AbstractConfiguration getConfiguration(

937

                     ConfigurationDeclaration decl) throws Exception

938

             {

939

50

                 FileConfiguration config = (FileConfiguration) super

940

                         .getConfiguration(decl);

941

50

                 config.load();

942

42

                 return (AbstractConfiguration) config;

943

             }

944

945

             /**

946

              * Initializes the bean instance. Ensures that the file configuration's

947

              * base path will be initialized with the base path of the factory so

948

              * that relative path names can be correctly resolved.

949

              *

950

              * @param bean the bean to be initialized

951

              * @param data the declaration

952

              * @throws Exception if an error occurs

953

              */

954

             protected void initBeanInstance(Object bean, BeanDeclaration data)

955

                     throws Exception

956

             {

957

50

                 FileConfiguration config = (FileConfiguration) bean;

958

50

                 config.setBasePath(((ConfigurationDeclaration) data)

959

                         .getConfigurationBuilder().getConfigurationBasePath());

960

50

                 super.initBeanInstance(bean, data);

961

50

             }

962

         }

963

964

         /**

965

          * A specialized configuration provider for file based configurations that

966

          * can handle configuration sources whose concrete type depends on the

967

          * extension of the file to be loaded. One example is the

968

          * <code>properties</code> tag: if the file ends with ".xml" a

969

          * XMLPropertiesConfiguration object must be created, otherwise a

970

          * PropertiesConfiguration object.

971

          */

972

2

         static class FileExtensionConfigurationProvider extends

973

                 FileConfigurationProvider

974

         {

975

             /** Stores the class to be created when the file extension matches. */

976

             private Class matchingClass;

977

978

             /**

979

              * Stores the class to be created when the file extension does not

980

              * match.

981

              */

982

             private Class defaultClass;

983

984

             /** Stores the file extension to be checked against. */

985

             private String fileExtension;

986

987

             /**

988

              * Creates a new instance of

989

              * <code>FileExtensionConfigurationProvider</code> and initializes it.

990

              *

991

              * @param matchingClass the class to be created when the file extension

992

              * matches

993

              * @param defaultClass the class to be created when the file extension

994

              * does not match

995

              * @param extension the file extension to be checked agains

996

              */

997

             public FileExtensionConfigurationProvider(Class matchingClass,

998

                     Class defaultClass, String extension)

999

4

             {

1000

4

                 this.matchingClass = matchingClass;

1001

4

                 this.defaultClass = defaultClass;

1002

4

                 fileExtension = extension;

1003

4

             }

1004

1005

             /**

1006

              * Creates the configuration object. The class is determined by the file

1007

              * name's extension.

1008

              *

1009

              * @param beanClass the class

1010

              * @param data the bean declaration

1011

              * @return the new bean

1012

              * @throws Exception if an error occurs

1013

              */

1014

             protected Object createBeanInstance(Class beanClass,

1015

                     BeanDeclaration data) throws Exception

1016

             {

1017

26

                 String fileName = ((ConfigurationDeclaration) data)

1018

                         .getConfiguration().getString(ATTR_FILENAME);

1019

26

                 if (fileName != null

1020

                         && fileName.toLowerCase().trim().endsWith(fileExtension))

1021

                 {

1022

6

                     return super.createBeanInstance(matchingClass, data);

1023

                 }

1024

                 else

1025

                 {

1026

20

                     return super.createBeanInstance(defaultClass, data);

1027

                 }

1028

             }

1029

         }

1030

1031

         /**

1032

          * A specialized configuration provider class that allows to include other

1033

          * configuration definition files.

1034

          */

1035

         static class ConfigurationBuilderProvider extends ConfigurationProvider

1036

         {

1037

             /**

1038

              * Creates a new instance of <code>ConfigurationBuilderProvider</code>.

1039

              */

1040

             public ConfigurationBuilderProvider()

1041

             {

1042

2

                 super(DefaultConfigurationBuilder.class);

1043

2

             }

1044

1045

             /**

1046

              * Creates the configuration. First creates a configuration builder

1047

              * object. Then returns the configuration created by this builder.

1048

              *

1049

              * @param decl the configuration declaration

1050

              * @return the configuration

1051

              * @exception Exception if an error occurs

1052

              */

1053

             public AbstractConfiguration getConfiguration(

1054

                     ConfigurationDeclaration decl) throws Exception

1055

             {

1056

2

                 DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super

1057

                         .getConfiguration(decl);

1058

2

                 return builder.getConfiguration(true);

1059

             }

1060

         }

1061

1062

         static

1063

         {

1064

             // register the configuration bean factory

1065

2

             BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,

1066

                     new ConfigurationBeanFactory());

1067

         }

1068

     }