001package com.nimbusds.infinispan.persistence.sql.config;
002
003
004import java.util.Properties;
005
006import net.jcip.annotations.Immutable;
007import org.apache.commons.lang3.StringUtils;
008import org.infinispan.commons.configuration.BuiltBy;
009import org.infinispan.commons.configuration.ConfigurationFor;
010import org.infinispan.commons.configuration.attributes.Attribute;
011import org.infinispan.commons.configuration.attributes.AttributeDefinition;
012import org.infinispan.commons.configuration.attributes.AttributeSet;
013import org.infinispan.commons.util.StringPropertyReplacer;
014import org.infinispan.configuration.cache.AbstractStoreConfiguration;
015import org.infinispan.configuration.cache.AsyncStoreConfiguration;
016import org.infinispan.configuration.cache.SingletonStoreConfiguration;
017import org.jooq.SQLDialect;
018
019import com.nimbusds.common.config.LoggableConfiguration;
020import com.nimbusds.infinispan.persistence.sql.Loggers;
021import com.nimbusds.infinispan.persistence.sql.SQLRecordTransformer;
022import com.nimbusds.infinispan.persistence.sql.SQLStore;
023
024
025/**
026 * SQL store configuration.
027 * 
028 * <p>Example XML configuration:
029 * 
030 * <pre>
031 * &lt;infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
032 *                xsi:schemaLocation="urn:infinispan:config:8.2 http://www.infinispan.org/schemas/infinispan-config-8.2.xsd"
033 *                xmlns="urn:infinispan:config:8.2"
034 *                xmlns:sql="urn:infinispan:config:store:sql:2.2"&gt;
035 *      
036 *      &lt;cache-container name="myCacheContainer" default-cache="myMap" statistics="true"&gt;
037 *              &lt;jmx duplicate-domains="true"/&gt;
038 *              &lt;local-cache name="myMap"&gt;
039 *                      &lt;eviction type="COUNT" size="100"/&gt;
040 *                      &lt;persistence passivation="false"&gt;
041 *                              &lt;sql-store xmlns="urn:infinispan:config:store:sql:2.2"
042 *                                            shared="true"
043 *                                            record-transformer="com.nimbusds.infinispan.persistence.sql.UserRecordTransformer"
044 *                                            sql-dialect="H2"
045 *                                            create-table-if-missing="true"&gt;
046 *
047 *                              &lt;property name="jdbcUrl"&gt;jdbc:h2:mem:test;DATABASE_TO_UPPER=false&lt;/property&gt;
048 *                              &lt;property name="username"&gt;admin&lt;/property&gt;
049 *                              &lt;property name="password"&gt;secret&lt;/property&gt;
050 *
051 *                              &lt;/sql-store&gt;
052 *                      &lt;/persistence&gt;
053 *              &lt;/local-cache&gt;
054 *      &lt;/cache-container&gt;
055 *      
056 * &lt;/infinispan&gt;
057 * </pre>
058 */
059@Immutable
060@BuiltBy(SQLStoreConfigurationBuilder.class)
061@ConfigurationFor(SQLStore.class)
062public class SQLStoreConfiguration extends AbstractStoreConfiguration implements LoggableConfiguration {
063        
064        
065        /**
066         * The attribute definition for the record transformer class.
067         */
068        static final AttributeDefinition<Class> RECORD_TRANSFORMER = AttributeDefinition.builder("recordTransformer", null, Class.class).build();
069        
070        
071        /**
072         * The attribute definition for the query executor class.
073         */
074        static final AttributeDefinition<Class> QUERY_EXECUTOR = AttributeDefinition.builder("queryExecutor", null, Class.class).build();
075        
076        
077        /**
078         * The attribute definition for the SQL dialect.
079         */
080        static final AttributeDefinition<SQLDialect> SQL_DIALECT = AttributeDefinition.builder("sqlDialect", SQLDialect.DEFAULT).build();
081        
082        
083        /**
084         * The attribute definition for the optional create table if missing
085         * setting.
086         */
087        static final AttributeDefinition<Boolean> CREATE_TABLE_IF_MISSING = AttributeDefinition.builder("createTableIfMissing", Boolean.TRUE).build();
088        
089        
090        /**
091         * The attribute definition for the optional connection pool reference.
092         */
093        static final AttributeDefinition<String> CONNECTION_POOL = AttributeDefinition.builder("connectionPool", null, String.class).build();
094        
095        
096        /**
097         * Returns the attribute definitions for the SQL store configuration.
098         *
099         * @return The attribute definitions.
100         */
101        public static AttributeSet attributeDefinitionSet() {
102                return new AttributeSet(SQLStoreConfiguration.class,
103                        AbstractStoreConfiguration.attributeDefinitionSet(),
104                        RECORD_TRANSFORMER, QUERY_EXECUTOR, SQL_DIALECT, CREATE_TABLE_IF_MISSING, CONNECTION_POOL);
105        }
106        
107        
108        /**
109         * The class for transforming between Infinispan entries (key / value
110         * pair and optional metadata) and a corresponding SQL record.
111         *
112         * <p>See {@link SQLRecordTransformer}.
113         */
114        private final Attribute<Class> recordTransformerClass;
115        
116        
117        /**
118         * The optional class for executing direct SQL queries against the
119         * database.
120         *
121         * <p>See {@link com.nimbusds.infinispan.persistence.common.query.QueryExecutor}
122         */
123        private final Attribute<Class> queryExecutorClass;
124        
125        
126        /**
127         * The configured SQL dialect.
128         */
129        private final Attribute<SQLDialect> sqlDialect;
130        
131        
132        /**
133         * The configured optional create table if missing setting.
134         */
135        private final Attribute<Boolean> createTableIfMissing;
136        
137        
138        /**
139         * The configured connection pool reference.
140         */
141        private final Attribute<String> connectionPool;
142
143
144        /**
145         * Creates a new SQL store configuration.
146         *
147         * @param attributes           The configuration attributes. Must not be
148         *                             {@code null}.
149         * @param asyncConfig          Configuration for the async cache
150         *                             loader.
151         * @param singletonStoreConfig Configuration for a singleton store.
152         */
153        public SQLStoreConfiguration(final AttributeSet attributes,
154                                     final AsyncStoreConfiguration asyncConfig,
155                                     final SingletonStoreConfiguration singletonStoreConfig) {
156
157                super(attributes, asyncConfig, singletonStoreConfig);
158                
159                recordTransformerClass = attributes.attribute(RECORD_TRANSFORMER);
160                assert recordTransformerClass != null;
161                
162                queryExecutorClass = attributes.attribute(QUERY_EXECUTOR);
163                
164                sqlDialect = attributes.attribute(SQL_DIALECT);
165                assert sqlDialect != null;
166                
167                createTableIfMissing = attributes.attribute(CREATE_TABLE_IF_MISSING);
168                
169                connectionPool = attributes.attribute(CONNECTION_POOL);
170        }
171        
172        
173        /**
174         * Returns the class for transforming between Infinispan entries (key /
175         * value pairs and optional metadata) and a corresponding SQL record.
176         *
177         * <p>See {@link SQLRecordTransformer}.
178         *
179         * @return The record transformer class.
180         */
181        public Class getRecordTransformerClass() {
182                
183                return recordTransformerClass.get();
184        }
185        
186        
187        /**
188         * Returns the optional class for executing direct SQL queries against
189         * the database.
190         *
191         * <p>See {@link com.nimbusds.infinispan.persistence.common.query.QueryExecutor}
192         *
193         * @return The query executor class, {@code null} if not specified.
194         */
195        public Class getQueryExecutorClass() {
196                
197                return queryExecutorClass.get();
198        }
199        
200        
201        /**
202         * Returns the configured SQL dialect.
203         *
204         * @return The SQL dialect.
205         */
206        public SQLDialect getSQLDialect() {
207                
208                return sqlDialect.get();
209        }
210        
211        
212        /**
213         * Returns the configured create table if missing setting.
214         *
215         * @return {@code true} to create the underlying table(s) if missing,
216         *         {@code false} to skip this check.
217         */
218        public boolean createTableIfMissing() {
219                
220                return createTableIfMissing.get();
221        }
222        
223        
224        /**
225         * Returns the configured connection pool reference.
226         *
227         * @return The connection pool reference, {@code null} if none.
228         */
229        public String getConnectionPool() {
230                
231                return connectionPool.get();
232        }
233        
234        
235        @Override
236        public Properties properties() {
237                
238                // Interpolate with system properties where ${sysPropName} is found
239                
240                Properties interpolatedProps = new Properties();
241                
242                for (String name: super.properties().stringPropertyNames()) {
243                        interpolatedProps.setProperty(name, StringPropertyReplacer.replaceProperties(super.properties().getProperty(name)));
244                }
245                
246                return interpolatedProps;
247        }
248        
249        
250        @Override
251        public void log() {
252                
253                Loggers.MAIN_LOG.info("[IS0000] Infinispan SQL store: Record transformer class: {} ", getRecordTransformerClass().getCanonicalName());
254                Loggers.MAIN_LOG.info("[IS0001] Infinispan SQL store: Query executor class: {} ", getQueryExecutorClass() != null ? getQueryExecutorClass().getCanonicalName() : "not specified");
255                Loggers.MAIN_LOG.info("[IS0002] Infinispan SQL store: SQL dialect: {} ", sqlDialect);
256                Loggers.MAIN_LOG.info("[IS0004] Infinispan SQL store: Create table if missing: {} ", createTableIfMissing);
257                Loggers.MAIN_LOG.info("[IS0008] Infinispan SQL store: Connection pool reference: {} ", getConnectionPool() != null ? getConnectionPool() : "not specified");
258                
259                if (StringUtils.isNotBlank(properties().getProperty("dataSourceClassName"))) {
260                        Loggers.MAIN_LOG.info("[IS0005] Infinispan SQL store: Data source class name: {} ", properties().getProperty("dataSourceClassName"));
261                }
262                
263                if (StringUtils.isNotBlank(properties().getProperty("dataSource.url"))) {
264                        Loggers.MAIN_LOG.info("[IS0006] Infinispan SQL store: Data source URL: {} ", properties().getProperty("dataSource.url"));
265                }
266                
267                // Old style JDBC URL config
268                if (StringUtils.isNotBlank(properties().getProperty("jdbcUrl"))) {
269                        Loggers.MAIN_LOG.info("[IS0003] Infinispan SQL store: JDBC URL: {} ", properties().getProperty("jdbcUrl"));
270                }
271        }
272}