package org.infinispan.tools.jdbc.migrator; import static org.infinispan.tools.jdbc.migrator.Element.BINARY; import static org.infinispan.tools.jdbc.migrator.Element.CACHE_NAME; import static org.infinispan.tools.jdbc.migrator.Element.CLASS; import static org.infinispan.tools.jdbc.migrator.Element.CONNECTION_POOL; import static org.infinispan.tools.jdbc.migrator.Element.CONNECTION_URL; import static org.infinispan.tools.jdbc.migrator.Element.DATA; import static org.infinispan.tools.jdbc.migrator.Element.DB; import static org.infinispan.tools.jdbc.migrator.Element.DIALECT; import static org.infinispan.tools.jdbc.migrator.Element.DISABLE_INDEXING; import static org.infinispan.tools.jdbc.migrator.Element.DISABLE_UPSERT; import static org.infinispan.tools.jdbc.migrator.Element.DRIVER_CLASS; import static org.infinispan.tools.jdbc.migrator.Element.EXTERNALIZERS; import static org.infinispan.tools.jdbc.migrator.Element.ID; import static org.infinispan.tools.jdbc.migrator.Element.KEY_TO_STRING_MAPPER; import static org.infinispan.tools.jdbc.migrator.Element.MAJOR_VERSION; import static org.infinispan.tools.jdbc.migrator.Element.MARSHALLER; import static org.infinispan.tools.jdbc.migrator.Element.MINOR_VERSION; import static org.infinispan.tools.jdbc.migrator.Element.NAME; import static org.infinispan.tools.jdbc.migrator.Element.PASSWORD; import static org.infinispan.tools.jdbc.migrator.Element.SOURCE; import static org.infinispan.tools.jdbc.migrator.Element.STRING; import static org.infinispan.tools.jdbc.migrator.Element.TABLE; import static org.infinispan.tools.jdbc.migrator.Element.TABLE_NAME_PREFIX; import static org.infinispan.tools.jdbc.migrator.Element.TARGET; import static org.infinispan.tools.jdbc.migrator.Element.TIMESTAMP; import static org.infinispan.tools.jdbc.migrator.Element.TYPE; import static org.infinispan.tools.jdbc.migrator.Element.USERNAME; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.infinispan.commons.CacheConfigurationException; import org.infinispan.commons.marshall.AdvancedExternalizer; import org.infinispan.commons.marshall.StreamingMarshaller; import org.infinispan.commons.util.Util; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.configuration.global.SerializationConfigurationBuilder; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.persistence.jdbc.DatabaseType; import org.infinispan.persistence.jdbc.configuration.ConnectionFactoryConfiguration; import org.infinispan.persistence.jdbc.configuration.JdbcStringBasedStoreConfigurationBuilder; import org.infinispan.persistence.jdbc.configuration.PooledConnectionFactoryConfiguration; import org.infinispan.persistence.jdbc.configuration.TableManipulationConfiguration; import org.infinispan.persistence.jdbc.table.management.DbMetaData; import org.infinispan.persistence.keymappers.DefaultTwoWayKey2StringMapper; import org.infinispan.persistence.keymappers.TwoWayKey2StringMapper; import org.infinispan.tools.jdbc.migrator.marshaller.LegacyVersionAwareMarshaller; import org.infinispan.transaction.TransactionMode; import org.infinispan.transaction.lookup.EmbeddedTransactionManagerLookup; /** * @author Ryan Emerson * @since 9.0 */ class MigratorConfiguration { final String cacheName; final StoreType storeType; private final Properties properties; private final boolean sourceStore; private final Element orientation; private final ClassLoader classLoader; private DbMetaData dbMetaData; private TableManipulationConfiguration stringTable = null; private TableManipulationConfiguration binaryTable = null; private ConnectionFactoryConfiguration connectionConfig; private JdbcStringBasedStoreConfigurationBuilder jdbcConfigBuilder; private TwoWayKey2StringMapper key2StringMapper; private StreamingMarshaller marshaller; MigratorConfiguration(boolean sourceStore, Properties properties) { this.properties = properties; this.sourceStore = sourceStore; this.orientation = sourceStore ? SOURCE : TARGET; this.classLoader = MigratorConfiguration.class.getClassLoader(); requiredProps(propKey(CACHE_NAME), propKey(TYPE)); this.cacheName = property(CACHE_NAME); this.storeType = StoreType.valueOf(property(TYPE).toUpperCase()); initStoreConfig(); } private void requiredProps(String... required) { for (String prop : required) { if (properties.get(prop) == null) { String msg = String.format("The property %s must be specified.", prop); throw new CacheConfigurationException(msg); } } } private void initStoreConfig() { JdbcStringBasedStoreConfigurationBuilder builder = new ConfigurationBuilder().persistence() .addStore(JdbcStringBasedStoreConfigurationBuilder.class); dbMetaData = createDbMeta(); connectionConfig = createConnectionConfig(builder); marshaller = createMarshaller(); if (sourceStore) { if (storeType == StoreType.MIXED || storeType == StoreType.STRING) { stringTable = createTableConfig(STRING, builder); key2StringMapper = createTwoWayMapper(); } if (storeType == StoreType.MIXED || storeType == StoreType.BINARY) binaryTable = createTableConfig(BINARY, builder); } else { key2StringMapper = createTwoWayMapper(); builder.key2StringMapper(key2StringMapper.getClass()); stringTable = createTableConfig(STRING, builder); builder.transaction() .transactionMode(TransactionMode.TRANSACTIONAL) .transactionManagerLookup(new EmbeddedTransactionManagerLookup()); } builder.validate(); jdbcConfigBuilder = builder; } private DbMetaData createDbMeta() { requiredProps(propKey(DIALECT)); String prop; DatabaseType type = DatabaseType.valueOf(property(DIALECT).toUpperCase()); int major = (prop = property(DB, MAJOR_VERSION)) != null ? new Integer(prop) : -1; int minor = (prop = property(DB, MINOR_VERSION)) != null ? new Integer(prop) : -1; boolean upsert = Boolean.parseBoolean(property(DB, DISABLE_UPSERT)); boolean indexing = Boolean.parseBoolean(property(DB, DISABLE_INDEXING)); return new DbMetaData(type, major, minor, upsert, indexing); } private TableManipulationConfiguration createTableConfig(Element tableType, JdbcStringBasedStoreConfigurationBuilder storeBuilder) { boolean createOnStart = orientation == TARGET; return storeBuilder.table() .createOnStart(createOnStart) .tableNamePrefix(property(TABLE, tableType, TABLE_NAME_PREFIX)) .idColumnName(property(TABLE, tableType, ID, NAME)) .idColumnType(property(TABLE, tableType, ID, TYPE)) .dataColumnName(property(TABLE, tableType, DATA, NAME)) .dataColumnType(property(TABLE, tableType, DATA, TYPE)) .timestampColumnName(property(TABLE, tableType, TIMESTAMP, NAME)) .timestampColumnType(property(TABLE, tableType, TIMESTAMP, TYPE)) .create(); } private PooledConnectionFactoryConfiguration createConnectionConfig(JdbcStringBasedStoreConfigurationBuilder storeBuilder) { requiredProps(propKey(CONNECTION_POOL, CONNECTION_URL), propKey(CONNECTION_POOL, DRIVER_CLASS)); return storeBuilder.connectionPool() .connectionUrl(property(CONNECTION_POOL, CONNECTION_URL)) .driverClass(property(CONNECTION_POOL, DRIVER_CLASS)) .username(property(CONNECTION_POOL, USERNAME)) .password(property(CONNECTION_POOL, PASSWORD)) .create(); } private TwoWayKey2StringMapper createTwoWayMapper() { String mapperClass = property(KEY_TO_STRING_MAPPER); if (mapperClass != null) { ClassLoader classLoader = MigratorConfiguration.class.getClassLoader(); try { return (TwoWayKey2StringMapper) Util.loadClass(mapperClass, classLoader).newInstance(); } catch (IllegalAccessException | InstantiationException e) { throw new CacheConfigurationException(String.format("Unabled to load TwoWayKey2StringMapper '%s' for %s store", mapperClass, orientation), e); } } return new DefaultTwoWayKey2StringMapper(); } private StreamingMarshaller createMarshaller() { MarshallerType marshallerType = MarshallerType.CURRENT; String marshallerTypeProp = property(MARSHALLER, TYPE); if (marshallerTypeProp != null) marshallerType = MarshallerType.valueOf(property(MARSHALLER, TYPE).toUpperCase()); switch (marshallerType) { case CURRENT: GlobalConfigurationBuilder globalConfig = new GlobalConfigurationBuilder() .globalJmxStatistics() .allowDuplicateDomains(true) .defaultCacheName(cacheName); SerializationConfigurationBuilder serializationBuilder = globalConfig.serialization(); for (Map.Entry<Integer, AdvancedExternalizer<?>> entry : getExternalizersFromProps().entrySet()) { serializationBuilder.addAdvancedExternalizer(entry.getKey(), entry.getValue()); } EmbeddedCacheManager manager = new DefaultCacheManager(globalConfig.build(), new ConfigurationBuilder().build()); return manager.getCache().getAdvancedCache().getComponentRegistry().getComponent(StreamingMarshaller.class); case CUSTOM: String marshallerClass = property(MARSHALLER, CLASS); if (marshallerClass == null) throw new CacheConfigurationException( String.format("The property %s.%s must be set if a custom marshaller type is specified", MARSHALLER, CLASS)); try { return (StreamingMarshaller) Util.loadClass(marshallerClass, classLoader).newInstance(); } catch (IllegalAccessException | InstantiationException e) { throw new CacheConfigurationException(String.format("Unabled to load StreamingMarshaller '%s' for %s store", marshallerClass, orientation), e); } case LEGACY: if (orientation != SOURCE) throw new CacheConfigurationException("The legacy marshaller can only be specified for source stores."); return new LegacyVersionAwareMarshaller(getExternalizersFromProps()); default: throw new IllegalStateException("Unexpected marshaller type"); } } // Expects externalizer string to be a comma-separated list of "<id>:<class>" private Map<Integer, AdvancedExternalizer<?>> getExternalizersFromProps() { Map<Integer, AdvancedExternalizer<?>> map = new HashMap<>(); String externalizers = property(MARSHALLER, EXTERNALIZERS); if (externalizers != null) { for (String ext : externalizers.split(",")) { String[] extArray = ext.split(":"); String className = extArray.length > 1 ? extArray[1] : extArray[0]; AdvancedExternalizer<?> instance = Util.getInstance(className, classLoader); int id = extArray.length > 1 ? new Integer(extArray[0]) : instance.getId(); map.put(id, instance); } } return map; } ConnectionFactoryConfiguration getConnectionConfig() { return connectionConfig; } DbMetaData getDbMeta() { return dbMetaData; } TableManipulationConfiguration getStringTable() { return stringTable; } TableManipulationConfiguration getBinaryTable() { return binaryTable; } JdbcStringBasedStoreConfigurationBuilder getJdbcConfigBuilder() { return jdbcConfigBuilder; } TwoWayKey2StringMapper getKey2StringMapper() { return key2StringMapper; } boolean hasCustomMarshaller() { return marshaller != null; } StreamingMarshaller getMarshaller() { return marshaller; } private String property(Element... elements) { String key = propKey(elements); return properties.getProperty(key); } private String propKey(Element... elements) { StringBuilder sb = new StringBuilder(orientation.toString().toLowerCase()); sb.append("."); for (int i = 0; i < elements.length; i++) { sb.append(elements[i].toString()); if (i != elements.length - 1) sb.append("."); } return sb.toString(); } }