/*
* 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.protobuf;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hibernate.AssertionFailure;
import org.hibernate.ogm.datastore.infinispanremote.impl.InfinispanRemoteDatastoreProvider;
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.schema.SequenceTableDefinition;
import org.hibernate.ogm.datastore.infinispanremote.impl.schema.TableDefinition;
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.model.key.spi.IdSourceKeyMetadata;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.exceptions.HotRodClientException;
import org.infinispan.protostream.FileDescriptorSource;
public class SchemaDefinitions {
private static final Log LOG = LoggerFactory.getLogger();
private final String packageName;
private final Map<String,TableDefinition> definitionsByTableName = new HashMap<>();
private final Map<IdSourceKeyMetadata, SequenceTableDefinition> idSchemaPerMetadata = new HashMap<>();
private final Map<String, SequenceTableDefinition> idSchemaPerName = new HashMap<>();
//guarded by synchronization on this
private String cachedSchema = null;
public SchemaDefinitions(String packageName) {
this.packageName = packageName;
}
// N.B. all messages to the server need to be wrapped in /org/infinispan/protostream/message-wrapping.proto
// (both the schema definitions and the key/value pairs)
// This resource is defined in the Protostream jar.
// Typically this is transparently handled by using the Protostream codecs but be aware of it when bypassing Protostream.
public void deploySchema(String generatedProtobufName, RemoteCache<String, String> protobufCache, SchemaCapture schemaCapture, SchemaOverride schemaOverrideService) {
final String generatedProtoschema = schemaOverrideService == null ? generateProtoschema() : schemaOverrideService.createProtobufSchema();
try {
protobufCache.put( generatedProtobufName, generatedProtoschema );
LOG.successfulSchemaDeploy( generatedProtobufName );
}
catch (HotRodClientException hrce) {
throw LOG.errorAtSchemaDeploy( generatedProtobufName, hrce );
}
if ( schemaCapture != null ) {
schemaCapture.put( generatedProtobufName, generatedProtoschema );
}
}
private synchronized String generateProtoschema() {
if ( cachedSchema != null ) {
return cachedSchema;
}
TypeDeclarationsCollector typesDefCollector = new TypeDeclarationsCollector();
StringBuilder sb = new StringBuilder( 400 );
sb.append( "package " ).append( packageName ).append( ";\n" );
idSchemaPerMetadata.forEach( ( k, v ) -> v.exportProtobufEntry( sb ) );
definitionsByTableName.forEach( ( k, v ) -> v.collectTypeDefinitions( typesDefCollector ) );
typesDefCollector.exportProtobufEntries( sb );
definitionsByTableName.forEach( ( k, v ) -> v.exportProtobufEntry( sb ) );
String fullSchema = sb.toString();
LOG.generatedSchema( fullSchema );
this.cachedSchema = fullSchema;
return fullSchema;
}
public void registerTableDefinition(TableDefinition td) {
TableDefinition previous = definitionsByTableName.put( td.getTableName(), td );
if ( previous != null ) {
throw new AssertionFailure( "There should be no duplicate table definitions" );
}
}
public Set<String> getTableNames() {
Set<String> unionSet = new HashSet<>();
unionSet.addAll( definitionsByTableName.keySet() );
unionSet.addAll( idSchemaPerName.keySet() );
return Collections.unmodifiableSet( unionSet );
}
public Map<String,ProtoDataMapper> generateSchemaMappingAdapters(InfinispanRemoteDatastoreProvider provider,
SchemaDefinitions sd, OgmProtoStreamMarshaller marshaller) {
Map<String,ProtoDataMapper> adaptersCollector = new HashMap<>();
definitionsByTableName.forEach( ( k, v ) ->
adaptersCollector.put( k, v.createProtoDataMapper( provider.getCache( k ), sd, marshaller ) )
);
return Collections.unmodifiableMap( adaptersCollector );
}
public FileDescriptorSource asFileDescriptorSource() throws IOException {
FileDescriptorSource source = new FileDescriptorSource();
StringReader stringReader = new StringReader( generateProtoschema() );
source.addProtoFile( "ogm-generated", stringReader );
return source;
}
public void createSequenceSchemaDefinition(IdSourceKeyMetadata idSourceKeyMetadata, String protobufPackageName) {
SequenceTableDefinition std = new SequenceTableDefinition( idSourceKeyMetadata, protobufPackageName );
SequenceTableDefinition previous = idSchemaPerMetadata.put( idSourceKeyMetadata, std );
if ( previous != null ) {
throw new AssertionFailure( "There should be no duplicate definitions for SequenceTableDefinition instances" );
}
previous = idSchemaPerName.put( std.getName(), std );
if ( previous != null ) {
throw new AssertionFailure( "There should be no duplicate definitions for SequenceTableDefinition instances" );
}
}
public Map<String, SequenceTableDefinition> getSequenceDefinitions() {
return Collections.unmodifiableMap( idSchemaPerName );
}
public void validateSchema() {
for ( TableDefinition td : definitionsByTableName.values() ) {
td.validate();
}
}
}