package com.appgree.core.redis.provider;
import org.apache.log4j.Logger;
import redis.clients.jedis.Response;
import com.appgree.core.redis.domain.RedisCallback;
/**
* The delayed response wrapper.
*
* @param <T> The return type
*/
public class RedisValue<T> {
/** The logger. */
private static Logger logger = Logger.getLogger(RedisValue.class.getName());
/** The value. */
private T value;
/** The must wait. */
private boolean mustWait = true;
/** The value set. */
private boolean valueSet = false;
/** The response. */
private Response<T> response;
/** The callback. */
private RedisCallback<T> callback;
/** The pipelined manager. */
private RedisPipelinedManager pipelinedManager;
/** The flushed. */
private boolean flushed = false;
/** The auto flush. */
private boolean autoFlush = false;
/** The processed value. */
private volatile Object processedValue = null;
/** The is processed. */
private volatile boolean isProcessed = false;
/** The ttl reference. */
private long ttlReference = 0;
/**
* Sets the pipelined manager.
*
* @param pipelinedManager the new pipelined manager
*/
void setPipelinedManager(RedisPipelinedManager pipelinedManager) {
this.pipelinedManager = pipelinedManager;
}
/**
* Flush.
*
* @return the redis value
*/
synchronized public RedisValue<T> flush() {
if (this.flushed) {
return this;
}
setFlushed();
if (this.pipelinedManager != null) {
this.pipelinedManager.flush();
}
return this;
}
/**
* Sets the flushed.
*/
synchronized void setFlushed() {
this.flushed = true;
this.autoFlush = false;
}
/**
* Sets the value.
*
* @param value the value
* @return the redis value
*/
synchronized RedisValue<T> setValue(T value) {
this.value = value;
this.valueSet = true;
this.ttlReference = System.currentTimeMillis();
setFlushed();
executeCallBack();
// wake it up
this.mustWait = false;
notifyAll();
return this;
}
/**
* Sets the response.
*
* @param response the response
* @return the redis value impl
*/
RedisValue<T> setResponse(Response<T> response) {
this.response = response;
return this;
}
/**
* Gets the response.
*
* @return the response
*/
Response<T> getResponse() {
return this.response;
}
/**
* Sets the call back.
*
* @param callback the new call back
*/
public synchronized void setCallBack(RedisCallback<T> callback) {
this.callback = callback;
executeCallBack();
}
/**
* Gets the.
*
* @return the t
*/
public synchronized T get() {
if (this.autoFlush && this.flushed == false) {
flush();
}
if (mustWait) {
RedisPipelinedManager.incrBlockingCounter(1);
try {
// waits for notification
wait(RedisPipelinedManager.getMaxWaitTimeout());
// this will prevent errors when closing main container
if (pipelinedManager != null && pipelinedManager.isShutdown()) {
throw new RuntimeException("Service is shutting down unexpectedly!");
}
// checks timeout state
if (mustWait) {
throw new RuntimeException("Cache: timeout in get() method");
}
} catch (InterruptedException e) {
throw new RuntimeException("Cache: Interrupted", e);
} finally {
RedisPipelinedManager.incrBlockingCounter(-1);
}
}
return value;
}
/**
* Execute call back.
*/
private synchronized void executeCallBack() {
try {
if (this.callback != null && valueSet) {
this.callback.execute(this.value);
}
} catch (Throwable e) {
logger.error("Exception executing callback", e);
}
}
/**
* Sets the auto flush.
*/
public void setAutoFlush() {
this.autoFlush = true;
}
/**
* Gets the.
*
* @param <Z> the generic type
* @param processor the processor
* @return the z
*/
@SuppressWarnings("unchecked")
public <Z> Z get(RedisValueProcessor<Z, T> processor) {
// checks the most common case
if (this.isProcessed) {
return (Z) this.processedValue;
}
if (processor == null) {
throw new NullPointerException("Internal error: Null processor in redis value");
}
// we execute the processor only once
synchronized (this) {
if (this.isProcessed) {
return (Z) this.processedValue;
}
Z temp = processor.process(this);
// reset values
this.processedValue = temp;
this.isProcessed = true;
}
return (Z) this.processedValue;
}
/**
* Retrieves the TTL reference.
*
* @return The TTL reference time
*/
public long getTtlReference() {
return ttlReference;
}
}