001/*
002 * GWTEventService
003 * Copyright (c) 2011 and beyond, strawbill UG (haftungsbeschränkt)
004 *
005 * This is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU Lesser General Public License as
007 * published by the Free Software Foundation; either version 3 of
008 * the License, or (at your option) any later version.
009 * Other licensing for GWTEventService may also be possible on request.
010 * Please view the license.txt of the project for more information.
011 *
012 * This software is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this software; if not, write to the Free
019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021 */
022package de.novanic.eventservice.config;
023
024import de.novanic.eventservice.client.config.ConfigurationException;
025import de.novanic.eventservice.config.loader.ConfigurationLoader;
026import de.novanic.eventservice.config.loader.PropertyConfigurationLoader;
027import de.novanic.eventservice.config.loader.DefaultConfigurationLoader;
028import de.novanic.eventservice.config.level.ConfigLevel;
029import de.novanic.eventservice.config.level.ConfigLevelFactory;
030
031import java.util.*;
032import java.util.concurrent.ConcurrentLinkedQueue;
033
034/**
035 * EventServiceConfigurationFactory can be used to create an instance of {@link EventServiceConfiguration}. There a
036 * various {@link de.novanic.eventservice.config.loader.ConfigurationLoader} strategies to initilize the configuration.
037 * {@link de.novanic.eventservice.config.loader.DefaultConfigurationLoader} is used at last, when no configuration could
038 * be found.
039 *
040 * <br><br>
041 * There are three pre-registered ConfigurationLoaders at various levels.
042 * <br> 1) Level {@link de.novanic.eventservice.config.level.ConfigLevelFactory#DEFAULT} (5000) - {@link de.novanic.eventservice.config.loader.PropertyConfigurationLoader}
043 * <br> 2) Level {@link de.novanic.eventservice.config.level.ConfigLevelFactory#DEFAULT} (5000) - {@link de.novanic.eventservice.config.loader.WebDescriptorConfigurationLoader}
044 * <br> 3) Level {@link de.novanic.eventservice.config.level.ConfigLevelFactory#HIGHEST} (10000) - {@link de.novanic.eventservice.config.loader.DefaultConfigurationLoader}
045 * <br>
046 * <br> That means when a property file is in the classpath, the property file is used for the configuration. When no property file is available,
047 * the web-descriptor is used. When no servlet-parameters are registered in the web-descriptor, the default configuration is used.
048 * To manipulate that sequence and to register custom configuration loaders {@link EventServiceConfigurationFactory#addConfigurationLoader(de.novanic.eventservice.config.level.ConfigLevel, de.novanic.eventservice.config.loader.ConfigurationLoader)}
049 * and other modifier methods of that class can be used. 
050 *
051 * @author sstrohschein
052 *         <br>Date: 23.10.2008
053 *         <br>Time: 14:36:49
054 */
055public class EventServiceConfigurationFactory
056{
057    private final Map<ConfigLevel, Queue<ConfigurationLoader>> myConfigurationLoaders;
058
059    /**
060     * The EventServiceConfigurationFactory should be created via the getInstance method.
061     * @see EventServiceConfigurationFactory#getInstance()
062     */
063    private EventServiceConfigurationFactory() {
064        myConfigurationLoaders = new TreeMap<ConfigLevel, Queue<ConfigurationLoader>>();
065        initConfigurationLoaders();
066    }
067
068    /**
069     * Factory-Holder class to ensure thread-safe lazy-loading with IODH.
070     */
071    private static class EventServiceConfigurationFactoryHolder {
072        private static EventServiceConfigurationFactory INSTANCE = new EventServiceConfigurationFactory();
073    }
074
075    /**
076     * This method should be used to create an instance of EventServiceConfigurationFactory.
077     * EventServiceConfigurationFactory is a singleton, so this method returns always the same instance of EventServiceConfigurationFactory.
078     * @return EventServiceConfigurationFactory (singleton)
079     */
080    public static EventServiceConfigurationFactory getInstance() {
081        return EventServiceConfigurationFactoryHolder.INSTANCE;
082    }
083
084    /**
085     * Loads the {@link de.novanic.eventservice.config.EventServiceConfiguration} with various
086     * {@link de.novanic.eventservice.config.loader.ConfigurationLoader} strategies.
087     * @param aPropertyName properties file if another properties file is preferred as the default properties file"
088     * (see description of {@link de.novanic.eventservice.config.loader.PropertyConfigurationLoader}).
089     * The returned configurations get enriched with default values when the parameters / options aren't configured.
090     * @return the configuration ({@link de.novanic.eventservice.config.EventServiceConfiguration})
091     * @throws ConfigurationException thrown when a configuration is available, but can't be loaded
092     */
093    public EventServiceConfiguration loadEventServiceConfiguration(String aPropertyName) {
094        replaceConfigurationLoader(ConfigLevelFactory.DEFAULT, new PropertyConfigurationLoader(aPropertyName));
095        initConfigurationLoaders();
096        return loadEventServiceConfiguration();
097    }
098
099    /**
100     * Loads the {@link de.novanic.eventservice.config.EventServiceConfiguration} with various
101     * {@link de.novanic.eventservice.config.loader.ConfigurationLoader} strategies. The returned configurations
102     * get enriched with default values when the parameters / options aren't configured.
103     * @return the configuration ({@link de.novanic.eventservice.config.EventServiceConfiguration})
104     * @throws de.novanic.eventservice.client.config.ConfigurationException thrown when a configuration is available, but can't be loaded
105     */
106    public EventServiceConfiguration loadEventServiceConfiguration() {
107        for(Queue<ConfigurationLoader> theConfigLoaders: myConfigurationLoaders.values()) {
108            for(ConfigurationLoader theConfigLoader: theConfigLoaders) {
109                if(theConfigLoader.isAvailable()) {
110                    return enrich(theConfigLoader.load());
111                }
112            }
113        }
114        //can not occur, because the DefaultConfigurationLoader is attached and always available
115        throw new ConfigurationException("No configuration is available!");
116    }
117
118    /**
119     * Adds a custom {@link de.novanic.eventservice.config.loader.ConfigurationLoader} (in the queue before the default
120     * configuration loaders).
121     * @param aConfigurationLoader custom {@link de.novanic.eventservice.config.loader.ConfigurationLoader}
122     */
123    public void addCustomConfigurationLoader(ConfigurationLoader aConfigurationLoader) {
124        addConfigurationLoader(ConfigLevelFactory.LOWEST, aConfigurationLoader);
125    }
126
127    /**
128     * Adds a {@link de.novanic.eventservice.config.loader.ConfigurationLoader} (in the queue before the default
129     * configuration loaders).
130     * @param aLevel {@link de.novanic.eventservice.config.level.ConfigLevel} to specify the priority/level for the {@link de.novanic.eventservice.config.loader.ConfigurationLoader}
131     * @param aConfigurationLoader custom {@link de.novanic.eventservice.config.loader.ConfigurationLoader}
132     */
133    public void addConfigurationLoader(ConfigLevel aLevel, ConfigurationLoader aConfigurationLoader) {
134        Queue<ConfigurationLoader> theConfigLoaders = myConfigurationLoaders.get(aLevel);
135        if(theConfigLoaders == null) {
136            theConfigLoaders = new ConcurrentLinkedQueue<ConfigurationLoader>();
137            myConfigurationLoaders.put(aLevel, theConfigLoaders);
138        }
139        theConfigLoaders.add(aConfigurationLoader);
140    }
141
142    /**
143     * Removes a {@link de.novanic.eventservice.config.loader.ConfigurationLoader}.
144     * @param aConfigurationLoader {@link de.novanic.eventservice.config.loader.ConfigurationLoader} to remove from the queue
145     */
146    public void removeConfigurationLoader(ConfigurationLoader aConfigurationLoader) {
147        for(Queue<ConfigurationLoader> theConfigLoaders: myConfigurationLoaders.values()) {
148            theConfigLoaders.remove(aConfigurationLoader);
149        }
150    }
151
152    /**
153     * Replaces a configuration loader at the specified configuration level.
154     * @param aLevel configuration level to search the {@link de.novanic.eventservice.config.loader.ConfigurationLoader}
155     * @param aConfigurationLoader {@link de.novanic.eventservice.config.loader.ConfigurationLoader} to add
156     */
157    public void replaceConfigurationLoader(ConfigLevel aLevel, ConfigurationLoader aConfigurationLoader) {
158        removeConfigurationLoader(aConfigurationLoader);
159        addConfigurationLoader(aLevel, aConfigurationLoader);
160    }
161
162    /**
163     * Initializes and registers the pre-definied ConfigurationLoaders ({@link de.novanic.eventservice.config.loader.ConfigurationLoader}).
164     * See the class description of {@link de.novanic.eventservice.config.EventServiceConfigurationFactory} for more information. 
165     */
166    private void initConfigurationLoaders() {
167        replaceConfigurationLoader(ConfigLevelFactory.DEFAULT, new PropertyConfigurationLoader());
168        replaceConfigurationLoader(ConfigLevelFactory.HIGHEST, new DefaultConfigurationLoader());
169    }
170
171    /**
172     * The configuration will be enriched with default values, when no values are contained for the parameters.
173     * @param aConfiguration configuration to enrich
174     * @return the enriched configuration
175     */
176    private EventServiceConfiguration enrich(EventServiceConfiguration aConfiguration) {
177        final EventServiceConfiguration theDefaultConfiguration =  new DefaultConfigurationLoader().load();
178        final Map<ConfigParameter, Object> theDefaultConfigMap = theDefaultConfiguration.getConfigMap();
179
180        for(Map.Entry<ConfigParameter, Object> theConfigEntry: aConfiguration.getConfigMap().entrySet()) {
181            Object theValue = theConfigEntry.getValue();
182            if(theValue == null) {
183                theConfigEntry.setValue(theDefaultConfigMap.get(theConfigEntry.getKey()));
184            }
185        }
186        
187        return aConfiguration;
188    }
189}