View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.impl.config.ds;
17  
18  import java.sql.DatabaseMetaData;
19  import java.sql.SQLException;
20  import java.util.List;
21  
22  import javax.annotation.concurrent.ThreadSafe;
23  import javax.naming.NamingException;
24  
25  import de.smartics.properties.api.config.domain.ConfigurationException;
26  import de.smartics.properties.api.config.domain.ConfigurationPropertiesManagement;
27  import de.smartics.properties.api.config.domain.PropertyProvider;
28  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
29  import de.smartics.properties.api.config.domain.key.ConfigurationKeyFactory;
30  import de.smartics.properties.impl.config.cache.CacheConfigurationPropertiesManagement;
31  import de.smartics.properties.spi.config.domain.key.ConfigurationKeyContextManager;
32  import de.smartics.properties.spi.config.ds.DataSourceCode;
33  import de.smartics.properties.spi.config.ds.DataSourceConfigurationMessageBean;
34  import de.smartics.properties.spi.config.ds.DataSourceConfigurationPropertyProvider;
35  import de.smartics.properties.spi.config.ds.DataSourceConnector;
36  import de.smartics.properties.spi.config.ds.DataSourceException;
37  import de.smartics.properties.spi.config.ds.DataSourceProxy;
38  import de.smartics.properties.spi.config.ds.JndiDataSourceProxy;
39  import de.smartics.properties.spi.config.ds.PropertiesStore;
40  import de.smartics.properties.spi.config.support.AbstractConfigurationPropertiesFactory;
41  import de.smartics.properties.spi.config.support.ConfigurationPropertiesManagementSpi;
42  
43  /**
44   * Factory, configured by properties in the
45   * <code>{@value de.smartics.properties.impl.config.ds.DataSourceConfigurationLoader#CLASSPATH_LOCATION}</code>
46   * folder, to create instances of {@link DataSourceConfigurationProperties}.
47   * <p>
48   * Please refer to {@link DataSourceConfiguration} for configuration options.
49   * </p>
50   */
51  @ThreadSafe
52  public class AutodetectDataSourceConfigurationPropertiesFactory extends
53      AbstractConfigurationPropertiesFactory<ConfigurationPropertiesManagement>
54  {
55    // ********************************* Fields *********************************
56  
57    // --- constants ------------------------------------------------------------
58  
59    /**
60     * The class version identifier.
61     */
62    private static final long serialVersionUID = 1L;
63  
64    // --- members --------------------------------------------------------------
65  
66    /**
67     * The cached manager for reuse to access properties information.
68     */
69    private volatile PropertiesStore manager;
70  
71    /**
72     * Optional datasource configuration. When not set via constructor it will be
73     * searched for a configuration in META-INF.
74     */
75    private final DataSourceConfiguration configuration;
76  
77    // ****************************** Initializer *******************************
78  
79    // ****************************** Constructors ******************************
80  
81    /**
82     * Default constructor.
83     */
84    public AutodetectDataSourceConfigurationPropertiesFactory()
85    {
86      getFactoryConfiguration().setSkipClassPathPropertyLoading(true);
87      this.configuration = new DataSourceConfigurationLoader().load();
88      init();
89    }
90  
91    /**
92     * Constructor that enables the configuration to be set.
93     *
94     * @param configuration the configuration to use for this factory.
95     */
96    public AutodetectDataSourceConfigurationPropertiesFactory(
97        final DataSourceConfiguration configuration)
98    {
99      this.configuration = configuration;
100     getFactoryConfiguration().setSkipClassPathPropertyLoading(true);
101     init();
102   }
103 
104   // ****************************** Inner Classes *****************************
105 
106   // ********************************* Methods ********************************
107 
108   // --- init -----------------------------------------------------------------
109 
110   private void init()
111   {
112 
113     final DataSourceProxy dataSourceProxy =
114         createDataSourceProxy(configuration);
115 
116     this.manager = createManager(configuration, dataSourceProxy);
117 
118     if (configuration.isCreateTable())
119     {
120       manager.createConfigTable();
121     }
122 
123     addProviders();
124   }
125 
126   private DataSourceProxy createDataSourceProxy(
127       final DataSourceConfiguration config)
128   {
129     final JndiDataSourceProxy jndiProxy = createProxy(config);
130     final String databaseId = createDatabaseId(jndiProxy);
131     final DataSourceConnector connector =
132         DataSourceProxyManager.getConnectorFor(databaseId);
133     final DataSourceProxy dataSourceProxy = connector.create(jndiProxy);
134     return dataSourceProxy;
135   }
136 
137   private PropertiesStore createManager(final DataSourceConfiguration config,
138       final DataSourceProxy dataSourceProxy)
139   {
140     final PropertiesStore.Builder builder = new PropertiesStore.Builder();
141     builder.setDataSourceProxy(dataSourceProxy);
142     builder.setIgnoreTableCreationProblems(false);
143     builder.setCreateTableSqlStatementTemplate(dataSourceProxy
144         .getCreateTableSqlTemplate());
145     builder.setInsertOrUpdateSqlStatementTemplate(dataSourceProxy
146         .getInsertOrUpdateSqlTemplate());
147     builder.setDropTable(config.isDropTable());
148     return builder.build();
149   }
150 
151   private void addProviders()
152   {
153     final List<String> keys = manager.getConfigurationKeys();
154     for (final String key : keys)
155     {
156       final ConfigurationKey<?> configurationKey = parseKey(key);
157       final PropertyProvider provider =
158           new DataSourceConfigurationPropertyProvider(configurationKey, manager);
159       addPropertyProviders(provider);
160     }
161   }
162 
163   private ConfigurationKey<?> parseKey(final String key)
164   {
165     final ConfigurationKeyFactory<?> factory =
166         ConfigurationKeyContextManager.INSTANCE.context()
167             .configurationKeyFactory();
168     try
169     {
170       final ConfigurationKey<?> configurationKey =
171           factory.createKeyFromString(key);
172       return configurationKey;
173 
174     }
175     catch (final IllegalArgumentException e)
176     {
177       final DataSourceConfigurationMessageBean message =
178           new DataSourceConfigurationMessageBean(
179               DataSourceCode.INVALID_CONFIGURATION_KEY, e,
180               manager.getDataSourceId(), null);
181       throw new DataSourceException(message); // NOPMD
182     }
183   }
184 
185   private JndiDataSourceProxy createProxy(final DataSourceConfiguration config)
186   {
187     try
188     {
189       return new JndiDataSourceProxy(config.getJndiName());
190     }
191     catch (final NamingException e)
192     {
193       throw new IllegalArgumentException("Cannot access JNDI context: "
194                                          + e.getMessage(), e);
195     }
196   }
197 
198   private static String createDatabaseId(final JndiDataSourceProxy proxy)
199   {
200     try
201     {
202       final DatabaseMetaData metaData =
203           proxy.getDataSource().getConnection().getMetaData();
204       final String databaseId = metaData.getDatabaseProductName();
205       return databaseId;
206     }
207     catch (final SQLException e)
208     {
209       throw new IllegalArgumentException("Cannot read database metadata: "
210                                          + e.getMessage(), e);
211     }
212   }
213 
214   // --- get&set --------------------------------------------------------------
215 
216   // --- business -------------------------------------------------------------
217 
218   @Override
219   protected ConfigurationPropertiesManagement createNewConfiguration(
220       final ConfigurationKey<?> key) throws NullPointerException,
221     ConfigurationException
222   {
223     final ConfigurationPropertiesManagement config = createCache(key);
224     return config;
225   }
226 
227   private ConfigurationPropertiesManagement createCache(
228       final ConfigurationKey<?> key)
229   {
230     final ConfigurationPropertiesManagementSpi uncached =
231         new DataSourceConfigurationProperties(key, getRegistry(), manager,
232             getFactoryConfiguration().getDecrypter());
233     if (getFactoryConfiguration().isUseCache())
234     {
235       return new CacheConfigurationPropertiesManagement(uncached);
236     }
237     else
238     {
239       return uncached;
240     }
241   }
242 
243   // --- object basics --------------------------------------------------------
244 
245 }