/*
* Copyright 2009 Joubin Houshyar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jredis.examples;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jredis.JRedis;
import org.jredis.JRedisFuture;
import org.jredis.connector.ConnectionSpec;
import org.jredis.protocol.ResponseStatus;
import org.jredis.ri.alphazero.support.DefaultCodec;
import org.jredis.ri.alphazero.support.Log;
/**
* Example usage of {@link JRedisFuture}, demonstrating the asynchronous semantics.
* <p>
* Note that this is an abstract class and concrete extensions will use specific
* implementation of {@link JRedisFuture}. Refer to the extension class for the
* specifics of the associated {@link JRedisFuture} provider class.
*
*
* @author Joubin Houshyar (alphazero@sensesay.net)
* @version alpha.0, Oct 11, 2009
* @since alpha.0
*
*/
public abstract class UsingJRedisFuture {
// ------------------------------------------------------------------------
// EXAMPLES:
// ------------------------------------------------------------------------
/**
* The basics of using {@link Future} objects and {@link JRedisFuture}.
* <p>Note the key differences between using {@link JRedis} and {@link JRedisFuture}:
* <ul>
* <li>Methods have identical parameters, but all asynchronous methods return a flavor of {@link Future}
* <li>Exception handling is significantly different.
* </ul>
* @throws InterruptedException
*/
public void theBasics () throws InterruptedException {
Log.log("theBasics()");
/*******************************************/
// fire and forget -- no exception handling and not even bothering about the response
/*******************************************/
// this may throw exceptions on the invoke
jredis.ping();
/*******************************************/
// invoke and wait for response -- was it OK?
/*******************************************/
Future<ResponseStatus> fResp = null;
try {
/*
* Alright, asynch call is just like the synchronous flavor, but the return type is
* a Future<?> object. Here we are doing a PING which simply returns a STATUS response
* from redis, so the return type is Future<ResponseStatus>
*
*/
fResp = jredis.ping();
/*
* So we have a Future<?> object, but is that the actual response? No. You need to wait
* for it to actually complete.
*
* Here we're waiting using blocking semantics: we wait until its done.
*/
ResponseStatus response = fResp.get();
/**
* So we have our response at this point. We can check it, etc.
*/
if(response.isError()){
Log.error("PING returned an ERR response -- is it an authorization issue?");
}
}
catch (InterruptedException e) {
/*
* This can happen if the calling thread (which is under your control, not JRedis) is
* interrupted. Given that, its entirely up to you to determine what you should do here.
* If this is all greek to you, then simply log and re-throw it back up, or add InterruptedException
* to your method signatures.
*/
Log.problem("thread was interrupted while waiting for the response to be processed.");
throw e;
}
catch (ExecutionException e) {
/**
* Remember the exceptions that a synchronous JRedis method can throw (ClientRuntime, ProviderException,
* and of course RedisException)? Well, ExecutionException is the overall wrapper exception that is thrown
* by (all) Future instances to permit the propagation of those exceptions to the caller in an asynch manner.
* So, basically, if any problems arose after your call successfully returned (from the JRedisFuture method),
* here/now is where/when you find out about it.
*
* So what need to happen is for you to get the underlying 'cause' of the generalized ExecutionException.
*/
Log.problem("An execution exception occurred");
}
/*******************************************/
// invoke and wait for response using timeouts
/*******************************************/
byte[] setval = "bar".getBytes();
try {
for(int i=0; i<100000; i++){
jredis.ping();
jredis.incr("cntr");
}
jredis.set("foo", setval);
/*
* Here we're waiting using blocking semantics with timeout
*/
Future<byte[]> fVal = jredis.get("foo");
long t1 = System.nanoTime();
int toCnt = 0;
while(true){
try {
byte[] val = fVal.get(100L, TimeUnit.MICROSECONDS);
String value = DefaultCodec.toStr(val);
System.out.format("=> %s\n", value);
break;
}
catch (TimeoutException e) {
toCnt++;
// System.out.println('.');
}
}
t1 = System.nanoTime() - t1;
Log.log("done after %d timeouts (%d nanos)", toCnt, t1);
}
catch (InterruptedException e) {
/*
* This can happen if the calling thread (which is under your control, not JRedis) is
* interrupted. Given that, its entirely up to you to determine what you should do here.
* If this is all greek to you, then simply log and re-throw it back up, or add InterruptedException
* to your method signatures.
*/
Log.problem("thread was interrupted while waiting for the response to be processed.");
throw e;
}
catch (ExecutionException e) {
/**
* Remember the exceptions that a synchronous JRedis method can throw (ClientRuntime, ProviderException,
* and of course RedisException)? Well, ExecutionException is the overall wrapper exception that is thrown
* by (all) Future instances to permit the propagation of those exceptions to the caller in an asynch manner.
* So, basically, if any problems arose after your call successfully returned (from the JRedisFuture method),
* here/now is where/when you find out about it.
*
* So what need to happen is for you to get the underlying 'cause' of the generalized ExecutionException.
*/
Log.problem("An execution exception occurred");
}
}
// ------------------------------------------------------------------------
// Properties
// ------------------------------------------------------------------------
/** {@link JRedisFuture} provider instance */
protected final JRedisFuture jredis;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Creates a new instances and runs the example methods.
* @param connectionSpec used for creating the connection
*/
public UsingJRedisFuture (ConnectionSpec connectionSpec){
this.jredis = getProviderInstance(connectionSpec);
try {
runExamples();
}
catch (InterruptedException e) {
Log.problem("Interrupted while running the examples.");
e.printStackTrace();
}
jredis.quit();
}
/**
* @throws InterruptedException
*
*/
private void runExamples () throws InterruptedException {
Log.log("running the JRedisFuture usage examples with %s as the provider implementation.", jredis.getClass().getSimpleName());
theBasics();
}
// ------------------------------------------------------------------------
// Extension point
// ------------------------------------------------------------------------
/**
* Extension point.
* @param connectionSpec used for creating the connection.
* @return the {@link JRedisFuture} implementation instance
*/
abstract
protected JRedisFuture getProviderInstance (ConnectionSpec connectionSpec);
}