/*
* 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.impl;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.hibernate.ogm.datastore.infinispanremote.InfinispanRemoteDialect;
import org.hibernate.ogm.datastore.infinispanremote.configuration.impl.InfinispanRemoteConfiguration;
import org.hibernate.ogm.datastore.infinispanremote.impl.protobuf.SchemaDefinitions;
import org.hibernate.ogm.datastore.infinispanremote.impl.protostream.OgmProtoStreamMarshaller;
import org.hibernate.ogm.datastore.infinispanremote.impl.protostream.ProtoDataMapper;
import org.hibernate.ogm.datastore.infinispanremote.impl.protostream.ProtostreamSerializerSetup;
import org.hibernate.ogm.datastore.infinispanremote.impl.schema.SequenceTableDefinition;
import org.hibernate.ogm.datastore.infinispanremote.impl.sequences.HotRodSequenceHandler;
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.datastore.spi.BaseDatastoreProvider;
import org.hibernate.ogm.datastore.spi.SchemaDefiner;
import org.hibernate.ogm.dialect.spi.GridDialect;
import org.hibernate.ogm.util.impl.EffectivelyFinal;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.service.spi.Startable;
import org.hibernate.service.spi.Stoppable;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.protostream.SerializationContext;
import org.infinispan.query.remote.client.ProtobufMetadataManagerConstants;
/**
* @author Sanne Grinovero
*/
public class InfinispanRemoteDatastoreProvider extends BaseDatastoreProvider
implements Startable, Stoppable, Configurable, ServiceRegistryAwareService {
private static final Log log = LoggerFactory.getLogger();
/**
* The org.infinispan.commons.marshall.Marshaller instance which shall be used
* by our Hot Rod client.
*/
private final OgmProtoStreamMarshaller marshaller = new OgmProtoStreamMarshaller();
// Only available during configuration
private InfinispanRemoteConfiguration config;
// The Hot Rod client; maintains TCP connections to the datagrid.
@EffectivelyFinal
private RemoteCacheManager hotrodClient;
//Useful to allow people to dump the generated schema,
//we use it to capture the schema in tests too.
@EffectivelyFinal
private SchemaCapture schemaCapture;
@EffectivelyFinal
private ServiceRegistryImplementor serviceRegistry;
@EffectivelyFinal
private SchemaOverride schemaOverrideService;
@EffectivelyFinal
private Set<String> mappedCacheNames;
//For each cache we have a schema and a set of encoders/decoders to the generated protobuf schema
@EffectivelyFinal
private Map<String,ProtoDataMapper> perCacheSchemaMappers;
@EffectivelyFinal
private HotRodSequenceHandler sequences;
@EffectivelyFinal
private SchemaDefinitions sd;
@EffectivelyFinal
private String schemaPackageName;
@Override
public Class<? extends GridDialect> getDefaultDialect() {
return InfinispanRemoteDialect.class;
}
@Override
public void start() {
hotrodClient = HotRodClientBuilder.builder().withConfiguration( config, marshaller ).build();
hotrodClient.start();
config = null; //no longer needed
}
public RemoteCacheManager getRemoteCacheManager() {
return hotrodClient;
}
@Override
public void stop() {
hotrodClient.stop();
}
@Override
public void configure(Map configurationValues) {
this.config = new InfinispanRemoteConfiguration();
this.config.initConfiguration( configurationValues, serviceRegistry );
this.schemaCapture = config.getSchemaCaptureService();
this.schemaOverrideService = config.getSchemaOverrideService();
this.schemaPackageName = config.getSchemaPackageName();
}
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
@Override
public Class<? extends SchemaDefiner> getSchemaDefinerType() {
return ProtobufSchemaInitializer.class;
}
public void registerSchemaDefinitions(SchemaDefinitions sd) {
this.sd = sd;
sd.validateSchema();
RemoteCache<String,String> protobufCache = getProtobufCache();
//FIXME make this name configurable & give it a sensible default:
final String generatedProtobufName = "Hibernate_OGM_Generated_schema.proto";
sd.deploySchema( generatedProtobufName, protobufCache, schemaCapture, schemaOverrideService );
this.sequences = new HotRodSequenceHandler( this, marshaller, sd.getSequenceDefinitions() );
setMappedCacheNames( sd );
startAndValidateCaches();
perCacheSchemaMappers = sd.generateSchemaMappingAdapters( this, sd, marshaller );
}
private void startAndValidateCaches() {
Set<String> failedCacheNames = new TreeSet<String>();
mappedCacheNames.forEach( cacheName -> {
RemoteCache<?,?> cache = hotrodClient.getCache( cacheName );
if ( cache == null ) {
failedCacheNames.add( cacheName );
}
} );
if ( failedCacheNames.size() > 1 ) {
throw log.expectedCachesNotDefined( failedCacheNames );
}
else if ( failedCacheNames.size() == 1 ) {
throw log.expectedCacheNotDefined( failedCacheNames.iterator().next() );
}
}
private void setMappedCacheNames(SchemaDefinitions sd) {
this.mappedCacheNames = sd.getTableNames();
}
private RemoteCache<String, String> getProtobufCache() {
return getCache( ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME );
}
@Override
public boolean allowsTransactionEmulation() {
// Hot Rod doesn't support "true" transaction yet
return true;
}
public String getProtobufPackageName() {
return schemaPackageName;
}
public Set<String> getMappedCacheNames() {
return mappedCacheNames;
}
public ProtoStreamMappingAdapter getDataMapperForCache(String cacheName) {
return perCacheSchemaMappers.get( cacheName );
}
public ProtostreamAssociationMappingAdapter getCollectionsDataMapper(String cacheName) {
return perCacheSchemaMappers.get( cacheName );
}
public HotRodSequenceHandler getSequenceHandler() {
return this.sequences;
}
public SerializationContext getSerializationContextForSequences(SequenceTableDefinition std) {
//This method is here so that we can cache / reuse these contexts ?
return ProtostreamSerializerSetup.buildSerializationContextForSequences( sd, std );
}
public <K, V> RemoteCache<K, V> getCache(String cacheName) {
RemoteCache<K,V> cache = hotrodClient.getCache( cacheName );
if ( cache == null ) {
throw log.expectedCacheNotDefined( cacheName );
}
return cache;
}
}