Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.hazelcast.internal.serialization.impl.compact.CompactStreamSerializer Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.internal.serialization.impl.compact;
import com.hazelcast.config.CompactSerializationConfig;
import com.hazelcast.config.CompactSerializationConfigAccessor;
import com.hazelcast.config.InvalidConfigurationException;
import com.hazelcast.core.ManagedContext;
import com.hazelcast.internal.nio.BufferObjectDataInput;
import com.hazelcast.internal.nio.BufferObjectDataOutput;
import com.hazelcast.internal.nio.ClassLoaderUtil;
import com.hazelcast.internal.serialization.impl.AbstractSerializationService;
import com.hazelcast.internal.serialization.impl.InternalGenericRecord;
import com.hazelcast.internal.serialization.impl.compact.record.JavaRecordSerializer;
import com.hazelcast.internal.util.TriTuple;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.nio.serialization.FieldKind;
import com.hazelcast.nio.serialization.HazelcastSerializationException;
import com.hazelcast.nio.serialization.StreamSerializer;
import com.hazelcast.nio.serialization.compact.CompactSerializer;
import com.hazelcast.nio.serialization.genericrecord.GenericRecord;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.hazelcast.internal.serialization.impl.FieldOperations.fieldOperations;
import static com.hazelcast.internal.serialization.impl.SerializationConstants.TYPE_COMPACT;
/**
* Serializer for compact serializable objects.
*
* This can be used to serialize objects that either
*
* registered as compact serializable through configuration.
* cannot be serialized with any other mechanism and they can be serialized
* reflectively, without any configuration.
*
*/
public class CompactStreamSerializer implements StreamSerializer {
private final Map classToRegistrationMap = new ConcurrentHashMap<>();
private final Map typeNameToRegistrationMap = new ConcurrentHashMap<>();
private final Map classToSchemaMap = new ConcurrentHashMap<>();
private final ReflectiveCompactSerializer reflectiveSerializer = new ReflectiveCompactSerializer(this);
private final JavaRecordSerializer javaRecordSerializer = new JavaRecordSerializer(this);
private final SchemaService schemaService;
private final ManagedContext managedContext;
private final ClassLoader classLoader;
private final AbstractSerializationService serializationService;
public CompactStreamSerializer(AbstractSerializationService serializationService,
CompactSerializationConfig compactSerializationConfig,
ManagedContext managedContext, SchemaService schemaService,
ClassLoader classLoader) {
this.serializationService = serializationService;
this.managedContext = managedContext;
this.schemaService = schemaService;
this.classLoader = classLoader;
registerSerializers(compactSerializationConfig);
registerDeclarativeConfigSerializers(compactSerializationConfig);
registerDeclarativeConfigClasses(compactSerializationConfig);
}
/**
* Returns true if the {@code clazz} is registered as compact serializable
* through configuration.
*/
public boolean isRegisteredAsCompact(Class clazz) {
return classToRegistrationMap.containsKey(clazz);
}
public Collection getCompactSerializableClasses() {
return classToRegistrationMap.keySet();
}
public boolean canBeSerializedAsCompact(Class> clazz) {
return serializationService.serializerForClass(clazz, false) instanceof CompactStreamSerializerAdapter;
}
@Override
public int getTypeId() {
return TYPE_COMPACT;
}
//========================== WRITE =============================//
@Override
public void write(ObjectDataOutput out, Object o) throws IOException {
assert out instanceof BufferObjectDataOutput;
BufferObjectDataOutput bufferObjectDataOutput = (BufferObjectDataOutput) out;
write(bufferObjectDataOutput, o, false);
}
void write(BufferObjectDataOutput out, Object o, boolean includeSchemaOnBinary) throws IOException {
if (o instanceof CompactGenericRecord compactGenericRecord) {
writeGenericRecord(out, compactGenericRecord, includeSchemaOnBinary);
} else {
writeObject(out, o, includeSchemaOnBinary);
}
}
void writeGenericRecord(BufferObjectDataOutput output, CompactGenericRecord record,
boolean includeSchemaOnBinary) throws IOException {
Schema schema = record.getSchema();
putToSchemaService(includeSchemaOnBinary, schema);
writeSchema(output, includeSchemaOnBinary, schema);
DefaultCompactWriter writer = new DefaultCompactWriter(this, output, schema, includeSchemaOnBinary);
Collection fields = schema.getFields();
for (FieldDescriptor fieldDescriptor : fields) {
String fieldName = fieldDescriptor.getFieldName();
FieldKind fieldKind = fieldDescriptor.getKind();
fieldOperations(fieldKind).writeFieldFromRecordToWriter(writer, record, fieldName);
}
writer.end();
}
public List allSchemas() {
return List.copyOf(classToSchemaMap.values());
}
private void putToSchemaService(boolean includeSchemaOnBinary, Schema schema) {
if (!includeSchemaOnBinary) {
schemaService.put(schema);
}
}
public void writeObject(BufferObjectDataOutput out, Object o, boolean includeSchemaOnBinary) throws IOException {
Class> aClass = o.getClass();
CompactSerializableRegistration registration = getOrCreateRegistration(aClass);
Schema schema = classToSchemaMap.get(aClass);
if (schema == null) {
schema = buildSchema(registration, o);
putToSchemaService(includeSchemaOnBinary, schema);
classToSchemaMap.put(aClass, schema);
}
writeSchema(out, includeSchemaOnBinary, schema);
DefaultCompactWriter writer = new DefaultCompactWriter(this, out, schema, includeSchemaOnBinary);
registration.getSerializer().write(writer, o);
writer.end();
}
private void writeSchema(BufferObjectDataOutput out, boolean includeSchemaOnBinary, Schema schema) throws IOException {
out.writeLong(schema.getSchemaId());
if (includeSchemaOnBinary) {
int sizeOfSchemaPosition = out.position();
out.writeInt(0);
int schemaBeginPos = out.position();
out.writeObject(schema);
int schemaEndPosition = out.position();
out.writeInt(sizeOfSchemaPosition, schemaEndPosition - schemaBeginPos);
}
}
//========================== READ =============================//
@Override
public Object read(@Nonnull ObjectDataInput in) throws IOException {
BufferObjectDataInput input = (BufferObjectDataInput) in;
return read(input, false);
}
Object read(BufferObjectDataInput input, boolean schemaIncludedInBinary) throws IOException {
Schema schema = getOrReadSchema(input, schemaIncludedInBinary);
CompactSerializableRegistration registration = getOrCreateRegistration(schema.getTypeName());
if (registration == CompactSerializableRegistration.GENERIC_RECORD_REGISTRATION) {
// We have tried to load class via class loader, it did not work.
// We are returning a GenericRecord.
return readGenericRecord(input, schema, schemaIncludedInBinary);
}
DefaultCompactReader reader = new DefaultCompactReader(this, input, schema,
registration.getClazz(), schemaIncludedInBinary);
Object object = registration.getSerializer().read(reader);
return managedContext != null ? managedContext.initialize(object) : object;
}
private Schema getOrReadSchema(ObjectDataInput input, boolean schemaIncludedInBinary) throws IOException {
long schemaId = input.readLong();
Schema schema = schemaService.get(schemaId);
if (schema != null) {
if (schemaIncludedInBinary) {
int sizeOfSchema = input.readInt();
input.skipBytes(sizeOfSchema);
}
return schema;
}
if (schemaIncludedInBinary) {
//sizeOfSchema
input.readInt();
schema = input.readObject();
long incomingSchemaId = schema.getSchemaId();
if (schemaId != incomingSchemaId) {
throw new HazelcastSerializationException("Invalid schema id found. Expected " + schemaId
+ ", actual " + incomingSchemaId + " for schema " + schema);
}
return schema;
}
throw new HazelcastSerializationException("The schema can not be found with id " + schemaId);
}
private CompactSerializableRegistration getOrCreateRegistration(Class clazz) {
return classToRegistrationMap.computeIfAbsent(clazz, aClass -> {
CompactSerializer serializer = javaRecordSerializer.isRecord(aClass) ? javaRecordSerializer : reflectiveSerializer;
return new CompactSerializableRegistration(aClass, aClass.getName(), serializer);
});
}
private CompactSerializableRegistration getOrCreateRegistration(String typeName) {
CompactSerializableRegistration currentRegistration = typeNameToRegistrationMap.get(typeName);
if (currentRegistration != null) {
return currentRegistration;
}
// Execute potentially long-lasting operation outside CHM lock in computeIfAbsent.
// Some special classloaders (e.g. JetClassLoader) may try to access external resources
// and require other threads.
// We might try to load the same class multiple times in parallel but this is not a problem.
CompactSerializableRegistration newRegistration = getOrCreateRegistration0(typeName);
// Registration might have been created by a concurrent thread.
// If so, use that one instead.
return typeNameToRegistrationMap.computeIfAbsent(typeName, k -> newRegistration);
}
private CompactSerializableRegistration getOrCreateRegistration0(String typeName) {
Class> clazz;
try {
// When the registration does not exist, we treat typeName as className
// to check if there is a class with the given name in the classpath.
clazz = ClassLoaderUtil.loadClass(classLoader, typeName);
} catch (Exception e) {
// There is no such class that has typeName as its name.
// We should try to read this as GenericRecord. We are
// returning this registration here to remember that we
// should read instances of this typeName as GenericRecords,
// instead of trying to load a class with that name over
// and over.
return CompactSerializableRegistration.GENERIC_RECORD_REGISTRATION;
}
return getOrCreateRegistration(clazz);
}
private GenericRecord readGenericRecord(BufferObjectDataInput input, Schema schema, boolean schemaIncludedInBinary) {
CompactInternalGenericRecord record =
new CompactInternalGenericRecord(this, input, schema, null, schemaIncludedInBinary);
Collection fields = schema.getFields();
DeserializedSchemaBoundGenericRecordBuilder builder = new DeserializedSchemaBoundGenericRecordBuilder(schema);
for (FieldDescriptor fieldDescriptor : fields) {
String fieldName = fieldDescriptor.getFieldName();
FieldKind fieldKind = fieldDescriptor.getKind();
builder.write(fieldName, record.readAny(fieldName), fieldKind);
}
return builder.build();
}
public GenericRecord readGenericRecord(ObjectDataInput in, boolean schemaIncludedInBinary) throws IOException {
Schema schema = getOrReadSchema(in, schemaIncludedInBinary);
BufferObjectDataInput input = (BufferObjectDataInput) in;
return readGenericRecord(input, schema, schemaIncludedInBinary);
}
public InternalGenericRecord readAsInternalGenericRecord(ObjectDataInput in) throws IOException {
Schema schema = getOrReadSchema(in, false);
BufferObjectDataInput input = (BufferObjectDataInput) in;
return new CompactInternalGenericRecord(this, input, schema, null, false);
}
private void registerSerializers(CompactSerializationConfig compactSerializationConfig) {
Map> registrations
= CompactSerializationConfigAccessor.getRegistrations(compactSerializationConfig);
for (TriTuple registration : registrations.values()) {
Class clazz = registration.element1;
String typeName = registration.element2;
CompactSerializer serializer = registration.element3;
if (serializer == null) {
if (javaRecordSerializer.isRecord(clazz)) {
serializer = javaRecordSerializer;
} else {
serializer = reflectiveSerializer;
}
}
CompactSerializableRegistration serializableRegistration
= new CompactSerializableRegistration(clazz, typeName, serializer);
saveRegistration(serializableRegistration);
}
}
private void saveRegistration(CompactSerializableRegistration registration) {
Class clazz = registration.getClazz();
CompactSerializableRegistration existing = classToRegistrationMap.putIfAbsent(clazz, registration);
if (existing != null) {
throw new InvalidConfigurationException("Duplicate serializer registrations "
+ "are found for the class '" + clazz + "'. Make sure only one "
+ "Compact serializer is registered for the same class. "
+ "Existing serializer: " + existing.getSerializer() + ", "
+ "new serializer: " + registration.getSerializer());
}
String typeName = registration.getTypeName();
existing = typeNameToRegistrationMap.putIfAbsent(typeName, registration);
if (existing != null) {
throw new InvalidConfigurationException("Duplicate serializer registrations "
+ "are found for the type name '" + typeName + "'. Make sure only one "
+ "Compact serializer is registered for the same type name. "
+ "Existing serializer: " + existing.getSerializer() + ", "
+ "new serializer: " + registration.getSerializer());
}
}
private void registerDeclarativeConfigSerializers(CompactSerializationConfig config) {
List serializerClassNames = CompactSerializationConfigAccessor.getSerializerClassNames(config);
for (String className : serializerClassNames) {
CompactSerializer serializer;
try {
serializer = ClassLoaderUtil.newInstance(classLoader, className);
} catch (Exception e) {
throw new InvalidConfigurationException("Cannot create an instance "
+ "of the Compact serializer '" + className + "'.");
}
CompactSerializableRegistration registration = new CompactSerializableRegistration(
serializer.getCompactClass(),
serializer.getTypeName(),
serializer
);
saveRegistration(registration);
}
}
private void registerDeclarativeConfigClasses(CompactSerializationConfig config) {
List compactSerializableClassNames
= CompactSerializationConfigAccessor.getCompactSerializableClassNames(config);
for (String className : compactSerializableClassNames) {
Class clazz;
try {
clazz = ClassLoaderUtil.loadClass(classLoader, className);
} catch (ClassNotFoundException e) {
throw new InvalidConfigurationException("Cannot load the Compact "
+ "serializable class '" + className + "'.");
}
CompactSerializer serializer;
if (javaRecordSerializer.isRecord(clazz)) {
serializer = javaRecordSerializer;
} else {
serializer = reflectiveSerializer;
}
CompactSerializableRegistration registration = new CompactSerializableRegistration(
clazz,
className,
serializer
);
saveRegistration(registration);
}
}
public Schema extractSchema(BufferObjectDataInput objectDataInput) throws IOException {
return getOrReadSchema(objectDataInput, false);
}
public Schema extractSchema(Object o) {
Class> aClass = o.getClass();
Schema schema = classToSchemaMap.get(aClass);
if (schema == null) {
CompactSerializableRegistration registration = getOrCreateRegistration(aClass);
schema = buildSchema(registration, o);
schemaService.putLocal(schema);
classToSchemaMap.put(aClass, schema);
return schema;
}
return schema;
}
private static Schema buildSchema(CompactSerializableRegistration registration, Object o) {
SchemaWriter writer = new SchemaWriter(registration.getTypeName());
registration.getSerializer().write(writer, o);
return writer.build();
}
}