jclouds/jclouds — pull request #1159 by adriancole (2013-01-09 07:04:38+00:00)
Title: Proxy overhaul

This is an important change as it significantly reduces the proxy code involved in jclouds.

Proxies in jclouds:

All proxies in jclouds are created with `FunctionalReflection.newProxy` via functions that implement `Function`. Proxies are used to implement `context.getApi()` (`S`) and `context.getAsyncApi()` (`A`). This is why no-one sees implementations of `S3Client` for example.

In jclouds a sync (`S`) or async (`A`) interface can have a few declarations that are valid.
* Methods on the `A` interface have jaxrs and other jclouds annotations return `ListenableFuture` values. These values are parsed from http calls.
* Methods on the `S` interface call the same methods defined on the `A` interface and block until complete.
* `@Provides` methods return values from guice. For example `@Provides Set regions();` returns the `Set` previously bound to guice.
* `@Delegate` methods propagate any args and annotations to the interface defined as the return val. For example, `@Path("/api2") @Delegate ServerApi getServerApi();` will enforce any rest calls are prefixed with `/api2/`.

`DelegatingInvocationFunction` does the above routing and dispatches http-related code to `InvokeHttpMethod`. There's a rare case where the `A` isn't http-backed, for example the `transient` BlobStore. In this case, delegation goes via `CallGetOnFuturesProvider`.

Guice details:

On creating the guice injector jclouds creates 2 proxies, one for each sync (`S`) and async (`A`) interface, again backed by `DelegatingInvocationFunction`. During runtime, `DelegatingInvocationFunction` will create a new proxy for each call to a `@Delegate` method, something we should likely cache.

The binding of the `A` implementation is handled by `AsyncHttpApiProvider` for example `SwiftAsyncApi`

The binding of the blocking `S` implementation is handled by `HttpApiProvider` for example `SwiftApi`. Note that this does not call the `A` proxy, rather it creates the http requests independently. This allows for much shorter stack traces without duplicating logic, as both `S` and `A` are implemented by the same class `InvokeHttpMethod`.

In the case of non-http async apis like `transient` `transient-chef` and `filesystem, `A` is expected to be previously bound, and the synchronous proxy is bound to guice by `CallGetOnFuturesProvider`.

All `S` `A` pairs need to be previously declared in the subclass of `RestClientModule`, or bound independently with `BinderUtils`. This process pre-caches the mapping between `A` and `S` apis, which significantly reduces complexity later.