Skip to content

Commit

Permalink
Merge pull request #4 from arago/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
tryptichon authored Sep 30, 2021
2 parents 6bb7d81 + cf76322 commit 770011c
Show file tree
Hide file tree
Showing 18 changed files with 783 additions and 201 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# v0.2.1

`collections`
* Refactoring of ExpiringStore

`json`
* Faster cloning in JsonTools
* Add clone with TypeReference
* Rename ambiguous calls

# v0.1.3

* Because of deployment tests. Code has not changed.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.3
0.2.1
390 changes: 390 additions & 0 deletions arago-eclipse-formatting.xml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions collections/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# v0.2.1

* Refactoring of ExpiringStore

# v0.1.0

* Initial release
Expand Down
3 changes: 1 addition & 2 deletions collections/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
<parent>
<artifactId>java-project</artifactId>
<groupId>co.arago</groupId>
<version>0.1.3</version>
<version>0.2.1</version>
</parent>

<groupId>co.arago.util</groupId>
<artifactId>collections</artifactId>
<version>0.1.3</version>

<description>
Utility library for collections.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package co.arago.util.collections.expiringstore;

import co.arago.util.collections.expiringstore.exceptions.StoreItemExistsException;
import co.arago.util.collections.expiringstore.exceptions.StoreItemExpiredException;
import co.arago.util.collections.expiringstore.messages.ExpiringMessage;

