Hi Jeremy,

TL;DR - The RestAnnotationProcessor can be improved :)


If I understand properly what you want to achieve, you pretend to have
a method like the following:

@Delegate
CDNApi cdnApiInRegionAndServiceType(@Nullable String region, @Nullable
ServiceType serviceType);

The RestAnnotationProcessor [1] currently supports four ways of
generating the endpoint, and they are invoked in the following order:

1. If any parameter of the method is of type HttpRequest, its endpoint is used.
2. If there is any parameter annotated with the @EndpointParam
annotation [2], that generator is used. The annotation defines a
Function<Object, URI> that given the value of the annotated parameter,
returns the appropriate endpoint [3].
3. If there is an @Endpoint annotation [4] at method or class level,
it uses it to get the endpoint. The @Endpoint annotation defines a
qualifier annotation that is used to configure a Supplier<URI> in the
Guice context. That supplier is invoked to get the configured endpoint
[5].
4. If no endpoint is found by the previous methods, the provider URI
is used [6].

The problem in this case, is that method 1 is not an option here; the
second approach only receives *one* parameter (region in this example)
and you need two (region and serviceType); and the third approach can
not be changed at runtime as it receives no parameters, it just uses
the context configuration.


So what I would do is to add a little code to the
RestAnnotationProcessor so it allows APIs to generate endpoints based
on multiple parameters. It should be pretty straightforward. What I'd
suggest is:

1. Create a new annotation to be configured at method level, such as
@EndpointFromParams, which defines a Function<List<Object>, URI>. That
function will receive *all* the arguments for the invoked method and
return the generated endpoint.
2. Modify the "getEndpointFor" method to fallback to this function if
the endpoint can't be found in a concrete param after the step 2 from
the list above. I'll add something like the following code after this
line [7]:

// If endpoint can't be computed from a single param, try with all them
endpoint = getEndpointFromAllParametersOrNull(invocation, injector);
if (endpoint == null) {
 ...

You should create the "getEndpointFromAllParametersOrNull" that would
check if the method has the @EndpointFromParams annotation, and would
call the defined function if the annotation is present.

The change is not difficult and this would allow you to define the api
method like:

@Delegate
@EndpointFromParams(parser = RegionAndServiceTypeToEndpoint.class)
CDNApi cdnApiInRegionAndServiceType(@Nullable String region, @Nullable
ServiceType serviceType);



HTH!

Ignasi


[1] 
https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java
[2] 
https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/rest/annotations/EndpointParam.java
[3] 
https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java#L489-L507
[4] 
https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/rest/annotations/Endpoint.java
[5] 
https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java#L523-L534
[6] 
https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java#L377-L380
[7] 
https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java#L523

On 3 February 2014 19:24, Jeremy Daggett <jeremy.dagg...@gmail.com> wrote:
> Hi jclouds devs,
>
> Last week, I ran into an issue with the new CloudFiles API and I don't know
> how to solve it, so I am looking for some help. I am hoping that other
> community members have some ideas here!
>
> From what I have found, the new CloudFiles is the *only* aggregate API that
> needs to support two different OpenStack service types from the service
> catalog [1].
>
> The jclouds OpenStack Keystone API defines the SERVICE_TYPE property [2]
> and is set in the defaultProperties() for each of the OpenStack APIs that
> are available today via the ApiMetadata (for example [3]).
>
> The two service types that need to be supported in the CloudFilesApi are
> "object-storage" and "rax:object-cdn". If I set the service type in the
> properties to "object-storage", the endpoint for the CloudFilesApi (which
> extends the SwiftApi) are routed to the endpoint for "object-storage".
> Great. Now, if I set it to "rax:object-cdn", then all of the calls are
> routed to the "rax:object-cdn" endpoint.
>
> That makes sense, but how can we support multiple types? I have been
> through the RestAnnotationProcessor tracing each of the calls, and I
> believe the issue lies in the Keystone suppliers [4] somehow. I wonder if
> we need the notion of "Service Types" in the Keystone API and register that
> via the properties. ??
>
> I am going to continue debugging and looking for a solution, but I am
> blocked on this right now and could use any insight. If anyone has ideas on
> how to support this, I would be very grateful. Thanks!
>
> /jd
>
> [1]
> http://docs.openstack.org/api/openstack-identity-service/2.0/content/POST_authenticate_v2.0_tokens_.html
>
> [2]
> https://github.com/jclouds/jclouds/blob/master/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/config/KeystoneProperties.java#L72
>
> [3]
> https://github.com/jclouds/jclouds/blob/master/apis/openstack-cinder/src/main/java/org/jclouds/openstack/cinder/v1/CinderApiMetadata.java#L59
>
> [4]
> https://github.com/jclouds/jclouds/tree/master/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v2_0/suppliers

Reply via email to