creating a GWT ValueProxy and sending to a service method - gwt

I want to call a method on a Service with a ValueProxy param - if I do personProxy.setName("test") and then request.callFn(personProxy).fire(), the name property doesn't get passed to server.
Should I do a request.edit(personProxy) before setting the name or something else?
This is the implementation I'm using:
//somewhere in MyActivity.java ...
PersonProxy cp = requestFactory.myRequest().create(PersonProxy.class);
cp.setName("John Doe");
requestFactory.myRequest().doSomething(cp,"extra_param_value").fire(new Receiver<List<PersonProxy>>() {
#Override
public void onSuccess(List<PersonProxy> response) {
//response from server...
}
});
//------------------------
public interface MyRequestFactory extends RequestFactory {
MyRequest myRequest();
}
//------------------------
#ServiceName(value="com.server.MyService", locator="com.server.MyServiceLocator")
public interface MyRequest extends RequestContext {
public Request<Integer> doSomething(PersonProxy param, String extraParam);
}
//------------------------
public class MyServiceLocator implements ServiceLocator {
public Object getInstance(Class<?> clazz) {
return new MyService();
}
}
//------------------------
public class MyService {
public Integer doSomething(Person param, String extraParam) {
System.out.println("person.name="+param.getName()); ---> prints NULL!!! why?
return 0;
}
}
//------------------------
#ProxyForName(value="com.server.Person")
public interface PersonProxy extends ValueProxy {
String getName();
void setName(String name);
}
//-----------------------
public class Person {
public Person() {
super();
}
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Thanks.

The PersonProxy is being created by one instance of a RequestContext and used in another. Turns out there's a bug in AbstractRequestContext.retainArg() that should have thrown an exception to tell you about the API mis-use. Editable proxies aren't supposed to be usable between different RequestContext instances.
TreeRequest ctx = factory.treeRequest();
PersonProxy person = ctx.create(PersonProxy.class);
person.setName("John Doe");
ctx.doSomething(person, "more stuff");
As discussed on IRC, the -Dgwt.rpc.dumpPayload=true JVM flag can be turned on when trying to diagnose where data is going (or isn't).

Related

How to pass parameters in dagger2?

Like the following:
public class MainActivity extends Activity {
#Inject
User mUser1;
#Inject
User mUser2;
#Inject
User mUser3;
protected void onCreate(#Nullable Bundle savedInstanceState) {
// how to inject ...
......
Log.d("XXX",mUser1.getName());
Log.d("XXX",mUser2.getName());
Log.d("XXX",mUser3.getName());
}
}
public class User {
private final String name;
public User (String name) {
this.name = name;
}
public String getName() {
return name;
}
}
#Module
public class MainModule {
#Provides
User providesUser(String name) {
return new User(name);
}
#Provides
String providesUser() {
return "Jack";
}
}
#Component(modules = MainModule .class)
public interface ChildComponent {
void inject(MainActivity activity);
}
Above the code, only create three user object with same name, but i want to created with diff name
I try to use '#Qualifier', but it only distinguish constructor, can't transfer the name parameter
I try this in MainModule.java:
#Named("Yuri")
#Provides
User providesUser() {
return new User("yuri");
}
#Named("Warren")
#Provides
User providesUser() {
return new User("warren");
}
#Named("Jack")
#Provides
User providesUser() {
return new User("Jack");
}
But the method 'providesUser' is already defined.
Name parameter is final, can't modify
How should this situation be handled!!!
Method name doesn't matter. Only return type is what matters. So:
#Named("Yuri")
#Provides
User providesUserYuri() {
return new User("yuri");
}
#Named("Warren")
#Provides
User providesUserWarren() {
return new User("warren");
}
#Named("Jack")
#Provides
User providesUserJack() {
return new User("Jack");
}
If you want a dynamic name association, don't use #Qualifier, these are just for make differential of #Providers parameters.
Simply use Java POJO objects.
Now the User class would be like this:
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now in your Activity you can say this:
#Override
protected void onPostCreate(#Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
user1.setName("Ali");
user2.setName("Hossein");
user3.setName("Reza");
Log.d(TAG, user1.getName());
Log.d(TAG, user2.getName());
Log.d(TAG, user3.getName());
}

Unreachable security context using Feign RequestInterceptor

The goal is to attach some data from security context using RequestInterceptor, but the problem, that the calling SecurityContextHolder.getContext().getAuthentication() always returns null even though it is not null (I am sure 100%).
As I understand that's because the Interceptor is created and is being run in other thread.
How could I solve this problem and get actual data from security context?
My service:
#FeignClient(value = "api", configuration = { FeignConfig.class })
public interface DocumentService {
#RequestMapping(value = "/list", method = RequestMethod.GET)
DocumentListOperation list();
}
My FeignConfig class:
#Bean
public RequestInterceptor requestInterceptor() {
return new HeaderInterceptor(userService);
}
public class HeaderInterceptor implements RequestInterceptor {
private UserService userService;
public HeaderInterceptor(UserService userService) {
this.userService = userService;
}
#Override
public void apply(RequestTemplate requestTemplate) {
Authentication a = SecurityContextHolder.getContext().getAuthentication()
requestTemplate.header("authentication", a.toString());
}
}
I managed to figure it out, thanks to the article I found here
Firstly you need to initiliaze HystrixRequestContext HystrixRequestContext.initializeContext();.
You have to create your own Context in which you will store information you need to pass to Hystrix child threads.
Here is example:
public class UserHystrixRequestContext {
private static final HystrixRequestVariableDefault<User> userContextVariable = new HystrixRequestVariableDefault<>();
private UserHystrixRequestContext() {}
public static HystrixRequestVariableDefault<User> getInstance() {
return userContextVariable;
}
}
You have to register new concurrency strategy that would wrap Callable interface
#Component
public class CustomHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
public CustomHystrixConcurrencyStrategy() {
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
}
#Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new HystrixContextWrapper<T>(callable);
}
public static class HystrixContextWrapper<V> implements Callable<V> {
private HystrixRequestContext hystrixRequestContext;
private Callable<V> delegate;
public HystrixContextWrapper(Callable<V> delegate) {
this.hystrixRequestContext = HystrixRequestContext.getContextForCurrentThread();
this.delegate = delegate;
}
#Override
public V call() throws Exception {
HystrixRequestContext existingState = HystrixRequestContext.getContextForCurrentThread();
try {
HystrixRequestContext.setContextOnCurrentThread(this.hystrixRequestContext);
return this.delegate.call();
} finally {
HystrixRequestContext.setContextOnCurrentThread(existingState);
}
}
}
}
So before calling Callable object we set new thread's Context to parent's context.
After that is done you should be able to access your new defined context inside Hystrix child threads
User = UserHystrixRequestContext.getInstance().get();
Hope that will help someone.

