juneau-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sblackmon <sblack...@apache.org>
Subject Re: Advice regarding Juneau RestClient, RestCall, Serializers, best practices
Date Wed, 22 Mar 2017 22:01:55 GMT
 
Regarding the improvements to proxies, great to hear that is an area of active development.
I’ll be watching for a new snapshot adding that method that would let me derive multiple
proxy interfaces from a single rest client by specifying a path for each.

Since we’re looking that this capability right now, would be it a stretch to attach the
@RestMethod or similar annotation with a path attribute directly to the interface class? 


@RestMethod(name = "statuses", path = "/statuses/*")
public interface Statuses {  
  public List lookup(StatusesLookupRequest parameters);
}

This way the interface describes each endpoint at a specific path, and where that path resides
relative to a root URL.  

Then a top-level accessor class would define the root URL and hold one instance of each interface
it exposed:

public class Twitter {
  private String rootUrl = “https://api.twitter.com/v1";  
  private RestClient client = new RestClientBuilder()  
    .serializer(JsonSerializer.class)
    .parser(JsonParser.class)  
    .authorization( … whatever … )
    .rootUrl(rootUrl)  
    .build();
  public Statuses statuses = client.getRemoteableProxy(Statuses.class);
  // etc for the other ~30 endpoints
}

Now any class that has a pointer to a Twitter could do the following synchronously: 

StatusesLookupRequest statusesLookupRequest = new StatusesLookupRequest().withId(new ArrayList(“123”,”456”));
List tweets = twitter.statuses.lookup(statusesLookupRequest);

This seems to me like a killer scheme for mapping and wrapping http apis for JVM that we could
apply across most of the data providers in Streams.  

Cheers,  
Steve

P.S. for a future thread, how about asynchronous access?
On March 22, 2017 at 11:17:36 AM, James Bognar (james.bognar@salesforce.com(mailto:james.bognar@salesforce.com))
wrote:

