I am planning to write a sequence generator which will be used in my REST resource implementation class during post to generate unique id. Since every post request is handl
You can take advantage of java.util.prefs.Preferences to persist the current state of your sequence generator on the disk and use it again later.
(also, you may want to use several sequence generators)
i.e.
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.prefs.Preferences;
public final class SequenceGenerator {
private static final Preferences PREFS = Preferences.userNodeForPackage(SequenceGenerator.class);
private static final AtomicLong SEQ_ID = new AtomicLong(Integer.parseInt(PREFS.get("seq_id", "1")));
private static final Map<Long, SoftReference<SequenceGenerator>> GENERATORS = new ConcurrentHashMap<>();
private static final SequenceGenerator DEF_GENERATOR = new SequenceGenerator(0L, Long.parseLong(PREFS.get("seq_0", "1")));
static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
GENERATORS.values().stream()
.map(SoftReference::get)
.filter(seq -> seq != null && seq.isPersistOnExit())
.forEach(SequenceGenerator::persist);
if (DEF_GENERATOR.isPersistOnExit()) {
DEF_GENERATOR.persist();
}
PREFS.put("seq_id", SEQ_ID.toString());
}));
}
private final long sequenceId;
private final AtomicLong counter;
private final AtomicBoolean persistOnExit = new AtomicBoolean();
private SequenceGenerator(long sequenceId, long initialValue) {
this.sequenceId = sequenceId;
counter = new AtomicLong(initialValue);
}
public long nextId() {
return counter.getAndIncrement();
}
public long currentId() {
return counter.get();
}
public long getSequenceId() {
return sequenceId;
}
public boolean isPersistOnExit() {
return persistOnExit.get();
}
public void setPersistOnExit(boolean persistOnExit) {
this.persistOnExit.set(persistOnExit);
}
public void persist() {
PREFS.put("seq_" + sequenceId, counter.toString());
}
@Override
protected void finalize() throws Throwable {
super.finalize();
GENERATORS.remove(sequenceId);
if (persistOnExit.get()) {
persist();
}
}
@Override
public int hashCode() {
return Long.hashCode(sequenceId);
}
@Override
public boolean equals(Object obj) {
return obj == this || obj != null && obj instanceof SequenceGenerator && sequenceId == ((SequenceGenerator) obj).sequenceId;
}
@Override
public String toString() {
return "{" +
"counter=" + counter +
", seq=" + sequenceId +
'}';
}
public static SequenceGenerator getDefault() {
return DEF_GENERATOR;
}
public static SequenceGenerator get(long sequenceId) {
if (sequenceId < 0) {
throw new IllegalArgumentException("(sequenceId = " + sequenceId + ") < 0");
}
if (sequenceId == 0) {
return DEF_GENERATOR;
}
SoftReference<SequenceGenerator> r = GENERATORS.computeIfAbsent(sequenceId, sid -> {
try {
return new SoftReference<>(new SequenceGenerator(sid, Long.parseLong(PREFS.get("seq_" + sid, null))));
} catch (Throwable t) {
return null;
}
});
return r == null ? null : r.get();
}
public static SequenceGenerator create() {
return create(1);
}
public static SequenceGenerator create(long initialValue) {
long sequenceId = SEQ_ID.getAndIncrement();
SequenceGenerator seq = new SequenceGenerator(sequenceId, Long.parseLong(PREFS.get("seq_" + sequenceId, "" + initialValue)));
GENERATORS.put(sequenceId, new SoftReference<>(seq));
return seq;
}
}
It will work, however AtomicInteger
is an built in type that is perfect for your use case.
AtomicInteger seq = new AtomicInteger();
int nextVal = seq.incrementAndGet();
If you are open to using String
for IDs, instead of int
, you might want to look into using UUID
(Universally Unique Identifier). Very easy to use and as the name implies, they are unique. Here is an example of how to generate one:
// the value of uuid will be something like '03c9a439-fba6-41e1-a18a-4c542c12e6a8'
String uuid = java.util.UUID.randomUUID().toString()
A UUID
also provides better security than int
because with integers you can guess the next request ID by simply adding 1 to your request ID, but UUIDs are not sequential and chances of anyone guessing anyone else's request ID are pretty slim.