Issue 9: better javadoc

git-svn-id: http://jclouds.googlecode.com/svn/trunk@883 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-05-30 10:47:35 +00:00
parent fd3c3841ee
commit d9ff753ac4
5 changed files with 303 additions and 287 deletions

View File

@ -40,103 +40,115 @@ import com.google.inject.Inject;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class FutureCommandConnectionPoolClient<C, O extends FutureCommand<?, ?, ?>> public class FutureCommandConnectionPoolClient<C, O extends FutureCommand<?, ?, ?>> extends
extends BaseLifeCycle implements FutureCommandClient<O> { BaseLifeCycle implements FutureCommandClient<O> {
private final FutureCommandConnectionPool<C, O> futureCommandConnectionPool; private final FutureCommandConnectionPool<C, O> futureCommandConnectionPool;
private final BlockingQueue<O> commandQueue; private final BlockingQueue<O> commandQueue;
@Inject @Inject
public FutureCommandConnectionPoolClient(ExecutorService executor, public FutureCommandConnectionPoolClient(ExecutorService executor,
FutureCommandConnectionPool<C, O> futureCommandConnectionPool, FutureCommandConnectionPool<C, O> futureCommandConnectionPool,
BlockingQueue<O> commandQueue) { BlockingQueue<O> commandQueue) {
super(executor, futureCommandConnectionPool); super(executor, futureCommandConnectionPool);
this.futureCommandConnectionPool = futureCommandConnectionPool; this.futureCommandConnectionPool = futureCommandConnectionPool;
this.commandQueue = commandQueue; this.commandQueue = commandQueue;
} }
@Override /**
protected boolean shouldDoWork() { * {@inheritDoc}
return super.shouldDoWork() * <p/>
&& futureCommandConnectionPool.getStatus() * we continue while the connection pool is active
.equals(Status.ACTIVE); */
} @Override
protected boolean shouldDoWork() {
return super.shouldDoWork() && futureCommandConnectionPool.getStatus().equals(Status.ACTIVE);
}
@Override /**
protected void doShutdown() { * {@inheritDoc}
exception.compareAndSet(null, futureCommandConnectionPool *
.getException()); * If the reason we are shutting down is due an exception, we set that exception on all pending
while (!commandQueue.isEmpty()) { * commands. Otherwise, we cancel the pending commands.
FutureCommand<?, ?, ?> command = (FutureCommand<?, ?, ?>) commandQueue */
.remove(); @Override
if (command != null) { protected void doShutdown() {
if (exception.get() != null) exception.compareAndSet(null, futureCommandConnectionPool.getException());
command.setException(exception.get()); while (!commandQueue.isEmpty()) {
else FutureCommand<?, ?, ?> command = (FutureCommand<?, ?, ?>) commandQueue.remove();
command.cancel(true); if (command != null) {
} if (exception.get() != null)
} command.setException(exception.get());
} else
command.cancel(true);
}
}
}
@Override @Override
protected void doWork() throws InterruptedException { protected void doWork() throws InterruptedException {
O command = commandQueue.poll(1, TimeUnit.SECONDS); takeACommandOffTheQueueAndInvokeIt();
if (command != null) { }
try {
invoke(command);
} catch (Exception e) {
Utils.<InterruptedException> rethrowIfRuntimeOrSameType(e);
logger.error(e, "Error processing command %s", command);
}
}
}
public void submit(O command) { private void takeACommandOffTheQueueAndInvokeIt() throws InterruptedException {
exceptionIfNotActive(); O command = commandQueue.poll(1, TimeUnit.SECONDS);
commandQueue.add(command); if (command != null) {
} try {
invoke(command);
} catch (Exception e) {
Utils.<InterruptedException> rethrowIfRuntimeOrSameType(e);
logger.error(e, "Error processing command %s", command);
}
}
}
protected void invoke(O command) { /**
exceptionIfNotActive(); * This is an asynchronous operation that puts the <code>command</code> onto a queue. Later, it
FutureCommandConnectionHandle<C, O> connectionHandle = null; * will be processed via the {@link #invoke(FutureCommand) invoke} method.
try { */
connectionHandle = futureCommandConnectionPool.getHandle(command); public void submit(O command) {
} catch (InterruptedException e) { exceptionIfNotActive();
logger commandQueue.add(command);
.warn( }
e,
"Interrupted getting a connection for command %1$s; retrying",
command);
commandQueue.add(command);
return;
} catch (TimeoutException e) {
logger.warn(e,
"Timeout getting a connection for command %1$s; retrying",
command);
commandQueue.add(command);
return;
}
if (connectionHandle == null) { /**
logger.error( * Invoke binds a command with a connection from the pool. This binding is called a
"Failed to obtain connection for command %1$s; retrying", * {@link FutureCommandConnectionHandle handle}. The handle will keep this binding until the
command); * command's response is parsed or an exception is set on the Command object.
commandQueue.add(command); *
return; * @param command
} */
connectionHandle.startConnection(); protected void invoke(O command) {
} exceptionIfNotActive();
FutureCommandConnectionHandle<C, O> connectionHandle = null;
try {
connectionHandle = futureCommandConnectionPool.getHandle(command);
} catch (InterruptedException e) {
logger.warn(e, "Interrupted getting a connection for command %1$s; retrying", command);
commandQueue.add(command);
return;
} catch (TimeoutException e) {
logger.warn(e, "Timeout getting a connection for command %1$s; retrying", command);
commandQueue.add(command);
return;
}
@Override if (connectionHandle == null) {
public String toString() { logger.error("Failed to obtain connection for command %1$s; retrying", command);
final StringBuilder sb = new StringBuilder(); commandQueue.add(command);
sb.append("FutureCommandConnectionPoolClient"); return;
sb.append("{status=").append(status); }
sb.append(", commandQueue=").append( connectionHandle.startConnection();
(commandQueue != null) ? commandQueue.size() : 0); }
sb.append(", futureCommandConnectionPool=").append(
futureCommandConnectionPool); @Override
sb.append('}'); public String toString() {
return sb.toString(); final StringBuilder sb = new StringBuilder();
} sb.append("FutureCommandConnectionPoolClient");
sb.append("{status=").append(status);
sb.append(", commandQueue=").append((commandQueue != null) ? commandQueue.size() : 0);
sb.append(", futureCommandConnectionPool=").append(futureCommandConnectionPool);
sb.append('}');
return sb.toString();
}
} }

View File

@ -28,7 +28,8 @@ import org.jclouds.http.commands.callables.xml.ParseSax;
import com.google.inject.Inject; import com.google.inject.Inject;
/** /**
* // TODO: Adrian: Document this! * temporary factory until guice can do multi-type assisted inject
* @see <a href="http://code.google.com/p/google-guice/issues/detail?id=346" />
* *
* @author Adrian Cole * @author Adrian Cole
*/ */

View File

@ -38,137 +38,137 @@ import org.jclouds.logging.Logger;
* @author Adrian Cole * @author Adrian Cole
*/ */
public abstract class BaseLifeCycle implements Runnable, LifeCycle { public abstract class BaseLifeCycle implements Runnable, LifeCycle {
@Resource @Resource
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
protected final ExecutorService executor; protected final ExecutorService executor;
protected final BaseLifeCycle[] dependencies; protected final BaseLifeCycle[] dependencies;
protected final Object statusLock; protected final Object statusLock;
protected volatile Status status; protected volatile Status status;
protected AtomicReference<Exception> exception = new AtomicReference<Exception>(); protected AtomicReference<Exception> exception = new AtomicReference<Exception>();
public BaseLifeCycle(ExecutorService executor, public BaseLifeCycle(ExecutorService executor, BaseLifeCycle... dependencies) {
BaseLifeCycle... dependencies) { this.executor = executor;
this.executor = executor; this.dependencies = dependencies;
this.dependencies = dependencies; this.statusLock = new Object();
this.statusLock = new Object(); this.status = Status.INACTIVE;
this.status = Status.INACTIVE; }
}
public Status getStatus() { public Status getStatus() {
return status; return status;
} }
public void run() { public void run() {
try { try {
while (shouldDoWork()) { while (shouldDoWork()) {
doWork(); doWork();
} }
} catch (Exception e) { } catch (Exception e) {
logger.error(e, "Exception doing work"); logger.error(e, "Exception doing work");
exception.set(e); exception.set(e);
} }
this.status = Status.SHUTTING_DOWN; this.status = Status.SHUTTING_DOWN;
doShutdown(); doShutdown();
this.status = Status.SHUT_DOWN; this.status = Status.SHUT_DOWN;
logger.info("%1$s", this); logger.info("%1$s", this);
} }
protected abstract void doWork() throws Exception; protected abstract void doWork() throws Exception;
protected abstract void doShutdown(); protected abstract void doShutdown();
protected boolean shouldDoWork() { /**
try { * @return false if any dependencies are inactive, or we are inactive, or we have a global
exceptionIfDepedenciesNotActive(); * exception.
} catch (IllegalStateException e) { */
return false; protected boolean shouldDoWork() {
} try {
return status.equals(Status.ACTIVE) && exception.get() == null; exceptionIfDepedenciesNotActive();
} } catch (IllegalStateException e) {
return false;
}
return status.equals(Status.ACTIVE) && exception.get() == null;
}
@PostConstruct @PostConstruct
public void start() { public void start() {
logger.info("starting %1$s", this); logger.info("starting %1$s", this);
synchronized (this.statusLock) { synchronized (this.statusLock) {
if (this.status.compareTo(Status.SHUTDOWN_REQUEST) >= 0) { if (this.status.compareTo(Status.SHUTDOWN_REQUEST) >= 0) {
doShutdown(); doShutdown();
this.status = Status.SHUT_DOWN; this.status = Status.SHUT_DOWN;
this.statusLock.notifyAll(); this.statusLock.notifyAll();
return; return;
} }
if (this.status.compareTo(Status.ACTIVE) == 0) { if (this.status.compareTo(Status.ACTIVE) == 0) {
this.statusLock.notifyAll(); this.statusLock.notifyAll();
return; return;
} }
if (this.status.compareTo(Status.INACTIVE) != 0) { if (this.status.compareTo(Status.INACTIVE) != 0) {
throw new IllegalStateException("Illegal state: " + this.status); throw new IllegalStateException("Illegal state: " + this.status);
} }
exceptionIfDepedenciesNotActive(); exceptionIfDepedenciesNotActive();
this.status = Status.ACTIVE; this.status = Status.ACTIVE;
} }
executor.execute(this); executor.execute(this);
} }
protected void exceptionIfDepedenciesNotActive() { protected void exceptionIfDepedenciesNotActive() {
for (BaseLifeCycle dependency : dependencies) { for (BaseLifeCycle dependency : dependencies) {
if (dependency.status.compareTo(Status.ACTIVE) != 0) { if (dependency.status.compareTo(Status.ACTIVE) != 0) {
throw new IllegalStateException(String.format( throw new IllegalStateException(String.format(
"Illegal state: %1$s for component: %2$s", "Illegal state: %1$s for component: %2$s", dependency.status, dependency));
dependency.status, dependency)); }
} }
} }
}
public Exception getException() { public Exception getException() {
return this.exception.get(); return this.exception.get();
} }
protected void awaitShutdown(long timeout) throws InterruptedException { protected void awaitShutdown(long timeout) throws InterruptedException {
awaitStatus(Status.SHUT_DOWN, timeout); awaitStatus(Status.SHUT_DOWN, timeout);
} }
protected void awaitStatus(Status intended, long timeout) protected void awaitStatus(Status intended, long timeout) throws InterruptedException {
throws InterruptedException { synchronized (this.statusLock) {
synchronized (this.statusLock) { long deadline = System.currentTimeMillis() + timeout;
long deadline = System.currentTimeMillis() + timeout; long remaining = timeout;
long remaining = timeout; while (this.status != intended) {
while (this.status != intended) { this.statusLock.wait(remaining);
this.statusLock.wait(remaining); if (timeout > 0) {
if (timeout > 0) { remaining = deadline - System.currentTimeMillis();
remaining = deadline - System.currentTimeMillis(); if (remaining <= 0) {
if (remaining <= 0) { break;
break; }
} }
} }
} }
} }
}
@PreDestroy @PreDestroy
public void shutdown() { public void shutdown() {
shutdown(2000); shutdown(2000);
} }
public void shutdown(long waitMs) { public void shutdown(long waitMs) {
synchronized (this.statusLock) { synchronized (this.statusLock) {
if (this.status.compareTo(Status.ACTIVE) > 0) { if (this.status.compareTo(Status.ACTIVE) > 0) {
return; return;
} }
this.status = Status.SHUTDOWN_REQUEST; this.status = Status.SHUTDOWN_REQUEST;
try { try {
awaitShutdown(waitMs); awaitShutdown(waitMs);
} catch (InterruptedException ignore) { } catch (InterruptedException ignore) {
} }
} }
} }
protected void exceptionIfNotActive() { protected void exceptionIfNotActive() {
if (!status.equals(Status.ACTIVE)) if (!status.equals(Status.ACTIVE))
throw new IllegalStateException(String.format("not active: %1$s", throw new IllegalStateException(String.format("not active: %1$s", this));
this)); }
}
} }

