Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added parameterless ctor to the Serializers to allow load them from hazelcast standard config file. #453

Merged
merged 3 commits into from Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions bucket4j-hazelcast-all/bucket4j-hazelcast/pom.xml
Expand Up @@ -44,6 +44,12 @@
<version>${hazelcast.latest.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Expand Up @@ -46,6 +46,7 @@
import io.github.bucket4j.distributed.versioning.Version;
import io.github.bucket4j.grid.hazelcast.serialization.HazelcastEntryProcessorSerializer;
import io.github.bucket4j.grid.hazelcast.serialization.HazelcastOffloadableEntryProcessorSerializer;
import io.github.bucket4j.grid.hazelcast.serialization.SerializationUtilities;
import io.github.bucket4j.grid.hazelcast.serialization.SimpleBackupProcessorSerializer;
import io.github.bucket4j.distributed.serialization.InternalSerializationHelper;

Expand Down Expand Up @@ -137,19 +138,19 @@ protected CompletableFuture<Void> removeAsync(K key) {
public static void addCustomSerializers(SerializationConfig serializationConfig, final int typeIdBase) {
serializationConfig.addSerializerConfig(
new SerializerConfig()
.setImplementation(new HazelcastEntryProcessorSerializer(typeIdBase))
.setImplementation(new HazelcastEntryProcessorSerializer(SerializationUtilities.getSerializerTypeId(HazelcastEntryProcessorSerializer.class, typeIdBase)))
.setTypeClass(HazelcastEntryProcessor.class)
);

serializationConfig.addSerializerConfig(
new SerializerConfig()
.setImplementation(new SimpleBackupProcessorSerializer(typeIdBase + 1))
.setImplementation(new SimpleBackupProcessorSerializer(SerializationUtilities.getSerializerTypeId(SimpleBackupProcessorSerializer.class, typeIdBase)))
.setTypeClass(SimpleBackupProcessor.class)
);

serializationConfig.addSerializerConfig(
new SerializerConfig()
.setImplementation(new HazelcastOffloadableEntryProcessorSerializer(typeIdBase + 2))
.setImplementation(new HazelcastOffloadableEntryProcessorSerializer(SerializationUtilities.getSerializerTypeId(HazelcastOffloadableEntryProcessorSerializer.class, typeIdBase)))
.setTypeClass(HazelcastOffloadableEntryProcessor.class)
);

Expand Down
Expand Up @@ -35,6 +35,9 @@ public class HazelcastEntryProcessorSerializer implements StreamSerializer<Hazel
public HazelcastEntryProcessorSerializer(int typeId) {
this.typeId = typeId;
}
public HazelcastEntryProcessorSerializer() {
this.typeId = SerializationUtilities.getSerializerTypeId(this.getClass());
}

public Class<HazelcastEntryProcessor> getSerializableType() {
return HazelcastEntryProcessor.class;
Expand Down
Expand Up @@ -36,6 +36,9 @@ public class HazelcastOffloadableEntryProcessorSerializer implements StreamSeria
public HazelcastOffloadableEntryProcessorSerializer(int typeId) {
this.typeId = typeId;
}
public HazelcastOffloadableEntryProcessorSerializer() {
this.typeId = SerializationUtilities.getSerializerTypeId(this.getClass());
}

public Class<HazelcastEntryProcessor> getSerializableType() {
return HazelcastEntryProcessor.class;
Expand Down
@@ -0,0 +1,66 @@
package io.github.bucket4j.grid.hazelcast.serialization;

/**
* Thrown to indicate that an expected Parameter (both from System Parameters or Environment Variables)
* is invalid.
*
* @author MonDeveloper
*/
public class InvalidConfigurationParameterException extends RuntimeException {

/**
* Constructs an <code>IllegalArgumentException</code> with no
* detail message.
*/
public InvalidConfigurationParameterException() {
super();
}

/**
* Constructs an <code>IllegalArgumentException</code> with the
* specified detail message.
*
* @param s the detail message.
*/
public InvalidConfigurationParameterException(String s) {
super(s);
}

/**
* Constructs a new exception with the specified detail message and
* cause.
*
* <p>Note that the detail message associated with <code>cause</code> is
* <i>not</i> automatically incorporated in this exception's detail
* message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link Throwable#getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link Throwable#getCause()} method). (A {@code null} value
* is permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.5
*/
public InvalidConfigurationParameterException(String message, Throwable cause) {
super(message, cause);
}

/**
* Constructs a new exception with the specified cause and a detail
* message of {@code (cause==null ? null : cause.toString())} (which
* typically contains the class and detail message of {@code cause}).
* This constructor is useful for exceptions that are little more than
* wrappers for other throwables (for example, {@link
* java.security.PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link Throwable#getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.5
*/
public InvalidConfigurationParameterException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,66 @@
package io.github.bucket4j.grid.hazelcast.serialization;

/**
* Thrown to indicate that an expected Parameter (both from System Parameters or Environment Variables)
* is missing.
*
* @author MonDeveloper
*/
public class MissingConfigurationParameterException extends RuntimeException {

/**
* Constructs an <code>IllegalArgumentException</code> with no
* detail message.
*/
public MissingConfigurationParameterException() {
super();
}

/**
* Constructs an <code>IllegalArgumentException</code> with the
* specified detail message.
*
* @param s the detail message.
*/
public MissingConfigurationParameterException(String s) {
super(s);
}

/**
* Constructs a new exception with the specified detail message and
* cause.
*
* <p>Note that the detail message associated with <code>cause</code> is
* <i>not</i> automatically incorporated in this exception's detail
* message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link Throwable#getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link Throwable#getCause()} method). (A {@code null} value
* is permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.5
*/
public MissingConfigurationParameterException(String message, Throwable cause) {
super(message, cause);
}

/**
* Constructs a new exception with the specified cause and a detail
* message of {@code (cause==null ? null : cause.toString())} (which
* typically contains the class and detail message of {@code cause}).
* This constructor is useful for exceptions that are little more than
* wrappers for other throwables (for example, {@link
* java.security.PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link Throwable#getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.5
*/
public MissingConfigurationParameterException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,84 @@
package io.github.bucket4j.grid.hazelcast.serialization;

import com.hazelcast.internal.util.StringUtil;
import com.hazelcast.nio.serialization.Serializer;

import java.text.MessageFormat;
import java.util.*;
import java.util.function.Supplier;

public class SerializationUtilities {
public static final String TYPE_ID_BASE_PROP_NAME = "Bucket4jSerializers_TypeId_Base";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to rename to "bucket4j.hazelcast.serializer.type_id_base"

private static final Map<Class<? extends Serializer>, Integer> serializerTypeIdOffsets = Map.ofEntries(
new AbstractMap.SimpleEntry<Class<? extends Serializer>, Integer>(HazelcastEntryProcessorSerializer.class, 0),
new AbstractMap.SimpleEntry<Class<? extends Serializer>, Integer>(SimpleBackupProcessorSerializer.class, 1),
new AbstractMap.SimpleEntry<Class<? extends Serializer>, Integer>(HazelcastOffloadableEntryProcessorSerializer.class, 2)
);

public static int getSerializerTypeId(Class<? extends Serializer> serializerType) {
var typeIdBase = getSerializersTypeIdBase();

if (typeIdBase.isEmpty()) {
String msg = MessageFormat.format("Missing TypeIdBase number, impossible to load Bucket4j custom serializers. It must be provided in form of Environment Variable or System Property, both using the following key: [{0}]", TYPE_ID_BASE_PROP_NAME);
throw new MissingConfigurationParameterException(msg);
}

return getSerializerTypeId(serializerType, typeIdBase.get());
}

public static int getSerializerTypeId(Class<? extends Serializer> serializerType, int typeIdBase) {
return typeIdBase + SerializationUtilities.getSerializerTypeIdOffset(serializerType);
}

private static Optional<Integer> getSerializersTypeIdBase() {
return Optional.ofNullable(
getSerializerTypeIdBaseFromSystemProperty()
.orElseGet(() -> getSerializerTypeIdBaseFromEnvironmentVariable()
.orElseGet(() -> null)));
}

private static int getSerializerTypeIdOffset(Class<? extends Serializer> serializerType) {
if (serializerTypeIdOffsets.containsKey(serializerType)) {
return serializerTypeIdOffsets.get(serializerType);
} else {
String msg = MessageFormat.format("The internal configuration does not include any offset for the serializerType [{0}]", serializerType);
throw new IllegalStateException(msg);
}
}

private static Optional<Integer> getSerializerTypeIdBaseFromEnvironmentVariable() {
return getPropertyValueFromExternal(() -> System.getenv(SerializationUtilities.TYPE_ID_BASE_PROP_NAME), "Environment Variable");
vladimir-bukhtoyarov marked this conversation as resolved.
Show resolved Hide resolved
}

private static Optional<Integer> getSerializerTypeIdBaseFromSystemProperty() {
return getPropertyValueFromExternal(() -> System.getProperty(SerializationUtilities.TYPE_ID_BASE_PROP_NAME), "System Property");
}

private static Optional<Integer> getPropertyValueFromExternal(Supplier<String> typeIdSupplier, String source) {
Optional<Integer> retVal = Optional.empty();

String typeIdBaseStr = typeIdSupplier.get();
if (!StringUtil.isNullOrEmptyAfterTrim(typeIdBaseStr)) {
retVal = parseInteger(typeIdBaseStr);
if (retVal.isEmpty()) {
String msg = MessageFormat.format("The {0} [{1}] has an invalid format. It must be a positive Integer.", source, TYPE_ID_BASE_PROP_NAME);
throw new InvalidConfigurationParameterException(msg);
}
}

return retVal;
}

private static Optional<Integer> parseInteger(String strNum) {
Optional<Integer> retVal = Optional.empty();
if (null != strNum) {
try {
Integer d = Integer.parseInt(strNum.trim());
retVal = Optional.of(d);
} catch (NumberFormatException nfe) {
retVal = Optional.empty();
}
}
return retVal;
}
}
Expand Up @@ -35,6 +35,9 @@ public class SimpleBackupProcessorSerializer implements StreamSerializer<SimpleB
public SimpleBackupProcessorSerializer(int typeId) {
this.typeId = typeId;
}
public SimpleBackupProcessorSerializer() {
this.typeId = SerializationUtilities.getSerializerTypeId(this.getClass());
}

public Class<SimpleBackupProcessor> getSerializableType() {
return SimpleBackupProcessor.class;
Expand Down