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