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.loader;
023
024import de.novanic.eventservice.client.config.ConfigurationException;
025import de.novanic.eventservice.config.EventServiceConfiguration;
026import de.novanic.eventservice.config.RemoteEventServiceConfiguration;
027import de.novanic.eventservice.config.ConfigParameter;
028import de.novanic.eventservice.logger.ServerLogger;
029import de.novanic.eventservice.logger.ServerLoggerFactory;
030
031import java.util.Properties;
032import java.util.Scanner;
033import java.io.InputStream;
034import java.io.IOException;
035
036/**
037 * PropertyConfigurationLoader is used by {@link de.novanic.eventservice.config.EventServiceConfigurationFactory}
038 * to load the {@link de.novanic.eventservice.config.EventServiceConfiguration} with a properties file.
039 * The default name of the properties is "eventservice.properties". For that file will be searched if not other defined.
040 * 
041 * @author sstrohschein
042 *         <br>Date: 23.10.2008
043 *         <br>Time: 14:37:13
044 */
045public class PropertyConfigurationLoader implements ConfigurationLoader
046{
047    private static final ServerLogger LOG = ServerLoggerFactory.getServerLogger(PropertyConfigurationLoader.class.getName());
048    private static final String DEFAULT_PROPERTY_NAME = "eventservice.properties";
049
050    private final String myPropertyName;
051
052    /**
053     * Creates a {@link PropertyConfigurationLoader} with the default properties ("eventservice.properties").
054     */
055    public PropertyConfigurationLoader() {
056        this(null);
057    }
058
059    /**
060     * Creates a {@link PropertyConfigurationLoader} with a properties file.
061     * @param aPropertyName properties file to load (the location must be attached to the classpath)
062     */
063    public PropertyConfigurationLoader(String aPropertyName) {
064        if(aPropertyName != null) {
065            myPropertyName = aPropertyName;
066        } else {
067            myPropertyName = DEFAULT_PROPERTY_NAME;
068        }
069    }
070
071    /**
072     * Checks if the configuration is available and can be loaded. If no configuration is available, the load method
073     * {@link ConfigurationLoader#load()} shouldn't called. In the case of {@link PropertyConfigurationLoader} the method
074     * returns true when the location of the properties file is attached to the classpath.
075     * @return true when available, otherwise false
076     */
077    public boolean isAvailable() {
078        return getPropertiesStream() != null;
079    }
080
081    /**
082     * Loads the configuration with the loader.
083     * @return {@link de.novanic.eventservice.config.EventServiceConfiguration} the loaded configuration or NULL if the
084     * properties file couldn't found with the classpath.
085     * @throws ConfigurationException occurs when the configuration can't be loaded or if it contains unreadable values.
086     */
087    public EventServiceConfiguration load() {
088        InputStream thePropertiesInputStream = getPropertiesStream();
089        if(thePropertiesInputStream != null) {
090            try {
091                Properties theProperties = new Properties();
092                theProperties.load(thePropertiesInputStream);
093                return load(theProperties);
094            } catch(IOException e) {
095                throw new ConfigurationException("Error on loading \"" + myPropertyName + "\"!", e);
096            } finally {
097                try {
098                    thePropertiesInputStream.close();
099                } catch(IOException e) {
100                    LOG.error("Error on closing stream of \"" + myPropertyName + "\"!", e);
101                }
102            }
103        }
104        return null;
105    }
106
107    /**
108     * Returns the properties file as a stream ({@link java.io.InputStream}).
109     * @return properties file as a stream ({@link java.io.InputStream})
110     */
111    private InputStream getPropertiesStream() {
112        return Thread.currentThread().getContextClassLoader().getResourceAsStream(myPropertyName);
113    }
114
115    /**
116     * Loads the {@link de.novanic.eventservice.config.EventServiceConfiguration} with the values of the properties file.
117     * @param aProperties properties to initialize the {@link de.novanic.eventservice.config.EventServiceConfiguration}
118     * @return initialized {@link de.novanic.eventservice.config.EventServiceConfiguration}
119     * @throws ConfigurationException thrown when the values aren't parsable to an integer
120     */
121    private EventServiceConfiguration load(Properties aProperties) {
122        final Integer theMaxWaitingTime = getIntValue(getPropertyValue(aProperties, ConfigParameter.FQ_MAX_WAITING_TIME_TAG, ConfigParameter.MAX_WAITING_TIME_TAG));
123        final Integer theMinWaitingTime = getIntValue(getPropertyValue(aProperties, ConfigParameter.FQ_MIN_WAITING_TIME_TAG, ConfigParameter.MIN_WAITING_TIME_TAG));
124        final Integer theTimeoutTime = getIntValue(getPropertyValue(aProperties, ConfigParameter.FQ_TIMEOUT_TIME_TAG, ConfigParameter.TIMEOUT_TIME_TAG));
125        final Integer theReconnectAttemptCount = getIntValue(getPropertyValue(aProperties, ConfigParameter.FQ_RECONNECT_ATTEMPT_COUNT_TAG, ConfigParameter.RECONNECT_ATTEMPT_COUNT_TAG));
126        final String theConnectionIdGenerator = getPropertyValue(aProperties, ConfigParameter.FQ_CONNECTION_ID_GENERATOR, ConfigParameter.CONNECTION_ID_GENERATOR);
127        final String theConnectionStrategyClientConnector = getPropertyValue(aProperties, ConfigParameter.FQ_CONNECTION_STRATEGY_CLIENT_CONNECTOR, ConfigParameter.CONNECTION_STRATEGY_CLIENT_CONNECTOR);
128        final String theConnectionStrategyServerConnector = getPropertyValue(aProperties, ConfigParameter.FQ_CONNECTION_STRATEGY_SERVER_CONNECTOR, ConfigParameter.CONNECTION_STRATEGY_SERVER_CONNECTOR);
129        final String theConnectionStrategyEncoding = getPropertyValue(aProperties, ConfigParameter.FQ_CONNECTION_STRATEGY_ENCODING, ConfigParameter.CONNECTION_STRATEGY_ENCODING);
130        final Integer theMaxEvents = getIntValue(getPropertyValue(aProperties, ConfigParameter.FQ_MAX_EVENTS, ConfigParameter.MAX_EVENTS));
131
132        return new RemoteEventServiceConfiguration(getConfigDescription(), theMinWaitingTime, theMaxWaitingTime, theTimeoutTime,
133                theReconnectAttemptCount,
134                theConnectionIdGenerator, theConnectionStrategyClientConnector, theConnectionStrategyServerConnector, theConnectionStrategyEncoding,
135                theMaxEvents);
136    }
137
138    /**
139     * Parses the integer value from a {@link String}.
140     * @param aString {@link String} to parse the integer values
141     * @return integer value which is contained in the {@link String}
142     * @throws de.novanic.eventservice.client.config.ConfigurationException thrown when the {@link String} values isn't parsable to an integer value
143     */
144    private Integer getIntValue(String aString) {
145        if(aString != null) {
146            Scanner theScanner = new Scanner(aString);
147            if(theScanner.hasNextInt()) {
148                return theScanner.nextInt();
149            } else {
150                throw new ConfigurationException("Error on processing configuration \"" + myPropertyName + "\"! " +
151                        "The value \"" + aString + "\" couldn't parsed to an integer!");
152            }
153        }
154        return null;
155    }
156
157    /**
158     * Returns the description of the configuration (could for example contain the config file name).
159     * @return configuration description
160     */
161    private String getConfigDescription() {
162        StringBuilder theConfigDescriptionBuffer = new StringBuilder(15 + myPropertyName.length());
163        theConfigDescriptionBuffer.append("Properties \"");
164        theConfigDescriptionBuffer.append(myPropertyName);
165        theConfigDescriptionBuffer.append('\"');
166        return theConfigDescriptionBuffer.toString();
167    }
168
169    /**
170     * Returns the best property value. The specified properties are checked in sequence and the value of the first available property is returned.
171     * @param aProperties properties
172     * @param aConfigParameters properties in sequence to check
173     * @return return the value of the best / first available property
174     */
175    private String getPropertyValue(Properties aProperties, ConfigParameter... aConfigParameters) {
176        for(ConfigParameter theConfigParameter: aConfigParameters) {
177            String theValue = aProperties.getProperty(theConfigParameter.declaration());
178            if(theValue != null) {
179                return theValue;
180            }
181        }
182        return null;
183    }
184
185    public boolean equals(Object anObject) {
186        if(this == anObject) {
187            return true;
188        }
189        if(anObject == null || getClass() != anObject.getClass()) {
190            return false;
191        }
192        PropertyConfigurationLoader theOtherLoader = (PropertyConfigurationLoader)anObject;
193        return myPropertyName.equals(theOtherLoader.myPropertyName);
194    }
195
196    public int hashCode() {
197        return myPropertyName.hashCode();
198    }
199}