Defining a resource assembler for a REST Spring HATEOAS controller

I'm trying to add HATEOAS links to a JSON resource served by a Spring REST controller.
I see I should use a resource assembler as described at https://github.com/spring-projects/spring-hateoas
The example displays a Person class and a PersonResource class.
I understand the PersonResource class is defined as:
public class PersonResource extends ResourceSupport {
}
What is then the Person class ? Is it a data domain class ?
In my case, I have defined an Admin class that is a REST domain class, and I specified it as having resource support:
public class Admin extends ResourceSupport {
private String firstname;
private String lastname;
private String email;
private String login;
private String password;
private String passwordSalt;
public Admin() {
}
public String getFirstname() {
return this.firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return this.lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getLogin() {
return this.login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPasswordSalt() {
return passwordSalt;
}
public void setPasswordSalt(String passwordSalt) {
this.passwordSalt = passwordSalt;
}
public EventAdmin toEventAdmin() {
EventAdmin eventAdmin = new EventAdmin();
BeanUtils.copyProperties(this, eventAdmin);
return eventAdmin;
}
public static Admin fromEventAdmin(EventAdmin eventAdmin) {
Admin admin = new Admin();
BeanUtils.copyProperties(eventAdmin, admin);
return admin;
}
}
My REST controller sees only this Admin class as it is a REST domain class. It does not know, and should not know, of anything data domain class.
So I wonder how to use the resource assembler support here.
I don't understand why I should have an additional data domain Admin class here.
kind Regards,
Following Mike's answer here is how my controller now looks like:
#RequestMapping(method = RequestMethod.POST, produces = "application/json; charset=utf-8")
#ResponseBody
public ResponseEntity<Admin> add(#RequestBody Admin admin, UriComponentsBuilder builder) {
AdminCreatedEvent adminCreatedEvent = adminService.add(new CreateAdminEvent(admin.toEventAdmin()));
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "application/json; charset=utf-8");
responseHeaders.setLocation(builder.path("/admin/{id}").buildAndExpand(adminCreatedEvent.getAdminId()).toUri());
Admin createdAdmin = adminResourceAssembler.toResource(adminCreatedEvent.getEventAdmin());
ResponseEntity<Admin> responseEntity = new ResponseEntity<Admin>(createdAdmin, responseHeaders, HttpStatus.CREATED);
return responseEntity;
}
Before, instead of using the resource assembler I was doing a:
Admin createdAdmin = Admin.fromEventAdmin(adminCreatedEvent.getEventAdmin());
createdAdmin.add(linkTo(methodOn(AdminController.class).add(createdAdmin, builder)).withSelfRel());
But it was not giving me the resource id in the url.
Your ResourceAssembler implementation needs to know about both the data domain class and the REST domain class, because its job is to convert the former to the latter.
If you want to keep knowledge of your data classes out of your controller, you could make a resource conversion service which would retrieve the data from the repo and use a ResourceAssembler to turn it into resources that the controller can know about.
#Component
public class AdminResourceAssembler extends ResourceAssemblerSupport<Admin, AdminResource> {
public AdminResourceAssembler() {
super(AdminController.class, AdminResource.class);
}
public AdminResource toResource(Admin admin) {
AdminResource adminResource = createResourceWithId(admin.getId(), admin); // adds a "self" link
// TODO: copy properties from admin to adminResource
return adminResource;
}
}
#Service
public class AdminResourceService {
#Inject private AdminRepository adminRepository;
#Inject private AdminResourceAssembler adminResourceAssembler;
#Transactional
public AdminResource findOne(Long adminId) {
Admin admin = adminRepository.findOne(adminId);
AdminResource adminResource = adminResourceAssembler.toResource(admin);
return adminResource;
}
}
#Controller
#RequestMapping("/admins")
public class AdminController {
#Inject private AdminResourceService adminResourceService;
#RequestMapping(value="/{adminId}", method=RequestMethod.GET)
public HttpEntity<AdminResource> findOne(#PathVariable("adminId") Long adminId) {
AdminResource adminResource = adminResourceService.findOne(adminId);
return new ReponseEntity<>(adminResource, HttpStatus.OK);
}
}

POJO information lost during RPC call (GWT)

I am having issues with RPC calls and GWT. Essentially, I have a Person class (common code between client and server) that is created in the client side web code, sent to the server code via an RPC call, and then saved to a DB (OrientDB). I have verified that the following work:
RPC call - I am able to send info to the server and retrieve info from the server
save to DB - have verified that a Person object is saved to the DB
Where I am having issues is the transfer of the POJO from the client to the server. I have verified that the POJO's properties are intact right before it is sent to the server, however, the object passed to the server contains null values for all properties. Essentially, the class is transferred but the information is not. It then saves to the DB, but obviously without any relevant information contained within it.
I will copy what I feel is relevant below, please let me know what else I can provide to make this problem easier to identify. Note these are still in a testing state, so mind the comments :)
Any idea why my POJO's information is being lost in translation?
Person object, followed by the abstract class it inherits from:
public class Person extends org.matesweb.shared.AbsPerson implements Serializable
{
#Id
private String id; // DON'T CREATE GETTER/SETTER FOR IT TO PREVENT THE CHANGING BY THE USER APPLICATION,
// UNLESS IT'S NEEDED
//sets new user details
public void setPerson(String fIrstName, String mIdInit, String lAstName, String email, String password)
{
firstName = fIrstName;
middleInitial = mIdInit;
lastName = lAstName;
}
/*getter and setter methods - required for every
* field due to restrictions imposed by OrientDB*/
public Object getId()
{
String tmp;
tmp = id.toString();
return tmp;
}
//end class
}
public class AbsPerson implements Serializable
{
String firstName;
String middleInitial;
String lastName;
// public sys.Login login;
public org.matesweb.shared.Group[] groups;
private org.matesweb.shared.Purchase[] purchases;
/*this method adds a new purchase to the purchases variable*/
/* public void addPurchase(float price, String description)
{
people.Purchase newPurchase = new people.Purchase(login, price, description);
}
*/
/*adds a person to a group by comparing the passed in group ID and PWD*/
public void addGroup(String groupID, String groupPWD)
{
//compare group ID with group PWD to add a user to the group
}
/*getter and setter methods - required for every
* field due to restrictions imposed by OrientDB*/
public String getFirstName()
{
return firstName;
}
public void setFirstName(String name)
{
firstName = name;
}
public String getMiddleInitial()
{
return middleInitial;
}
public void setMiddleInitial(String midInit)
{
middleInitial = midInit;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String ln)
{
lastName = ln;
}
/*
public sys.Login getLogin()
{
return login;
}
public void setLogin(sys.Login log)
{
login = log;
}
*/
public org.matesweb.shared.Group[] getGroups()
{
return groups;
}
public void setGroups(org.matesweb.shared.Group[] gro)
{
groups = gro;
}
public org.matesweb.shared.Purchase[] getPurchases()
{
return purchases;
}
public void setPurchases(org.matesweb.shared.Purchase[] purch)
{
purchases = purch;
}
}
Service
package org.matesweb.client;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
import org.matesweb.shared.Person;
#RemoteServiceRelativePath("peopleService")
public interface PeopleService extends RemoteService {
//test services
String stringTest(String outgoingString);
Person getPerson(String persId);
//production services
String savePerson(Person p);
}
ServiceAsync
import com.google.gwt.user.client.rpc.AsyncCallback;
import org.matesweb.shared.Person;
public interface PeopleServiceAsync
{
//tests
void stringTest(String outgoingString, AsyncCallback<String> incomingString);
void getPerson(String persId, AsyncCallback<Person> retPerson);
//production services
void savePerson(Person p , AsyncCallback<String> st);
}
ServiceImpl call for this particular method:
//production calls
#Override
public String savePerson(Person p) {
String st = ioObj.saveObj(p);
if(st.equals("Success")){
return "Your information has been saved successfully!";
} else{
return "Something has gone wrong on our end... Sorry! Error:<br /> " + st;
}
}
and finally, the call itself
private static void savePerson(Person p)
{
// Initialize the service proxy.
if (peopleSvc == null) {
peopleSvc = GWT.create(PeopleService.class);
}
//resets status
st="";
// Set up the callback object.
AsyncCallback<String> callback = new AsyncCallback<String>() {
#Override
public void onFailure(Throwable caught) {
st = caught.getMessage();
Label stLabel= new Label(st);
personTable.setWidget(3,1,stLabel);
}
#Override
public void onSuccess(String result) {
st = result;
HTML stLabel= new HTML(st);
joinPanel.add(stLabel);
}
};
// Make the call to the people service.
peopleSvc.savePerson(p, callback);
}
I was able to fix this issue by implementing GWT's IsSerializable interface. I also removed the Serializable interface from the Person class and let it inherit IsSerializable from the abstract class it inherits from.

