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.service.connection.id.ConnectionIdGenerator;
026import de.novanic.eventservice.service.connection.strategy.connector.ConnectionStrategyServerConnector;
027
028import java.lang.reflect.Constructor;
029import java.lang.reflect.InvocationTargetException;
030
031/**
032 * The {@link de.novanic.eventservice.config.ConfigurationDependentFactory} can create instances from a configuration
033 * which can be configured with a class name (for example {@link de.novanic.eventservice.config.ConfigParameter#CONNECTION_ID_GENERATOR}.
034 * The created instances are hold as a singleton.
035 *
036 * @author sstrohschein
037 *         <br>Date: 05.04.2010
038 *         <br>Time: 13:55:51
039 */
040public final class ConfigurationDependentFactory
041{
042    private static EventServiceConfiguration myConfiguration;
043
044    private ConnectionIdGenerator myConnectionIdGenerator;
045    private ConnectionStrategyServerConnector myConnectionStrategyServerConnector;
046
047    /**
048     * Initializes the {@link de.novanic.eventservice.config.ConfigurationDependentFactory}. That constructor is only called one time,
049     * because the factory is created as a singleton.
050     */
051    private ConfigurationDependentFactory() {}
052
053    /**
054     * Factory-Holder class to ensure thread-safe lazy-loading with IODH.
055     */
056    private static class ConfigurationDependentFactoryHolder {
057        private static ConfigurationDependentFactory INSTANCE = new ConfigurationDependentFactory();
058    }
059
060    /**
061     * This method should be used to create an instance of {@link de.novanic.eventservice.config.ConfigurationDependentFactory}.
062     * {@link de.novanic.eventservice.config.ConfigurationDependentFactory} is a singleton, so this method returns always the same instance of
063     * {@link de.novanic.eventservice.config.ConfigurationDependentFactory}. This method initializes the factory with a configuration and should be
064     * called before {@link ConfigurationDependentFactory#getInstance()} is used, because it has to be initialized with
065     * with a configuration at first.
066     * @return {@link de.novanic.eventservice.config.ConfigurationDependentFactory} (singleton)
067     */
068    public static ConfigurationDependentFactory getInstance(EventServiceConfiguration aConfiguration) {
069        if(aConfiguration == null) {
070            throw new ConfigurationException(ConfigurationDependentFactory.class.getName() + " was initialized without a configuration!");
071        }
072        if(myConfiguration == null) {
073            myConfiguration = aConfiguration;
074        }
075        return ConfigurationDependentFactoryHolder.INSTANCE;
076    }
077
078    /**
079     * This method returns the {@link de.novanic.eventservice.config.ConfigurationDependentFactory} as a singleton. It has to be initialized
080     * with a configuration at first. Therefore the method {@link de.novanic.eventservice.config.ConfigurationDependentFactory#getInstance(EventServiceConfiguration)} should
081     * be used at first.
082     * @return {@link de.novanic.eventservice.config.ConfigurationDependentFactory} (singleton)
083     */
084    public static ConfigurationDependentFactory getInstance() {
085        if(myConfiguration == null) {
086            throw new ConfigurationException(ConfigurationDependentFactory.class.getName() + " has to be initialized with a configuration before!");
087        }
088        return ConfigurationDependentFactoryHolder.INSTANCE;
089    }
090
091    /**
092     * Returns the configured {@link de.novanic.eventservice.service.connection.id.ConnectionIdGenerator}.
093     * @see de.novanic.eventservice.config.ConfigParameter#CONNECTION_ID_GENERATOR
094     * @return the configured {@link de.novanic.eventservice.service.connection.id.ConnectionIdGenerator}
095     */
096    public ConnectionIdGenerator getConnectionIdGenerator() {
097        if(myConnectionIdGenerator == null) {
098            synchronized(this) {
099                if(myConnectionIdGenerator == null) {
100                    try {
101                        myConnectionIdGenerator = createObject(myConfiguration.getConnectionIdGeneratorClassName());
102                    } catch(ClassCastException e) {
103                        throw new ConfigurationException(myConfiguration.getConnectionIdGeneratorClassName() + " should have another type!", e);
104                    }
105                }
106            }
107        }
108        return myConnectionIdGenerator;
109    }
110
111    /**
112     * Returns the server side part / connector of the configured connection strategy.
113     * @see de.novanic.eventservice.config.ConfigParameter#CONNECTION_STRATEGY_SERVER_CONNECTOR
114     * @return server side part / connector of the configured connection strategy
115     */
116    public ConnectionStrategyServerConnector getConnectionStrategyServerConnector() {
117        if(myConnectionStrategyServerConnector == null) {
118            synchronized(this) {
119                if(myConnectionStrategyServerConnector == null) {
120                    try {
121                        myConnectionStrategyServerConnector = createObject(myConfiguration.getConnectionStrategyServerConnectorClassName());
122                    } catch(ClassCastException e) {
123                        throw new ConfigurationException(myConfiguration.getConnectionStrategyServerConnectorClassName() + " should have another type!", e);
124                    }
125                }
126            }
127        }
128        return myConnectionStrategyServerConnector;
129    }
130
131    /**
132     * Creates and initializes an object of a specific type.
133     */
134    @SuppressWarnings("unchecked")
135    private static <T> T createObject(String aClassName) {
136        //when no class is configured, no object is created
137        if(aClassName == null) {
138            return null;
139        }
140
141        try {
142            final Class theConnectionIdGeneratorClass = Class.forName(aClassName);
143
144            Constructor<T> theDefaultConstructor = null;
145            for(Constructor<T> theConstructor: theConnectionIdGeneratorClass.getDeclaredConstructors()) {
146                Class<?>[] theParameterTypes = theConstructor.getParameterTypes();
147                switch(theParameterTypes.length) {
148                    case 0:
149                        theDefaultConstructor = theConstructor;
150                        break;
151                    case 1:
152                        if(theParameterTypes[0].equals(EventServiceConfiguration.class)) {
153                            return theConstructor.newInstance(myConfiguration);
154                        }
155                }
156            }
157
158            if(theDefaultConstructor == null) {
159                throw new ConfigurationException("The class \"" + theConnectionIdGeneratorClass + "\" has no default constructor and no constructor which requires a single configuration! At least one of both is needed!");
160            }
161            return theDefaultConstructor.newInstance();
162        } catch(ClassNotFoundException e) {
163            throw new ConfigurationException(aClassName + " couldn't be instantiated!", e);
164        } catch(InstantiationException e) {
165            throw new ConfigurationException(aClassName + " couldn't be instantiated!", e);
166        } catch(IllegalAccessException e) {
167            throw new ConfigurationException(aClassName + " couldn't be instantiated!", e);
168        } catch(InvocationTargetException e) {
169            throw new ConfigurationException(aClassName + " couldn't be instantiated!", e);
170        }
171    }
172
173    /**
174     * Returns the configuration which was specified with the initialization of the {@link de.novanic.eventservice.config.ConfigurationDependentFactory}.
175     * @see {@link ConfigurationDependentFactory#getInstance(EventServiceConfiguration)}
176     * @return configuration which was specified with the initialization of the {@link de.novanic.eventservice.config.ConfigurationDependentFactory}
177     */
178    public static EventServiceConfiguration getConfiguration() {
179        return myConfiguration;
180    }
181
182    public static void reset() {
183        myConfiguration = null;
184        ConfigurationDependentFactoryHolder.INSTANCE.myConnectionIdGenerator = null;
185        ConfigurationDependentFactoryHolder.INSTANCE.myConnectionStrategyServerConnector = null;
186    }
187}