View File

@ -30,21 +30,21 @@ import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* // TODO: Adrian: Document this! * This will close objects in the reverse order that they were added.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class Closer implements Closeable { public class Closer implements Closeable {
List<Closeable> methodsToClose = new ArrayList<Closeable>(); List<Closeable> methodsToClose = new ArrayList<Closeable>();
public void addToClose(Closeable toClose) { public void addToClose(Closeable toClose) {
methodsToClose.add(toClose); methodsToClose.add(toClose);
} }
public void close() throws IOException { public void close() throws IOException {
Collections.reverse(methodsToClose); Collections.reverse(methodsToClose);
for (Closeable toClose : methodsToClose) { for (Closeable toClose : methodsToClose) {
toClose.close(); toClose.close();
} }
} }
} }

View File

@ -48,82 +48,85 @@ import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener; import com.google.inject.spi.TypeListener;
/** /**
* // TODO: Adrian: Document this! * This associates java lifecycle annotations with guice hooks. For example, we invoke
* {@link PostConstruct} after injection, and Associate {@link PostDestroy} with a global
* {@link Closer} object.
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class LifeCycleModule extends AbstractModule { public class LifeCycleModule extends AbstractModule {
protected void configure() { protected void configure() {
final ExecutorService executor = Executors.newCachedThreadPool(); final ExecutorService executor = Executors.newCachedThreadPool();
bind(ExecutorService.class).toInstance(executor); bind(ExecutorService.class).toInstance(executor);
Closer closer = new Closer(); Closer closer = new Closer();
closer.addToClose(new Closeable() { closer.addToClose(new Closeable() {
public void close() throws IOException { public void close() throws IOException {
executor.shutdownNow(); executor.shutdownNow();
} }
}); });
bind(Closer.class).toInstance(closer); bind(Closer.class).toInstance(closer);
bindPostInjectionInvoke(closer); bindPostInjectionInvoke(closer);
} }
protected void bindPostInjectionInvoke(final Closer closer) { protected void bindPostInjectionInvoke(final Closer closer) {
bindListener(any(), new TypeListener() { bindListener(any(), new TypeListener() {
public <I> void hear(TypeLiteral<I> injectableType, public <I> void hear(TypeLiteral<I> injectableType, TypeEncounter<I> encounter) {
TypeEncounter<I> encounter) { Set<Method> methods = new HashSet<Method>();
Set<Method> methods = new HashSet<Method>(); Class<? super I> type = injectableType.getRawType();
Class<? super I> type = injectableType.getRawType(); while (type != null) {
while (type != null) { methods.addAll(Arrays.asList(type.getDeclaredMethods()));
methods.addAll(Arrays.asList(type.getDeclaredMethods())); type = type.getSuperclass();
type = type.getSuperclass(); }
} for (final Method method : methods) {
for (final Method method : methods) { invokePostConstructMethodAfterInjection(encounter, method);
PostConstruct postConstruct = method associatePreDestroyWithCloser(closer, encounter, method);
.getAnnotation(PostConstruct.class); }
if (postConstruct != null) { }
encounter.register(new InjectionListener<I>() {
public void afterInjection(I injectee) {
try {
method.invoke(injectee);
} catch (InvocationTargetException ie) {
Throwable e = ie.getTargetException();
throw new ProvisionException(
e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new ProvisionException(
e.getMessage(), e);
}
}
});
}
PreDestroy preDestroy = method private <I> void associatePreDestroyWithCloser(final Closer closer,
.getAnnotation(PreDestroy.class); TypeEncounter<I> encounter, final Method method) {
if (preDestroy != null) { PreDestroy preDestroy = method.getAnnotation(PreDestroy.class);
encounter.register(new InjectionListener<I>() { if (preDestroy != null) {
public void afterInjection(final I injectee) { encounter.register(new InjectionListener<I>() {
closer.addToClose(new Closeable() { public void afterInjection(final I injectee) {
public void close() throws IOException { closer.addToClose(new Closeable() {
try { public void close() throws IOException {
method.invoke(injectee); try {
} catch (InvocationTargetException ie) { method.invoke(injectee);
Throwable e = ie } catch (InvocationTargetException ie) {
.getTargetException(); Throwable e = ie.getTargetException();
throw new IOException(e throw new IOException(e.getMessage());
.getMessage()); } catch (IllegalAccessException e) {
} catch (IllegalAccessException e) { throw new IOException(e.getMessage());
throw new IOException(e }
.getMessage()); }
} });
}
});
} }
}); });
} }
} }
}
}); private <I> void invokePostConstructMethodAfterInjection(TypeEncounter<I> encounter,
} final Method method) {
PostConstruct postConstruct = method.getAnnotation(PostConstruct.class);
if (postConstruct != null) {
encounter.register(new InjectionListener<I>() {
public void afterInjection(I injectee) {
try {
method.invoke(injectee);
} catch (InvocationTargetException ie) {
Throwable e = ie.getTargetException();
throw new ProvisionException(e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new ProvisionException(e.getMessage(), e);
}
}
});
}
}
});
}
} }