-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support jackson-gwt decoder #6
Comments
Hi! yep, I need to support other codecs, to support jackson-gwt autorest need to generate an ObjectMapper, this will make autorest coupled with jackson-gwt, so need to be decoupled. I have various strategies in mind BUT... For now, autorest works perfect with JsInterop, is super small and almost zero-overhead lib only using annotation processor. But jackson-gwt uses generators so for now if you want to use jackson-gwt just use resty-gwt, both use generators and both works perfectly together. I'll promise than if jackson-gwt start using processor I'll make a release supporting it bc at that point both can be used "theoretically" with j2cl. Also not that IMO this lib, and more or less resty-gwt can be used only with JAX-RS as the exposed API, if you do that you should change both without pain, so again for now using resty-gwt with jackson-gwt is the best option as using autorest with JsInterop 👍. To do that I strongly recomended to use resty-gwt DirectInterfaces, those are a bit anoying because of the REST.withCallback().call(), and hehe bc if you use it you are coupled all over the place with resty-gwt. To decouple that you can use https://github.com/ibaca/restygwt-rxadapter. I think is broken, I make some changes to make it better but this was the point when I start autorest, if you are going to use we can fix it 🙌. |
Hi, I understand your doubts about jackson-gwt and it "old school" code generation. Regarding JAX-RS, direct interface and decoupling GWT from server side I am using the "double interface" trick: I have base interface (ClientService just a flag interface) in shared project e.g. public interface AboutService extends ClientService {
String SRV_PATH = "/about/all"; //$NON-NLS-1$
List<AboutModel> getAll();
} that has been implemented by Spring REST controller and is completely independent of GWT (no RESTY or JAX-RS dependencies) and the REST interface in gwt project that extends the base interface and adapts it to GWT like below. public interface AboutRestService extends AboutService, DirectRestService {
@GET
@Path(SRV_ROOT + SRV_PATH)
@Override
List<AboutModel> getAll();
} Will check your adapter. |
And with AutoREST did you use just one common JAX-RS interface? or did you need to do the 2-interfaces trick? |
I have obtained pretty great results with JsInterop, maybe if you ask about what you miss from Jackson or about JsInterop friendly alternatives you might find that Jackson is not so needed. |
Hmm... I am pretty new with RxJava and never use it before :) So the progress is not very fast. I take the small project with GWT and Resty and try to replace the Resty with AutoRest. At least one call. So the public interface LogUiLoggerService extends RestService {
@GET
@Path("/rest/loggers/{inherited}/{filter}")
void get(@PathParam("filter") String filter, @PathParam("inherited") boolean inherited, MethodCallback<List<LogUiLogger>> callback);
@POST
@Path("/rest/logger")
void set(LogUiLogger loger, MethodCallback<Boolean> callback);
@GET
@Path("/rest/suggestLogger/{query}/{limit}")
void requestSuggestion(@PathParam("query") String query, @PathParam("limit") int limit, MethodCallback<List<String>> callback);
} was duplicate with @AutoRestGwt
@Path("/rest")
public interface LogUiLoggers {
@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
public static class Logger {
public String name;
public String level;
public String effectiveLevel;
public Appender[] appenders;
public boolean additive;
@JsOverlay
public final LogUiLogger to() {
final LogUiLogger result = new LogUiLogger();
result.setAdditive(additive);
result.setAppenders(Stream.of(appenders).map(Appender::to).collect(Collectors.toList()));
result.setEffectiveLevel(LogUiLevel.valueOf(effectiveLevel));
result.setLevel(level == null ? null : LogUiLevel.valueOf(level));
result.setName(name);
return result;
}
}
@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
public static class Appender {
public String name;
public String className;
@JsOverlay
public final LogUiAppender to() {
final LogUiAppender result = new LogUiAppender();
result.setClassName(className);
result.setName(name);
return result;
}
}
@GET
@Path("/loggers/{inherited}/{filter}")
Observable<Logger> get(@PathParam("filter") String filter, @PathParam("inherited") boolean inherited);
@POST
@Path("/logger")
Single<Boolean> set(LogUiLogger loger);
@GET
@Path("/suggestLogger/{query}/{limit}")
Observable<String> requestSuggestion(@PathParam("query") String query, @PathParam("limit") int limit);
} and srv.get(getFilter(), getInherited(), new AbstractMethodCallback<List<LogUiLogger>>(bus) {
@Override
public void onSuccess(final Method method, final List<LogUiLogger> result) {
dataGrid.setRowCount(result.size());
updateRowData(start, result.subList(start, result.size()));
}
}); was replaced with final Observable<Logger> ls = logUiLoggers.get(getFilter(), getInherited());
ls.count().subscribe(dataGrid::setRowCount);
ls.skip(start).map(Logger::to).toList().subscribe(list -> updateRowData(start, list)); I will need to create special objects |
Yep, this is how AutoREST best works right now. Duplicating the DTO and a domain object is annoying, so you need to try to use the DTO, but also be careful to not to influence the DTO with logic, if you need inheritance and logic then this is the moment when you should stop using the DTO. It is a different approach and it requires various refactors in your code so I also recommend making the migration progressively, one service interface each time. Even if you get forced to have a DTO object and a Domain object this can be an advantage too bc sometimes you need something specific in the domain, may be motivated for performance or to easier UI handling reasons, that might change your API, so if there are 2 models, the API end up more... API friendly and the domain fits perfectly to be used in the widgets. But to be honest, in our project, we most of the time reuse the DTOs and usually end up in the table, chart or form directly. Sometimes we just wrap the DTO in a decorator/adaptor class to add more data or just to adapt to some other type, but we do not have many duplicated models in the client. This kind of models fits perfectly with D3 and similar libs. About RxJava, you should be careful, RX is usually lazy and reusable (not always true! it just an option, so each place that returns an RX type, like an Observable, should specify what it is going to happen). But in case of AutoREST it is lazy and reusable which means that srv.get(…) returns an Observable, but at that point no request has been fired, each time you subscribe to the observable it will fire a network request so in your code example you are actually making 2 requests to the server. You really need to learn a bit of RxJava to actually get an advantage of this, but for now, I just say that you can, for example, change those lines with this and it will just make 1 request.
The cache will subscribe only one time to the actual request and will remember the result so the 2 subscribe call will obtain the same result making only one server request. |
Ok. Thank you for your suggestions regarding RX. I will update the code with cache() method :) But the main issue is the conversation - for such example it is easy, but even this example contains 2 string to enum and one array to list conversation, second point the getters/setters - I miss them :) In other projects, I use the DTOs but they are significant complex (in details) than simple data structure with public fields. I will check the restygwt-rxadapter for now. Hope @nmorel will migrate the project to the APT soon :) (nmorel/gwt-jackson#63) One more question. I see the rxjava-gwt project is migrating (migrated?) to RxJava2, but AutoREST uses the previous version of rxjava-gwt based on RxJava1. What is the status? |
I have already migrated all tools (rxjava-gwt, rxgwt and autorest) to RxJava 2 the problem is that gwt uses eclipse jdt to compile and it contains a bug that make RxJava 2 compilation super slow, so for now you need to use a custom build (more info in the rxjava-gwt project). Gwt just need to upgrade to the last Jdt, And this is almost mandatory to add Java9 Lang features so y hope this will occur in gwt 2.9. The custom build only fixes that so IMO should not be a problem. Anyway, yep using RxJava 2 is a good idea! |
Hi |
Yep, hehe we feel that way too, but any solution will require a parallel instance tree, I mean, first parsing as JSON (which is the first and unique step right now) and second instantiating new types (so no JsType native) and setting the actual values. And I still think it doesn't worth. What we do is to add the "parsing" method just next to the native field, like... @JsType(native = true, package = GLOBAL, name = "Object") class SomeDto {
public int type;
@JsOverlay @JsonIgnore
public final SomeEnum getTypeEnum() { return SomeEnum.values()[type]; }
} We add overlay for setter and getters when the type dosen match, and for array, usually T[] returned as Stream. |
yeah, I have an annotation processor that generates methods like that in an interface, I can then just add the interface to the class. But still.... |
For enums we do something similar, but we use the string representation instead: Let's assume we have enum:
This is a very verbose representation of what we do; our models are generated to automatically create the |
We think we managed to do what was originally requested in this thread (seamless integration of autorest with gwt-jackson-apt) in this pull request: #11 . I won't repeat our approach here - it is all described in the comments of #11. The one thing I'll emphasize again is only that our approach is keeping autorest completely unaware of gwt-jackson-apt (and the other way around of course). It is as minimal as it gets, while still allowing to use the two libs together. ... or even use autorest with whatever serialization/deserialization policy you have in mind - including just continue using straight JSON-compatible |
@ivmarkov have you checked this |
Unless we are reading your code incorrectly, domino-autorest-jackson does not really support deserialization of the following:
Basically, you only support either non-parameterized Java Beans or Java arrays of non-parameterized Java Bean as a return type of each method, i.e.
But not really:
... and that's fair, because autorest currently only provides a The thing is, without extended type information, and in the presence of Java erasure you can only get that far - no Java collections, and in general - no instantiated parameterized Java beans. but we need those - especially the collections. Hence why we introduced the One more thing: gwt-jackson-apt currently has restrictions which e.g. prohibit it from generating Object Mappers for generified (but instantiated) types. I.e. it currently cannot generate
It will therefore probably not be a surprise that we have also extended gwt-jackson-apt so that it can generate ObjectMapper implementations for all of the above types, and more. The pull request will land to your code repo in a few hours. Thoughts/feedback greatly appreciated! |
@ivmarkov such PR is highly appreciated. thank you |
Native JSON deserialization is quite fast, but have some limitation. I need to have the same domain model on both sides (browser and server) and Jackson is the more flexible solution.
The text was updated successfully, but these errors were encountered: