package org.icij.extract.queue;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.icij.extract.document.Document;
import org.icij.extract.document.DocumentFactory;
import org.icij.extract.redis.ConnectionManagerFactory;
import org.icij.extract.redis.DocumentDecoder;
import org.icij.extract.redis.DocumentEncoder;
import org.icij.task.Options;
import org.icij.task.annotation.Option;
import org.icij.task.annotation.OptionsClass;
import org.redisson.RedissonBlockingQueue;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import org.redisson.command.CommandSyncService;
import org.redisson.connection.ConnectionManager;
/**
* A {@link DocumentQueue} using Redis as a backend.
*
* @author Matthew Caruana Galizia <mcaruana@icij.org>
* @since 1.0.0-beta
*/
@Option(name = "queueName", description = "The name of the queue.", parameter = "name")
@Option(name = "charset", description = "Set the output encoding for strings. Defaults to UTF-8.", parameter = "name")
@OptionsClass(ConnectionManagerFactory.class)
public class RedisDocumentQueue extends RedissonBlockingQueue<Document> implements DocumentQueue {
/**
* The default name for a queue in Redis.
*/
private static final String DEFAULT_NAME = "extract:queue";
private final ConnectionManager connectionManager;
/**
* Create a Redis-backed queue using the provided configuration.
*
* @param factory for creating {@link Document} objects
* @param options options for connecting to Redis
*/
RedisDocumentQueue(final DocumentFactory factory, final Options<String> options) {
this(factory, new ConnectionManagerFactory().withOptions(options).create(),
options.get("queueName").value().orElse(DEFAULT_NAME),
options.get("charset").parse().asCharset().orElse(StandardCharsets.UTF_8));
}
/**
* Instantiate a new Redis-backed queue using the provided connection manager and name.
*
* @param factory for creating {@link Document} objects
* @param connectionManager instantiated using {@link ConnectionManagerFactory}
* @param name the name of the queue
* @param charset the character set for encoding and decoding paths
*/
private RedisDocumentQueue(final DocumentFactory factory, final ConnectionManager connectionManager,
final String name, final Charset charset) {
super(new DocumentQueueCodec(factory, charset), new CommandSyncService(connectionManager), null == name ?
DEFAULT_NAME : name);
this.connectionManager = connectionManager;
}
@Override
public void close() throws IOException {
connectionManager.shutdown();
}
/**
* Codec for a queue of paths to documents.
*
* @author Matthew Caruana Galizia <mcaruana@icij.org>
* @since 1.0.0-beta
*/
static class DocumentQueueCodec implements Codec {
private final Decoder<Object> documentDecoder;
private final Encoder documentEncoder;
DocumentQueueCodec(final DocumentFactory factory, final Charset charset) {
documentDecoder = new DocumentDecoder(factory, charset);
documentEncoder = new DocumentEncoder(charset);
}
@Override
public Decoder<Object> getValueDecoder() {
return documentDecoder;
}
@Override
public Decoder<Object> getMapValueDecoder() {
return documentDecoder;
}
@Override
public Decoder<Object> getMapKeyDecoder() {
return documentDecoder;
}
@Override
public Encoder getMapValueEncoder() {
return documentEncoder;
}
@Override
public Encoder getMapKeyEncoder() {
return documentEncoder;
}
@Override
public Encoder getValueEncoder() {
return documentEncoder;
}
}
}