/* * 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.util.HashMap; import java.util.Map; import org.hibernate.ogm.datastore.infinispanremote.impl.VersionedAssociation; import org.hibernate.ogm.datastore.infinispanremote.impl.protostream.MainOgmCoDec; import org.hibernate.ogm.datastore.infinispanremote.impl.protostream.ProtostreamAssociationPayload; import org.hibernate.ogm.datastore.infinispanremote.impl.protostream.ProtostreamId; import org.hibernate.ogm.datastore.infinispanremote.impl.protostream.ProtostreamPayload; import org.hibernate.ogm.datastore.map.impl.MapTupleSnapshot; import org.hibernate.ogm.dialect.spi.TupleContext; import org.hibernate.ogm.model.key.spi.EntityKey; import org.hibernate.ogm.model.spi.Tuple; import org.infinispan.client.hotrod.RemoteCache; import org.infinispan.protostream.MessageMarshaller.ProtoStreamReader; import org.infinispan.protostream.MessageMarshaller.ProtoStreamWriter; public final class CompositeProtobufCoDec implements MainOgmCoDec { private final String tableName; private final String protobufTypeName; private final String protobufIdTypeName; private final RemoteCache remoteCache; private final ProtofieldAccessorSet keyFields; private final ProtofieldAccessorSet valueFields; private final SchemaDefinitions sd; public CompositeProtobufCoDec(String tableName, String protobufTypeName, String protobufIdTypeName, ProtofieldAccessorSet keyFields, ProtofieldAccessorSet valueFields, RemoteCache remoteCache, SchemaDefinitions sd) { this.tableName = tableName; this.protobufTypeName = protobufTypeName; this.protobufIdTypeName = protobufIdTypeName; this.remoteCache = remoteCache; this.keyFields = keyFields; this.valueFields = valueFields; this.sd = sd; } @Override public RemoteCache getLinkedCache() { return remoteCache; } @Override public ProtostreamId readProtostreamId(ProtoStreamReader reader) throws IOException { final int size = keyFields.size(); final String[] columnNames = new String[size]; final Object[] columnValues = new Object[size]; for ( int i = 0; i < size; i++ ) { final UnsafeProtofield protoField = keyFields.getDecoderByListOrder( i ); columnNames[i] = protoField.getColumnName(); columnValues[i] = protoField.read( reader ); } return createIdPayload( columnNames, columnValues ); } @Override public ProtostreamId createIdPayload(String[] columnNames, Object[] columnValues) { assert verifyAllColumnNamesArePartOfId( columnNames ); return new ProtostreamId( columnNames, columnValues ); } private boolean verifyAllColumnNamesArePartOfId(String[] columnNames) { for ( String name : columnNames ) { if ( ! keyFields.columnNameExists( name ) ) { return false; } } return true; } @Override public void writeIdTo(ProtoStreamWriter writer, ProtostreamId id) throws IOException { final int size = id.columnNames.length; for ( int i = 0; i < size; i++ ) { final String columnName = id.columnNames[i]; UnsafeProtofield protofieldWriter = keyFields.getDecoderByColumnName( columnName ); if ( protofieldWriter == null ) { //Sometimes the Association Key contains columns beyond the strictly required ones for the key continue; } protofieldWriter.writeTo( writer, id.columnValues[i] ); } } @Override public ProtostreamPayload readPayloadFrom(ProtoStreamReader reader) throws IOException { final int size = valueFields.size(); Map mapTuple = new HashMap<>( size ); for ( int i = 0; i < size; i++ ) { final UnsafeProtofield protoField = valueFields.getDecoderByListOrder( i ); final String column = protoField.getColumnName(); final Object value = protoField.read( reader ); if ( value != null ) { mapTuple.put( column, value ); } } MapTupleSnapshot loadedSnapshot = new MapTupleSnapshot( mapTuple ); return new ProtostreamPayload( loadedSnapshot ); } @Override public void writePayloadTo(ProtoStreamWriter writer, ProtostreamPayload payload) { //N.B. we iterate in order by tag number of the protobuf field, as this affects encoding performance //This implies we might be requesting columns which are not defined in the payload valueFields.forEachProtostreamMappedField( f -> { Object columnValue = payload.getColumnValue( f.getColumnName() ); if ( columnValue != null ) { f.writeTo( writer, columnValue ); } } ); } @Override public ProtostreamPayload createValuePayload(Tuple tuple) { return new ProtostreamPayload( tuple ); } @Override public String getProtobufTypeName() { return protobufTypeName; } @Override public String getIdProtobufTypeName() { return protobufIdTypeName; } @Override public ProtostreamAssociationPayload createAssociationPayload(EntityKey key, VersionedAssociation assoc, TupleContext tupleContext) { return new ProtostreamAssociationPayload( assoc ); } @Override public String convertColumnNameToFieldName(String columnName) { return valueFields.getDecoderByColumnName( columnName ).getProtobufName(); } @Override public String[] listIdColumnNames() { return keyFields.getColumnNames(); } }