juneau-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From James Bognar <james.bog...@salesforce.com>
Subject Re: Advice regarding Juneau RestClient, RestCall, Serializers, best practices
Date Wed, 22 Mar 2017 23:55:09 GMT
Hi Steve,

So there's already a @Remoteable annotation that can be applied to
Classes/Interfaces/Methods to fine-tune which methods are visible for
proxying.  Maybe add to this annotation?

@Remoteable(path = "/statuses")
public interface Statuses {
  public List lookup(StatusesLookupRequest parameters);
}

I've added a confluence design document to discuss further.  Feel free to
edit...
https://cwiki.apache.org/confluence/display/JUNEAU/Remote+proxy+REST+methods


On Wed, Mar 22, 2017 at 3:01 PM, sblackmon <sblackmon@apache.org> wrote:

>
> 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
>
>


-- 
James Bognar

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message