/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.datastore.infinispanremote.configuration.impl;
import static org.hibernate.ogm.datastore.infinispanremote.InfinispanRemoteProperties.HOT_ROD_CLIENT_PREFIX;
import static org.infinispan.client.hotrod.impl.ConfigurationProperties.FORCE_RETURN_VALUES;
import static org.infinispan.client.hotrod.impl.ConfigurationProperties.MARSHALLER;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.ogm.datastore.infinispanremote.InfinispanRemoteProperties;
import org.hibernate.ogm.datastore.infinispanremote.impl.protostream.OgmProtoStreamMarshaller;
import org.hibernate.ogm.datastore.infinispanremote.logging.impl.Log;
import org.hibernate.ogm.datastore.infinispanremote.logging.impl.LoggerFactory;
import org.hibernate.ogm.datastore.infinispanremote.schema.spi.SchemaCapture;
import org.hibernate.ogm.datastore.infinispanremote.schema.spi.SchemaOverride;
import org.hibernate.ogm.util.configurationreader.spi.ConfigurationPropertyReader;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
/**
* Configuration for {@link InfinispanRemoteProperties}.
* <p>
* This class also keep track of the configuration for the Hot Rod client.
* There are two ways to configure the client:
* <ul>
* <li>Using an external Hot Rod properties file; see {@link InfinispanRemoteProperties#CONFIGURATION_RESOURCE_NAME}
* <li>Defining the properties of the client using {@value InfinispanRemoteProperties#HOT_ROD_CLIENT_PREFIX}
* </ul>
* <p>
* When a property of the Hot Rod client is set in the hibernate configuration, it should be prefixed
* with {@value InfinispanRemoteProperties#HOT_ROD_CLIENT_PREFIX}.
* For example, the property {@code infinispan.client.hotrod.server_list}
* becomes {@code hibernate.ogm.infinispan_remote.client.server_list}.
* <p>
* Currently, some properties in the Hot Rod client don't have a prefix, in this case it must be added
* in the hibernate configuration.
* For example, the property {@code maxActive} becomes {@code hibernate.ogm.infinispan_remote.client.maxActive}
* <p>
* Properties with the Hibernate OGM prefix ({@value InfinispanRemoteProperties#HOT_ROD_CLIENT_PREFIX}) will
* override corresponding properties defined in the external Hot Rod configuration file.
*
* @see InfinispanRemoteProperties
* @see ConfigurationProperties
*
* @author Davide D'Alto
*/
public class InfinispanRemoteConfiguration {
private static final Log log = LoggerFactory.getLogger();
/**
* The prefix used by many configuration properties for Hot Rod
*/
private static final String HOT_ROD_ORIGINAL_PREFIX = "infinispan.client.hotrod.";
/*
* Currently, some properties in Hot Rod are without a prefix
*/
private static String[] noPrefixProperties = {
"exhaustedAction",
"maxActive",
"maxTotal",
"maxWait",
"maxIdle",
"minIdle",
"numTestsPerEvictionRun",
"minEvictableIdleTimeMillis",
"timeBetweenEvictionRunsMillis",
"lifo",
"testOnBorrow",
"testOnReturn",
"testWhileIdle"
};
/*
* The expected configuration value for some properties, an exception is thrown if the value is changed.
*/
private static final String[][] expectedValuesForHotRod = {
{ FORCE_RETURN_VALUES, "true" },
{ MARSHALLER, OgmProtoStreamMarshaller.class.getName() }
};
private URL configurationResource;
private SchemaCapture schemaCaptureService;
private SchemaOverride schemaOverrideService;
private String schemaPackageName;
private Properties clientProperties;
/**
* The location of the configuration file.
*
* @see InfinispanRemoteProperties#CONFIGURATION_RESOURCE_NAME
* @return might be the name of the file (too look it up in the class path) or an URL to a file.
*/
public URL getConfigurationResourceUrl() {
return configurationResource;
}
/**
* Extract from the configuration the properties to apply to the Hot Rod (Infinispan remote) client.
*
* @return the clientProperties Hot Rod client properties
*/
public Properties getClientProperties() {
return clientProperties;
}
public SchemaCapture getSchemaCaptureService() {
return schemaCaptureService;
}
public SchemaOverride getSchemaOverrideService() {
return schemaOverrideService;
}
public String getSchemaPackageName() {
return schemaPackageName;
}
/**
* Initialize the internal values from the given {@link Map}.
*
* @param configurationMap
* The values to use as configuration
* @param serviceRegistry
*/
public void initConfiguration(Map<?, ?> configurationMap, ServiceRegistryImplementor serviceRegistry) {
ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class );
ConfigurationPropertyReader propertyReader = new ConfigurationPropertyReader( configurationMap, classLoaderService );
this.configurationResource = propertyReader
.property( InfinispanRemoteProperties.CONFIGURATION_RESOURCE_NAME, URL.class )
.getValue();
this.clientProperties = getHotRodConfiguration( configurationMap, propertyReader, this.configurationResource );
this.schemaCaptureService = propertyReader
.property( InfinispanRemoteProperties.SCHEMA_CAPTURE_SERVICE, SchemaCapture.class )
.instantiate()
.getValue();
this.schemaOverrideService = propertyReader
.property( InfinispanRemoteProperties.SCHEMA_OVERRIDE_SERVICE, SchemaOverride.class )
.instantiate()
.getValue();
this.schemaPackageName = propertyReader
.property( InfinispanRemoteProperties.SCHEMA_PACKAGE_NAME, String.class )
.withDefault( InfinispanRemoteProperties.DEFAULT_SCHEMA_PACKAGE_NAME )
.getValue();
log.tracef( "Initializing Infinispan Hot Rod client from configuration file at '%1$s'", configurationResource );
}
/**
* Extract from the configuration the Hot Rod client properties: the one prefixed with
* {@link InfinispanRemoteProperties#HOT_ROD_CLIENT_PREFIX} or defined in the resource file via
* {@link InfinispanRemoteProperties#CONFIGURATION_RESOURCE_NAME}.
* <p>
* Note that the properties with the prefix will override the same properties in the resource file.
*/
private Properties getHotRodConfiguration(Map<?, ?> configurationMap, ConfigurationPropertyReader propertyReader, URL configurationResourceUrl) {
Properties hotRodConfiguration = new Properties();
loadResourceFile( configurationResourceUrl, hotRodConfiguration );
setAdditionalProperties( configurationMap, propertyReader, hotRodConfiguration );
setExpectedPropertiesIfNull( hotRodConfiguration );
validate( hotRodConfiguration );
return hotRodConfiguration;
}
/**
* Load the properties from the resource file if one is specified
*/
private void loadResourceFile(URL configurationResourceUrl, Properties hotRodConfiguration) {
if ( configurationResourceUrl != null ) {
try ( InputStream openStream = configurationResourceUrl.openStream() ) {
hotRodConfiguration.load( openStream );
}
catch (IOException e) {
throw log.failedLoadingHotRodConfigurationProperties( e );
}
}
}
/**
* Set the properties defined using the prefix {@link InfinispanRemoteProperties#HOT_ROD_CLIENT_PREFIX}
*
* @param configurationMap contains all the properties defined for OGM
* @param propertyReader read the value of a property
* @param hotRodConfiguration the Hot Rod configuration to update
*/
private void setAdditionalProperties(Map<?, ?> configurationMap, ConfigurationPropertyReader propertyReader, Properties hotRodConfiguration) {
// Programmatic properties override the resource file
for ( Entry<?, ?> property : configurationMap.entrySet() ) {
String key = (String) property.getKey();
if ( key.startsWith( HOT_ROD_CLIENT_PREFIX ) ) {
String hotRodProperty = key.substring( HOT_ROD_CLIENT_PREFIX.length() );
String value = propertyReader.property( key, String.class ).getValue();
if ( !ArrayHelper.contains( noPrefixProperties, hotRodProperty ) ) {
hotRodProperty = HOT_ROD_ORIGINAL_PREFIX + hotRodProperty;
}
hotRodConfiguration.setProperty( hotRodProperty, value );
}
}
}
/*
* We provide some default values in case some properties are not set
*/
private void setExpectedPropertiesIfNull(Properties hotRodConfiguration) {
for ( int i = 0; i < expectedValuesForHotRod.length; i++ ) {
String property = expectedValuesForHotRod[i][0];
String expectedValue = expectedValuesForHotRod[i][1];
if ( !hotRodConfiguration.containsKey( property ) ) {
hotRodConfiguration.setProperty( property, expectedValue );
}
}
}
private void validate(Properties hotRodConfiguration) {
for ( int i = 0; i < expectedValuesForHotRod.length; i++ ) {
String property = expectedValuesForHotRod[i][0];
String expectedValue = expectedValuesForHotRod[i][1];
String actualValue = trim( hotRodConfiguration.getProperty( property ) );
if ( !expectedValue.equals( actualValue ) && !expectedValue.equalsIgnoreCase( actualValue ) ) {
throw log.invalidConfigurationValue( property, expectedValue, actualValue );
}
}
}
private String trim(String property) {
if ( property == null ) {
return null;
}
return property.trim();
}
}