import java.time.Instant;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
* Abstract root class for expiring stores.
* <p>
* Contains the store items with an "expiresAt" after which the items are automatically removed from the store.
*
* @param <T> Type of items to store
* @param <M> Type of ExpiringMessages to use.
*/
public abstract class AbstractExpiringStore<T, M extends ExpiringMessage<T>> implements AutoCloseable {

protected static final AtomicInteger counter = new AtomicInteger(0);

private final Timer timer;
private final String name;

protected final Map<String, M> storeMap = new HashMap<>();

/**
* Constructor
*
* @param name Name of the ExpiringStore.
*/
public AbstractExpiringStore(String name) {
this.name = name;
this.timer = new Timer("Timer for " + name);
}

/**
* Add the message
*
* @param expiringMessage Message to add
* @throws StoreItemExistsException When the expiringMessage already exists.
*/
protected void addInternal(M expiringMessage) throws StoreItemExistsException {
M existingMessage = storeMap.putIfAbsent(expiringMessage.getId(), expiringMessage);
if (existingMessage != null) {
throw new StoreItemExistsException("Not adding " + expiringMessage.getMessage().getClass().getSimpleName() +
" " + expiringMessage.getId() + " because it already exists.");
}
}

/**
* Put the message, allowing to overwrite messages with the same id. Overwritten messages will be canceled.
*
* @param expiringMessage Message to put
*/
protected void putInternal(M expiringMessage) {
M existingMessage = storeMap.put(expiringMessage.getId(), expiringMessage);
if (existingMessage != null)
existingMessage.cancel();
}

/**
* Add a message to the store if it does not exist
*
* @param expiresAt Timestamp after which the message expires
* @param id The unique id of the message
* @param message The message itself to store
* @throws StoreItemExpiredException When the expiresAt is already expired.
* @throws StoreItemExistsException When the message already exists.
*/
public abstract void add(Instant expiresAt, String id, T message) throws StoreItemExpiredException, StoreItemExistsException;

/**
* Put a message to the store, possibly overwriting existing messages.
*
* @param expiresAt Timestamp after which the message expires
* @param id The unique id of the message
* @param message The message itself to store
* @throws StoreItemExpiredException When the expiresAt is already expired.
*/
public abstract void put(Instant expiresAt, String id, T message) throws StoreItemExpiredException;

/**
* Remove a message from the storeMap and cancel its TimerTask.
*
* @param id Id of the message
*/
public synchronized void remove(String id) {
M message = storeMap.remove(id);
if (message != null)
message.cancel();
}

/**
* Getter
*
* @param id Id of the message
* @return The stored message
*/
public synchronized T get(String id) {
M message = storeMap.get(id);
return (message != null ? message.getMessage() : null);
}

/**
* Getter
*
* @return The name of this store.
*/
public String getName() {
return name;
}

/**
* Schedule an expiry with the {@link #timer},
*
* @param timerTask The timerTask to schedule.
* @param date The timestamp when the timerTask will be called.
*/
public void schedule(TimerTask timerTask, Date date) {
timer.schedule(timerTask, date);
}

/**
* Cancel the {@link #timer} and clear the {@link #storeMap}. This Store cannot be used thereafter.
*/
@Override
public synchronized void close() {
timer.cancel();
storeMap.clear();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import co.arago.util.collections.expiringstore.exceptions.StoreItemExistsException;
import co.arago.util.collections.expiringstore.exceptions.StoreItemExpiredException;
import co.arago.util.collections.expiringstore.messages.ExpiringRetryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -13,32 +14,11 @@
*
* @param <T> Type of items to store
*/
public class ExpiringRetryStore<T> extends ExpiringStore<T> {
public class ExpiringRetryStore<T> extends AbstractExpiringStore<T, ExpiringRetryMessage<T>> {

static final int DEFAULT_RETRIES = 4;
private final static Logger log = LoggerFactory.getLogger(ExpiringRetryStore.class);

/**
* A message with an expire and retry. The item is removed from its container when timeout has run out.
*/
protected class ExpiringRetryMessage extends ExpiringMessage {
protected int retriesLeft;

/**
* Constructor
*
* @param expiresAt Timestamp after which the message expires and will be removed from the
* {@link ExpiringStore#storeMap}.
* @param id The unique id of the message
* @param message The original message
* @param maxRetries Max retries (default is 4)
* @throws StoreItemExpiredException When the expiresAt is already expired.
*/
ExpiringRetryMessage(Instant expiresAt, String id, T message, int maxRetries) throws StoreItemExpiredException {
super(expiresAt, id, message);
this.retriesLeft = maxRetries;
}
}

protected int retriesLeft;

Expand All @@ -57,6 +37,17 @@ public ExpiringRetryStore() {
* @param maxRetries Maximum amount of retries.
*/
public ExpiringRetryStore(int maxRetries) {
this(maxRetries, "RetryStore-" + counter.incrementAndGet());
}

/**
* Constructor
*
* @param maxRetries Maximum amount of retries.
* @param name Name of this store.
*/
public ExpiringRetryStore(int maxRetries, String name) {
super(name);
this.retriesLeft = maxRetries;
}

Expand All @@ -70,8 +61,32 @@ public ExpiringRetryStore(int maxRetries) {
* @throws StoreItemExpiredException When the expiresAt is already expired.
* @throws StoreItemExistsException When the message already exists.
*/
public synchronized void add(Instant expiresAt, String id, T message) throws StoreItemExpiredException, StoreItemExistsException {
addInternal(new ExpiringRetryMessage(expiresAt, id, message, retriesLeft));
public synchronized void add(
Instant expiresAt,
String id,
T message
) throws StoreItemExpiredException, StoreItemExistsException {
addInternal(new ExpiringRetryMessage<T>(this, expiresAt, id, message, retriesLeft));
}

/**
* Add a message to the store if it does not exist. This message will also be removed when its "retriesLeft" is
* exhausted via {@link #retryGet(String)}.
*
* @param expiresAt Timestamp after which the message expires
* @param id The unique id of the message
* @param message The message itself to store
* @param retriesLeft Explicitly set the retries for this message.
* @throws StoreItemExpiredException When the expiresAt is already expired.
* @throws StoreItemExistsException When the message already exists.
*/
public synchronized void add(
Instant expiresAt,
String id,
T message,
int retriesLeft
) throws StoreItemExpiredException, StoreItemExistsException {
addInternal(new ExpiringRetryMessage<T>(this, expiresAt, id, message, retriesLeft));
}

/**
Expand All @@ -84,34 +99,52 @@ public synchronized void add(Instant expiresAt, String id, T message) throws Sto
* @param message The message itself to store
* @throws StoreItemExpiredException When the expiresAt is already expired.
*/
public synchronized void put(Instant expiresAt, String id, T message) throws StoreItemExpiredException {
putInternal(new ExpiringRetryMessage(expiresAt, id, message, retriesLeft));
public synchronized void put(
Instant expiresAt,
String id,
T message
) throws StoreItemExpiredException {
putInternal(new ExpiringRetryMessage<T>(this, expiresAt, id, message, retriesLeft));
}

/**
* Put a message to the store, possibly overwriting existing messages with the same id.
* This message will also be removed when its "retriesLeft" is
* exhausted via {@link #retryGet(String)}.
*
* @param expiresAt Timestamp after which the message expires
* @param id The unique id of the message
* @param message The message itself to store
* @param retriesLeft Explicitly set the retries for this message.
* @throws StoreItemExpiredException When the expiresAt is already expired.
*/
public synchronized void put(
Instant expiresAt,
String id,
T message,
int retriesLeft
) throws StoreItemExpiredException {
putInternal(new ExpiringRetryMessage<T>(this, expiresAt, id, message, retriesLeft));
}

/**
* Each call decreases {@link ExpiringRetryMessage#retriesLeft} for the message with this id.
* Each call uses {@link ExpiringRetryMessage#getAndDecRetries()} for the message with this id.
* When it reaches 0, the message is discarded from the {@link ExpiringRetryStore}.
*
* @param id Id of the message.
* @return The message or null when message got discarded or no message with this id exists.
*/
public synchronized T retryGet(String id) {
ExpiringMessage existingMessage = storeMap.get(id);
if (existingMessage == null)
ExpiringRetryMessage<T> existingRetryMessage = storeMap.get(id);
if (existingRetryMessage == null)
return null;

if (existingMessage instanceof ExpiringRetryStore<?>.ExpiringRetryMessage) {
ExpiringRetryMessage existingRetryMessage = (ExpiringRetryMessage) existingMessage;

if (existingRetryMessage.retriesLeft <= 0) {
log.debug("Discard message {} because no retries left.", id);
remove(id);
return null;
}

existingRetryMessage.retriesLeft--;
if (existingRetryMessage.getAndDecRetries() <= 0) {
log.debug("Discard message {} because no retries left.", id);
remove(id);
return null;
}

return get(id);
return existingRetryMessage.getMessage();
}
}
Loading

0 comments on commit 770011c

Please sign in to comment.