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