> *This makes a lot of sense to me. If it’s possible to also allow for an
> in-line override of Serializer or Parser, that would limit the number of
> RestClients our provider classes need to create (to 1).*
>  
> Will do.
>  
>  
> *And you can also specify arrays...getResponse(MyBean[].class). This is
> perfect. I remember now seeing the Type,Type… method in the release notes
> and will try it out right away. Of course, I’m hoping that RemoteableProxy
> can be used across the board, if so we won’t need implementation classes
> that would have to do this at all.*
>  
> I need to update the javadocs on the RestClient.getResponse(Type,Type...)
> method. It's not clear from the docs how to use it. For now, just refer
> to the Parser#parse(Type,Type...) for how that works for maps and
> collections.
>  
> If you're interested in the remoteable proxies, you'll probably be
> interested in what I've been working on this week...adding interface
> proxies using @RestMethod. It's a simplified way to define them without
> having to use the RemoteableServlet. Instead, you just define a
> @RestMethod(name="PROXY") method with an interface return type, like so....
>  
> /**
> * [PROXY /*]
> * Return a proxy interface to IAddressBook.
> */
> @RestMethod(name="PROXY", path="/myproxy/*")
> public IAddressBook getProxy() {
> return addressBook;
> }
> On the client side, you get the proxy using RestClient like so....
>  
> IAddressBook ab = client.getRemoteableProxy(IAddressBook.class,
> "/addressBook/myproxy");
> Under-the-covers, a method call is simply a POST with parameters and
> returned object marshalled in the HTTP request and response bodies, and the
> individual methods are addressed using the method signature...e.g....
> POST /addressBook/myproxy/callMyMethod(java.lang.String,boolean)
>  
>  
>  
>  
>  
>  
>  
> On Wed, Mar 22, 2017 at 11:20 AM, sblackmon wrote:
>  
> > Thanks James. Comments below.
> > On March 20, 2017 at 11:48:16 AM, James Bognar (
> > james.bognar@salesforce.com) wrote:
> >
> > Hi Steve!
> >
> > I'm in training today, so my responses may not be prompt.
> >
> > Heads up....in 6.2.0 (what I'm working on now, currently labeled 6.1.1),
> > I'm revamping the RestClient code to use builders just like I did for the
> > serializers and parsers in 6.1.0. So now's a good time for suggested
> > changes! I think the new design makes it considerably easier to understand
> > and reuse HttpClients or HttpClientBuilders.
> >
> > You're correct that a RestClient only uses a single serializer/parser
> > pair. The thoughts were...
> > 1) You avoid having to specify the Accept and Content-Type on the RestCall
> > object.
> > 2) Typical real-world scenario would be that you're only interested in
> > working in a particular language.
> >
> > If you anticipate needing support for multiple languages, it won't be
> > difficult to add. Most likely with a 'default' language, and if you want
> > to use one of the others, you need to do...
> > RestClient.doX().setAccept("text/foo").setContentType("
> > text/foo").getResponse(X);
> >
> > Let me know if you want this.
> > This makes a lot of sense to me. If it’s possible to also allow for an
> > in-line override of Serializer or Parser, that would limit the number of
> > RestClients our provider classes need to create (to 1).
> >
> >
> >
> > *I also am thinking that a new method on RestClient that allowed you to
> > doGet from an request POJO, and have it converted into parameters (using
> > BeanMap?) would help make this easier.*
> > I'm not sure I understand. Can you provide a code example? (or is it in
> > the branch already?)
> > Here’s a specific use case from STREAMS-496.
> >
> > We have an interface that describes the behavior of an external REST
> > endpoint:
> >
> > https://github.com/steveblackmon/incubator-streams/blob/STREAMS-496/
> > streams-contrib/streams-provider-twitter/src/main/java/org/apache/streams/
> > twitter/api/Statuses.java
> >
> > The module contains POJOs for the inputs and outputs of these endpoints,
> > generate from jsonschemas.
> >
> > https://github.com/steveblackmon/incubator-streams/blob/STREAMS-496/
> > streams-contrib/streams-provider-twitter/src/main/
> > jsonschema/org/apache/streams/twitter/api/StatusesLookupRequest.json
> >
> > As I understand, If the third-party endpoints worked on the ‘post with
> > json to get json’ model, then we could use juneau’s RemotableProxy
> > capability to execute these transactions with just the POJOs and the
> > interface, no implementation of the interface required! Very cool...
> > However, the third-party endpoints actually use a ‘get with a bunch of
> > params to get json’ model, and defining the interface methods as a chain of
> > 10+ primitives, most of which are optional, is ugly and difficult to use in
> > java. I’d much prefer to define the interface using a ‘one POJO in, one
> > POJO out’ (like in the links above), and use the fluent builders on those
> > beans to craft the request objects.
> >
> > My suggestion is to add a generic swap capability usable by RestClient or
> > RemoteableProxy that can translate a single (probably flat) POJO argument
> > into get parameters transparently, so that remotableproxy using get
> > requests with complex argument structures can be as easy to code and easy
> > to read as those using post requests with payloads.
> >
> > If this sounds good to you, put it in JIRA and I’ll take a shot at the
> > implementation.
> >
> >
> >
> > *Also wondering whether type erasure will limit the flexibility of
> > restCall.getResponse(T), when the result comes back as a List. In my own
> > APIs I always return an Object but a top-level array is fairly common
> > practice.*
> >
> > The getResponse(Type,Type...) method should allow you to reconstruct
> > parameterized lists and maps.
> > For example:
> > getResponse(LinkedList.class,MyBean.class) - A list of beans.
> > getResponse(HashMap.class,String.class,LinkedList.class,MyBean.class) - A
> > hashmap with string keys and list of beans values.
> >
> > And you can also specify arrays...getResponse(MyBean[].class).
> > This is perfect. I remember now seeing the Type,Type… method in the
> > release notes and will try it out right away. Of course, I’m hoping that
> > RemoteableProxy can be used across the board, if so we won’t need
> > implementation classes that would have to do this at all.
> >
> >
> >
> > Refer to the Parser#parse(Object input, Type type, Type...args) javadocs
> > for info. The RestCall#getResponse() should act the same.
> >
> >
> >
> > On Mon, Mar 20, 2017 at 2:55 AM, sblackmon wrote:
> >
> > > Hello,
> > >
> > > I’m working on a class that uses RestClient to implement several
> > > contextually related methods.
> > >
> > > Each method has a POJO describing the request, and a POJO describing the
> > > response (some methods return a list of the response POJOs).
> > >
> > > As I understand from reading docs for 6.1.1-incubating-SNAPSHOT, while a
> > > RestServlet may bind multiple Serializers and Parsers, a RestClient may
> > > only bind one of each.
> > >
> > > So i guess that means I need to instantiate a different RestClient for
> > > each supported method, or bind a single custom Serializer and Parser
> > that
> > > can handle all of the request and response objects?
> > >
> > > I notice that it’s easy to create and re-use one HttpClient across
> > > RestClients, and there’s presumably no problem reusing all the member
> > > objects of RestClient, so there should be minimal performance overhead
> > to
> > > holding more than one on the class, or even creating a new RestClient
> > for
> > > every call is I don’t close the connection.
> > >
> > > I also am thinking that a new method on RestClient that allowed you to
> > > doGet from an request POJO, and have it converted into parameters (using
> > > BeanMap?) would help make this easier.
> > >
> > > Also wondering whether type erasure will limit the flexibility of
> > > restCall.getResponse(T), when the result comes back as a List. In my own
> > > APIs I always return an Object but a top-level array is fairly common
> > > practice.
> > >
> > > Anyway, I’ve pushed some early work to this branch, and I’d be
> > interested
> > > in feedback as I work through this, whether it’s on the right track to
> > > minimize code complexity and maximize performance for what we’re trying
> > to
> > > do.
> > >
> > > I’m not yet to the point of running any integration tests using this new
> > > implementation but hope to be soon.
> > >
> > > https://github.com/apache/incubator-streams/compare/
> > > master...steveblackmon:STREAMS-496
> > >
> > > Thanks in advance,
> > > Steve (Streams)
> >
> >
> >
> >
> > --
> > James Bognar
> >
>  
>  
>  
> --
> James Bognar


Mime
View raw message