RF 'Unfrozen bean with null RequestContext' when using a ValueProxy param with JsonRpc dialect

When i try to send a request that uses a ValueProxy params i'm getting this 'Unforzen bean' exception. I don't know if this exception is because a bug with RF using JsonDialect or i'm doing something wrong... ¿Some help?
java.lang.AssertionError: Unfrozen bean with null RequestContext
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext.checkStreamsNotCrossed(AbstractRequestContext.java:981)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext.editProxy(AbstractRequestContext.java:509)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext.edit(AbstractRequestContext.java:502)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext.retainArg(AbstractRequestContext.java:1230)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext.access$2(AbstractRequestContext.java:1223)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext$JsonRpcPayloadDialect.addInvocation(AbstractRequestContext.java:202)
at com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext.addInvocation(AbstractRequestContext.java:661)
at es.xxxx.taller.client.Taller_SomeRequestContextImpl.SomeCall(Taller_SomeRequestContextImpl.java:29)
at es.xxxx.taller.client.Taller.onModuleLoad(Taller.java:417)
public class SomeEntryPoint implements EntryPoint {
#JsonRpcProxy
public interface SomeProxy extends ValueProxy {
String getSomeProperty();
void setSomeProperty(String value);
}
#JsonRpcProxy
public interface VoidProxy extends ValueProxy {
}
public interface SomeAutoBeanFactory extends AutoBeanFactory {
SomeAutoBeanFactory INSTANCE = GWT.create(SomeAutoBeanFactory.class);
AutoBean<SomeProxy> someProxy();
}
public interface SomeRequestFactory extends RequestFactory {
SomeRequestFactory INSTANCE = GWT.create(SomeRequestFactory.class);
SomeRequestContext context();
}
#JsonRpcService
public interface SomeRequestContext extends RequestContext {
SomeCall SomeCall(SomeProxy proxy);
#JsonRpcWireName(value = "SomeCall")
public interface SomeCall extends Request<VoidProxy> {
}
}
public void onModuleLoad() {
SomeProxy someProxy = SomeAutoBeanFactory.INSTANCE.someProxy().as();
someProxy.setSomeProperty("someValue");
SomeRequestFactory.INSTANCE.context().SomeCall(someProxy).fire();
}
}
Proxies should be created by a RequestContext, not an AutoBeanFactory! Using the JsonRpc dialect doesn't change how you use RequestFactory.
public void onModuleLoad() {
SomeRequestContext ctx = SomeRequestFactory.INSTANCE.context();
SomeProxy someProxy = ctx.create(SomeProxy.class);
someProxy.setSomeProperty("someValue");
ctx.SomeCall(someProxy).fire();
}