    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 */
    017package org.apache.commons.configuration2.builder;
    018
    019import java.util.Collection;
    020import java.util.Collections;
    021import java.util.HashMap;
    022import java.util.Map;
    023
    024import org.apache.commons.configuration2.ConfigurationUtils;
    025import org.apache.commons.configuration2.ImmutableConfiguration;
    026import org.apache.commons.configuration2.Initializable;
    027import org.apache.commons.configuration2.beanutils.BeanDeclaration;
    028import org.apache.commons.configuration2.beanutils.BeanHelper;
    029import org.apache.commons.configuration2.beanutils.ConstructorArg;
    030import org.apache.commons.configuration2.event.Event;
    031import org.apache.commons.configuration2.event.EventListener;
    032import org.apache.commons.configuration2.event.EventListenerList;
    033import org.apache.commons.configuration2.event.EventListenerRegistrationData;
    034import org.apache.commons.configuration2.event.EventSource;
    035import org.apache.commons.configuration2.event.EventType;
    036import org.apache.commons.configuration2.ex.ConfigurationException;
    037import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
    038import org.apache.commons.configuration2.reloading.ReloadingController;
    039
    040/**
    041 * <p>
    042 * An implementation of the {@code ConfigurationBuilder} interface which is able to create different concrete
    043 * {@code ImmutableConfiguration} implementations based on reflection.
    044 * </p>
    045 * <p>
    046 * When constructing an instance of this class the concrete {@code ImmutableConfiguration} implementation class has to
    047 * be provided. Then properties for the new {@code ImmutableConfiguration} instance can be set. The first call to
    048 * {@code getConfiguration()} creates and initializes the new {@code ImmutableConfiguration} object. It is cached and
    049 * returned by subsequent calls. This cache - and also the initialization properties set so far - can be flushed by
    050 * calling one of the {@code reset()} methods. That way other {@code ImmutableConfiguration} instances with different
    051 * properties can be created.
    052 * </p>
    053 * <p>
    054 * If the newly created {@code ImmutableConfiguration} object implements the {@code Initializable} interface, its
    055 * {@code initialize()} method is called after all initialization properties have been set. This way a concrete
    056 * implementation class can perform arbitrary initialization steps.
    057 * </p>
    058 * <p>
    059 * There are multiple options for setting up a {@code BasicConfigurationBuilder} instance:
    060 * </p>
    061 * <ul>
    062 * <li>All initialization properties can be set in one or multiple calls of the {@code configure()} method. In each call
    063 * an arbitrary number of {@link BuilderParameters} objects can be passed. The API allows method chaining and is
    064 * intended to be used from Java code.</li>
    065 * <li>If builder instances are created by other means - e.g. using a dependency injection framework -, the fluent API
    066 * approach may not be suitable. For those use cases it is also possible to pass in all initialization parameters as a
    067 * map. The keys of the map have to match initialization properties of the {@code ImmutableConfiguration} object to be
    068 * created, the values are the corresponding property values. For instance, the key <em>throwExceptionOnMissing</em> in
    069 * the map will cause the method {@code setThrowExceptionOnMissing()} on the {@code ImmutableConfiguration} object to be
    070 * called with the corresponding value as parameter.</li>
    071 * </ul>
    072 * <p>
    073 * A builder instance can be constructed with an <em>allowFailOnInit</em> flag. If set to <strong>true</strong>,
    074 * exceptions during initialization of the configuration are ignored; in such a case an empty configuration object is
    075 * returned. A use case for this flag is a scenario in which a configuration is optional and created on demand the first
    076 * time configuration data is to be stored. Consider an application that stores user-specific configuration data in the
    077 * user's home directory: When started for the first time by a new user there is no configuration file; so it makes
    078 * sense to start with an empty configuration object. On application exit, settings can be stored in this object and
    079 * written to the associated file. Then they are available on next application start.
    080 * </p>
    081 * <p>
    082 * This class is thread-safe. Multiple threads can modify initialization properties and call {@code getConfiguration()}.
    083 * However, the intended use case is that the builder is configured by a single thread first. Then
    084 * {@code getConfiguration()} can be called concurrently, and it is guaranteed that always the same
    085 * {@code ImmutableConfiguration} instance is returned until the builder is reset.
    086 * </p>
    087 *
    088 * @since 2.0
    089 * @param <T> the concrete type of {@code ImmutableConfiguration} objects created by this builder
    090 */
    091public class BasicConfigurationBuilder<T extends ImmutableConfiguration> implements ConfigurationBuilder<T> {
    092    /** The class of the objects produced by this builder instance. */
    093    private final Class<? extends T> resultClass;
    094
    095    /** An object managing the event listeners registered at this builder. */
    096    private final EventListenerList eventListeners;
    097
    098    /** A flag whether exceptions on initializing configurations are allowed. */
    099    private final boolean allowFailOnInit;
    100
    101    /** The map with current initialization parameters. */
    102    private Map<String, Object> parameters;
    103
    104    /** The current bean declaration. */
    105    private BeanDeclaration resultDeclaration;
    106
    107    /** The result object of this builder. */
    108    private volatile T result;
    109
    110    /**
    111     * Creates a new instance of {@code BasicConfigurationBuilder} and initializes it with the given result class. No
    112     * initialization properties are set.
    113     *
    114     * @param resCls the result class (must not be <b>null</b>)
    115     * @throws IllegalArgumentException if the result class is <b>null</b>
    116     */
    117    public BasicConfigurationBuilder(final Class<? extends T> resCls) {
    118        this(resCls, null);
    119    }
    120
    121    /**
    122     * Creates a new instance of {@code BasicConfigurationBuilder} and initializes it with the given result class and an
    123     * initial set of builder parameters. The <em>allowFailOnInit</em> flag is set to <strong>false</strong>.
    124     *
    125     * @param resCls the result class (must not be <b>null</b>)
    126     * @param params a map with initialization parameters
    127     * @throws IllegalArgumentException if the result class is <b>null</b>
    128     */
    129    public BasicConfigurationBuilder(final Class<? extends T> resCls, final Map<String, Object> params) {
    130        this(resCls, params, false);
    131    }
    132
    133    /**
    134     * Creates a new instance of {@code BasicConfigurationBuilder} and initializes it with the given result class, an
    135     * initial set of builder parameters, and the <em>allowFailOnInit</em> flag. The map with parameters may be <b>null</b>,
    136     * in this case no initialization parameters are set.
    137     *
    138     * @param resCls the result class (must not be <b>null</b>)
    139     * @param params a map with initialization parameters
    140     * @param allowFailOnInit a flag whether exceptions on initializing a newly created {@code ImmutableConfiguration}
    141     *        object are allowed
    142     * @throws IllegalArgumentException if the result class is <b>null</b>
    143     */
    144    public BasicConfigurationBuilder(final Class<? extends T> resCls, final Map<String, Object> params, final boolean allowFailOnInit) {
    145        if (resCls == null) {
    146            throw new IllegalArgumentException("Result class must not be null!");
    147        }
    148
    149        resultClass = resCls;
    150        this.allowFailOnInit = allowFailOnInit;
    151        eventListeners = new EventListenerList();
    152        updateParameters(params);
    153    }
    154
    155    /**
    156     * Returns the result class of this builder. The objects produced by this builder have the class returned here.
    157     *
    158     * @return the result class of this builder
    159     */
    160    public Class<? extends T> getResultClass() {
    161        return resultClass;
    162    }
    163
    164    /**
    165     * Returns the <em>allowFailOnInit</em> flag. See the header comment for information about this flag.
    166     *
    167     * @return the <em>allowFailOnInit</em> flag
    168     */
    169    public boolean isAllowFailOnInit() {
    170        return allowFailOnInit;
    171    }
    172
    173    /**
    174     * Sets the initialization parameters of this builder. Already existing parameters are replaced by the content of the
    175     * given map.
    176     *
    177     * @param params the new initialization parameters of this builder; can be <b>null</b>, then all initialization
    178     *        parameters are removed
    179     * @return a reference to this builder for method chaining
    180     */
    181    public synchronized BasicConfigurationBuilder<T> setParameters(final Map<String, Object> params) {
    182        updateParameters(params);
    183        return this;
    184    }
    185
    186    /**
    187     * Adds the content of the given map to the already existing initialization parameters.
    188     *
    189     * @param params the map with additional initialization parameters; may be <b>null</b>, then this call has no effect
    190     * @return a reference to this builder for method chaining
    191     */
    192    public synchronized BasicConfigurationBuilder<T> addParameters(final Map<String, Object> params) {
    193        final Map<String, Object> newParams = new HashMap<>(getParameters());
    194        if (params != null) {
    195            newParams.putAll(params);
    196        }
    197        updateParameters(newParams);
    198        return this;
    199    }
    200
    201    /**
    202     * Appends the content of the specified {@code BuilderParameters} objects to the current initialization parameters.
    203     * Calling this method multiple times will create a union of the parameters provided.
    204     *
    205     * @param params an arbitrary number of objects with builder parameters
    206     * @return a reference to this builder for method chaining
    207     * @throws NullPointerException if a <b>null</b> array is passed
    208     */
    209    public BasicConfigurationBuilder<T> configure(final BuilderParameters... params) {
    210        final Map<String, Object> newParams = new HashMap<>();
    211        for (final BuilderParameters p : params) {
    212            newParams.putAll(p.getParameters());
    213            handleEventListenerProviders(p);
    214        }
    215
    216        return setParameters(newParams);
    217    }
    218
    219    /**
    220     * {@inheritDoc} This implementation creates the result configuration on first access. Later invocations return the same
    221     * object until this builder is reset. The double-check idiom for lazy initialization is used (Bloch, Effective Java,
    222     * item 71).
    223     */
    224    @Override
    225    public T getConfiguration() throws ConfigurationException {
    226        fireBuilderEvent(new ConfigurationBuilderEvent(this, ConfigurationBuilderEvent.CONFIGURATION_REQUEST));
    227
    228        T resObj = result;
    229        boolean created = false;
    230        if (resObj == null) {
    231            synchronized (this) {
    232                resObj = result;
    233                if (resObj == null) {
    234                    result = resObj = createResult();
    235                    created = true;
    236                }
    237            }
    238        }
    239
    240        if (created) {
    241            fireBuilderEvent(new ConfigurationBuilderResultCreatedEvent(this, ConfigurationBuilderResultCreatedEvent.RESULT_CREATED, resObj));
    242        }
    243        return resObj;
    244    }
    245
    246    /**
    247     * {@inheritDoc} This implementation also takes care that the event listener is added to the managed configuration
    248     * object.
    249     *
    250     * @throws IllegalArgumentException if the event type or the listener is <b>null</b>
    251     */
    252    @Override
    253    public <E extends Event> void addEventListener(final EventType<E> eventType, final EventListener<? super E> listener) {
    254        installEventListener(eventType, listener);
    255    }
    256
    257    /**
    258     * {@inheritDoc} This implementation also takes care that the event listener is removed from the managed configuration
    259     * object.
    260     */
    261    @Override
    262    public <E extends Event> boolean removeEventListener(final EventType<E> eventType, final EventListener<? super E> listener) {
    263        fetchEventSource().removeEventListener(eventType, listener);
    264        return eventListeners.removeEventListener(eventType, listener);
    265    }
    266
    267    /**
    268     * Clears an existing result object. An invocation of this method causes a new {@code ImmutableConfiguration} object to
    269     * be created the next time {@link #getConfiguration()} is called.
    270     */
    271    public void resetResult() {
    272        final T oldResult;
    273        synchronized (this) {
    274            oldResult = result;
    275            result = null;
    276            resultDeclaration = null;
    277        }
    278
    279        if (oldResult != null) {
    280            removeEventListeners(oldResult);
    281        }
    282        fireBuilderEvent(new ConfigurationBuilderEvent(this, ConfigurationBuilderEvent.RESET));
    283    }
    284
    285    /**
    286     * Removes all initialization parameters of this builder. This method can be called if this builder is to be reused for
    287     * creating result objects with a different configuration.
    288     */
    289    public void resetParameters() {
    290        setParameters(null);
    291    }
    292
    293    /**
    294     * Resets this builder. This is a convenience method which combines calls to {@link #resetResult()} and
    295     * {@link #resetParameters()}.
    296     */
    297    public synchronized void reset() {
    298        resetParameters();
    299        resetResult();
    300    }
    301
    302    /**
    303     * Connects this builder with a {@code ReloadingController}. With this method support for reloading can be added to an
    304     * arbitrary builder object. Event listeners are registered at the reloading controller and this builder with connect
    305     * both objects:
    306     * <ul>
    307     * <li>When the reloading controller detects that a reload is required, the builder's {@link #resetResult()} method is
    308     * called; so the managed result object is invalidated.</li>
    309     * <li>When a new result object has been created the controller's reloading state is reset, so that new changes can be
    310     * detected again.</li>
    311     * </ul>
    312     *
    313     * @param controller the {@code ReloadingController} to connect to (must not be <b>null</b>)
    314     * @throws IllegalArgumentException if the controller is <b>null</b>
    315     */
    316    public final void connectToReloadingController(final ReloadingController controller) {
    317        if (controller == null) {
    318            throw new IllegalArgumentException("ReloadingController must not be null!");
    319        }
    320        ReloadingBuilderSupportListener.connect(this, controller);
    321    }
    322
    323    /**
    324     * Creates a new, initialized result object. This method is called by {@code getConfiguration()} if no valid result
    325     * object exists. This base implementation performs two steps:
    326     * <ul>
    327     * <li>{@code createResultInstance()} is called to create a new, uninitialized result object.</li>
    328     * <li>{@code initResultInstance()} is called to process all initialization parameters.</li>
    329     * </ul>
    330     * It also evaluates the <em>allowFailOnInit</em> flag, i.e. if initialization causes an exception and this flag is set,
    331     * the exception is ignored, and the newly created, uninitialized configuration is returned. Note that this method is
    332     * called in a synchronized block.
    333     *
    334     * @return the newly created result object
    335     * @throws ConfigurationException if an error occurs
    336     */
    337    protected T createResult() throws ConfigurationException {
    338        final T resObj = createResultInstance();
    339
    340        try {
    341            initResultInstance(resObj);
    342        } catch (final ConfigurationException cex) {
    343            if (!isAllowFailOnInit()) {
    344                throw cex;
    345            }
    346        }
    347
    348        return resObj;
    349    }
    350
    351    /**
    352     * Creates the new, uninitialized result object. This is the first step of the process of producing a result object for
    353     * this builder. This implementation uses the {@link BeanHelper} class to create a new object based on the
    354     * {@link BeanDeclaration} returned by {@link #getResultDeclaration()}. Note: This method is invoked in a synchronized
    355     * block.
    356     *
    357     * @return the newly created, yet uninitialized result object
    358     * @throws ConfigurationException if an exception occurs
    359     */
    360    protected T createResultInstance() throws ConfigurationException {
    361        final Object bean = fetchBeanHelper().createBean(getResultDeclaration());
    362        checkResultInstance(bean);
    363        return getResultClass().cast(bean);
    364    }
    365
    366    /**
    367     * Initializes a newly created result object. This is the second step of the process of producing a result object for
    368     * this builder. This implementation uses the {@link BeanHelper} class to initialize the object's property based on the
    369     * {@link BeanDeclaration} returned by {@link #getResultDeclaration()}. Note: This method is invoked in a synchronized
    370     * block. This is required because internal state is accessed. Sub classes must not call this method without proper
    371     * synchronization.
    372     *
    373     * @param obj the object to be initialized
    374     * @throws ConfigurationException if an error occurs
    375     */
    376    protected void initResultInstance(final T obj) throws ConfigurationException {
    377        fetchBeanHelper().initBean(obj, getResultDeclaration());
    378        registerEventListeners(obj);
    379        handleInitializable(obj);
    380    }
    381
    382    /**
    383     * Returns the {@code BeanDeclaration} that is used to create and initialize result objects. The declaration is created
    384     * on first access (by invoking {@link #createResultDeclaration(Map)}) based on the current initialization parameters.
    385     *
    386     * @return the {@code BeanDeclaration} for dynamically creating a result object
    387     * @throws ConfigurationException if an error occurs
    388     */
    389    protected final synchronized BeanDeclaration getResultDeclaration() throws ConfigurationException {
    390        if (resultDeclaration == null) {
    391            resultDeclaration = createResultDeclaration(getFilteredParameters());
    392        }
    393        return resultDeclaration;
    394    }
    395
    396    /**
    397     * Returns a (unmodifiable) map with the current initialization parameters set for this builder. The map is populated
    398     * with the parameters set using the various configuration options.
    399     *
    400     * @return a map with the current set of initialization parameters
    401     */
    402    protected final synchronized Map<String, Object> getParameters() {
    403        if (parameters != null) {
    404            return parameters;
    405        }
    406        return Collections.emptyMap();
    407    }
    408
    409    /**
    410     * Obtains the {@code BeanHelper} object to be used when dealing with bean declarations. This method checks whether this
    411     * builder was configured with a specific {@code BeanHelper} instance. If so, this instance is used. Otherwise, the
    412     * default {@code BeanHelper} is returned.
    413     *
    414     * @return the {@code BeanHelper} to be used
    415     */
    416    protected final BeanHelper fetchBeanHelper() {
    417        final BeanHelper helper = BasicBuilderParameters.fetchBeanHelper(getParameters());
    418        return helper != null ? helper : BeanHelper.INSTANCE;
    419    }
    420
    421    /**
    422     * Creates a new {@code BeanDeclaration} which is used for creating new result objects dynamically. This implementation
    423     * creates a specialized {@code BeanDeclaration} object that is initialized from the given map of initialization
    424     * parameters. The {@code BeanDeclaration} must be initialized with the result class of this builder, otherwise
    425     * exceptions will be thrown when the result object is created. Note: This method is invoked in a synchronized block.
    426     *
    427     * @param params a snapshot of the current initialization parameters
    428     * @return the {@code BeanDeclaration} for creating result objects
    429     * @throws ConfigurationException if an error occurs
    430     */
    431    protected BeanDeclaration createResultDeclaration(final Map<String, Object> params) throws ConfigurationException {
    432        return new BeanDeclaration() {
    433            @Override
    434            public Map<String, Object> getNestedBeanDeclarations() {
    435                // no nested beans
    436                return Collections.emptyMap();
    437            }
    438
    439            @Override
    440            public Collection<ConstructorArg> getConstructorArgs() {
    441                // no constructor arguments
    442                return Collections.emptySet();
    443            }
    444
    445            @Override
    446            public Map<String, Object> getBeanProperties() {
    447                // the properties are equivalent to the parameters
    448                return params;
    449            }
    450
    451            @Override
    452            public Object getBeanFactoryParameter() {
    453                return null;
    454            }
    455
    456            @Override
    457            public String getBeanFactoryName() {
    458                return null;
    459            }
    460
    461            @Override
    462            public String getBeanClassName() {
    463                return getResultClass().getName();
    464            }
    465        };
    466    }
    467
    468    /**
    469     * Copies all {@code EventListener} objects registered at this builder to the specified target configuration builder.
    470     * This method is intended to be used by derived classes which support inheritance of their properties to other builder
    471     * objects.
    472     *
    473     * @param target the target configuration builder (must not be <b>null</b>)
    474     * @throws NullPointerException if the target builder is <b>null</b>
    475     */
    476    protected synchronized void copyEventListeners(final BasicConfigurationBuilder<?> target) {
    477        copyEventListeners(target, eventListeners);
    478    }
    479
    480    /**
    481     * Copies all event listeners in the specified list to the specified target configuration builder. This method is
    482     * intended to be used by derived classes which have to deal with managed configuration builders that need to be
    483     * initialized with event listeners.
    484     *
    485     * @param target the target configuration builder (must not be <b>null</b>)
    486     * @param listeners the event listeners to be copied over
    487     * @throws NullPointerException if the target builder is <b>null</b>
    488     */
    489    protected void copyEventListeners(final BasicConfigurationBuilder<?> target, final EventListenerList listeners) {
    490        target.eventListeners.addAll(listeners);
    491    }
    492
    493    /**
    494     * Adds the specified event listener to this object. This method is called by {@code addEventListener()}, it does the
    495     * actual listener registration. Because it is final it can be called by sub classes in the constructor if there is
    496     * already the need to register an event listener.
    497     *
    498     * @param eventType the event type object
    499     * @param listener the listener to be registered
    500     * @param <E> the event type
    501     */
    502    protected final <E extends Event> void installEventListener(final EventType<E> eventType, final EventListener<? super E> listener) {
    503        fetchEventSource().addEventListener(eventType, listener);
    504        eventListeners.addEventListener(eventType, listener);
    505    }
    506
    507    /**
    508     * Sends the specified builder event to all registered listeners.
    509     *
    510     * @param event the event to be fired
    511     */
    512    protected void fireBuilderEvent(final ConfigurationBuilderEvent event) {
    513        eventListeners.fire(event);
    514    }
    515
    516    /**
    517     * Replaces the current map with parameters by a new one.
    518     *
    519     * @param newParams the map with new parameters (may be <b>null</b>)
    520     */
    521    private void updateParameters(final Map<String, Object> newParams) {
    522        final Map<String, Object> map = new HashMap<>();
    523        if (newParams != null) {
    524            map.putAll(newParams);
    525        }
    526        parameters = Collections.unmodifiableMap(map);
    527    }
    528
    529    /**
    530     * Registers the available event listeners at the given object. This method is called for each result object created by
    531     * the builder.
    532     *
    533     * @param obj the object to initialize
    534     */
    535    private void registerEventListeners(final T obj) {
    536        final EventSource evSrc = ConfigurationUtils.asEventSource(obj, true);
    537        for (final EventListenerRegistrationData<?> regData : eventListeners.getRegistrations()) {
    538            registerListener(evSrc, regData);
    539        }
    540    }
    541
    542    /**
    543     * Removes all available event listeners from the given result object. This method is called when the result of this
    544     * builder is reset. Then the old managed configuration should no longer generate events.
    545     *
    546     * @param obj the affected result object
    547     */
    548    private void removeEventListeners(final T obj) {
    549        final EventSource evSrc = ConfigurationUtils.asEventSource(obj, true);
    550        for (final EventListenerRegistrationData<?> regData : eventListeners.getRegistrations()) {
    551            removeListener(evSrc, regData);
    552        }
    553    }
    554
    555    /**
    556     * Returns an {@code EventSource} for the current result object. If there is no current result or if it does not extend
    557     * {@code EventSource}, a dummy event source is returned.
    558     *
    559     * @return the {@code EventSource} for the current result object
    560     */
    561    private EventSource fetchEventSource() {
    562        return ConfigurationUtils.asEventSource(result, true);
    563    }
    564
    565    /**
    566     * Checks whether the specified parameters object implements the {@code EventListenerProvider} interface. If so, the
    567     * event listeners it provides are added to this builder.
    568     *
    569     * @param params the parameters object
    570     */
    571    private void handleEventListenerProviders(final BuilderParameters params) {
    572        if (params instanceof EventListenerProvider) {
    573            eventListeners.addAll(((EventListenerProvider) params).getListeners());
    574        }
    575    }
    576
    577    /**
    578     * Checks whether the class of the result configuration is compatible with this builder's result class. This is done to
    579     * ensure that only objects of the expected result class are created.
    580     *
    581     * @param inst the result instance to be checked
    582     * @throws ConfigurationRuntimeException if an invalid result class is detected
    583     */
    584    private void checkResultInstance(final Object inst) {
    585        if (!getResultClass().isInstance(inst)) {
    586            throw new ConfigurationRuntimeException("Incompatible result object: " + inst);
    587        }
    588    }
    589
    590    /**
    591     * Returns a map with initialization parameters where all parameters starting with the reserved prefix have been
    592     * filtered out.
    593     *
    594     * @return the filtered parameters map
    595     */
    596    private Map<String, Object> getFilteredParameters() {
    597        final Map<String, Object> filteredMap = new HashMap<>(getParameters());
    598        filteredMap.keySet().removeIf(key -> key.startsWith(BuilderParameters.RESERVED_PARAMETER_PREFIX));
    599        return filteredMap;
    600    }
    601
    602    /**
    603     * Performs special initialization of the result object. This method is called after parameters have been set on a newly
    604     * created result instance. If supported by the result class, the {@code initialize()} method is now called.
    605     *
    606     * @param obj the newly created result object
    607     */
    608    private void handleInitializable(final T obj) {
    609        if (obj instanceof Initializable) {
    610            ((Initializable) obj).initialize();
    611        }
    612    }
    613
    614    /**
    615     * Registers an event listener at an event source object.
    616     *
    617     * @param evSrc the event source
    618     * @param regData the registration data object
    619     * @param <E> the type of the event listener
    620     */
    621    private static <E extends Event> void registerListener(final EventSource evSrc, final EventListenerRegistrationData<E> regData) {
    622        evSrc.addEventListener(regData.getEventType(), regData.getListener());
    623    }
    624
    625    /**
    626     * Removes an event listener from an event source object.
    627     *
    628     * @param evSrc the event source
    629     * @param regData the registration data object
    630     * @param <E> the type of the event listener
    631     */
    632    private static <E extends Event> void removeListener(final EventSource evSrc, final EventListenerRegistrationData<E> regData) {
    633        evSrc.removeEventListener(regData.getEventType(), regData.getListener());
    634    }
    635}