gwt rpc serialize generic class - gwt

I need to pass a class object through the gwt rpc connection as a generic but it seems that rpc does not cooperate with it. The class is serialized using the java.io.Serializable. I have checked it using the gwt IsSerializable but i still have the error.
Here is my code
MySource.java
#SuppressWarnings("serial")
#PersistenceCapable
#Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
public abstract class MySource implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent
private String userId;
#Persistent
private String title;
#Persistent
private String description;
#Persistent
private String blobKey;
#Persistent
private String youtubeLink;
#Persistent
private String personalLink;
#Persistent
private Date submitedDate;
#Persistent
private float price;
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getBlobKey() {
return blobKey;
}
public void setBlobKey(String blobKey) {
this.blobKey = blobKey;
}
public String getYoutubeLink() {
return youtubeLink;
}
public void setYoutubeLink(String youtubeLink) {
this.youtubeLink = youtubeLink;
}
public String getPersonalLink() {
return personalLink;
}
public void setPersonalLink(String personalLink) {
this.personalLink = personalLink;
}
public Date getSubmitedDate() {
return submitedDate;
}
public void setSubmitedDate(Date submitedDate) {
this.submitedDate = submitedDate;
}
public Long getId() {
return id;
}
}
AndroidSource.java
#SuppressWarnings("serial")
#PersistenceCapable
public class AndroidSource extends MySource{
public AndroidSource() {
super();
}
}
CategoryBrowseService.java which is the remoteservice model
#RemoteServiceRelativePath("categoryService")
public interface CategoryBrowseService extends RemoteService{
ArrayList<MySource> getSourceList(Class<? extends MySource> classType);
}
CategoryBrowseServiceAsync.java
public interface CategoryBrowseServiceAsync {
void getSourceList(Class<? extends MySource> classType,
AsyncCallback<ArrayList<MySource>> callback);
}
CategoryBrowsePresenter.java where the rpc is called
private void retrieveSources(Class<? extends MySource> classType) {
CategoryBrowseServiceAsync rpcService = GWT.create(CategoryBrowseService.class);
rpcService.getSourceList(classType, new AsyncCallback<ArrayList<MySource>>() {
#Override
public void onFailure(Throwable caught) {
Window.alert("Ooops!!!Sorry!Something went wrong.I am still beta!");
}
#Override
public void onSuccess(ArrayList<MySource> result) {
sourceList = result;
display.setSourceContent(sourceList);
}
});
}
CategoryBrowseServiceImpl.java
#SuppressWarnings("serial")
public class CategoryBrowseServiceImpl extends RemoteServiceServlet implements CategoryBrowseService{
private SourceDatastore dataStore;
public CategoryBrowseServiceImpl() {
dataStore = new SourceDatastore();
}
#Override
public ArrayList<MySource> getSourceList(Class<? extends MySource> classType) {
return dataStore.getSources(classType);
}
}
Here is the error that i get.
Compiling module com.sourcebay.SourceBay
Scanning for additional dependencies: file:/home/santaris/workspace/SourceBay/src/com/sourcebay/client/presenter/mybay/browse/CategoryBrowsePresenter.java
Computing all possible rebind results for 'com.sourcebay.client.model.mybay.browse.CategoryBrowseService'
Rebinding com.sourcebay.client.model.mybay.browse.CategoryBrowseService
Invoking generator com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator
Generating client proxy for remote service interface 'com.sourcebay.client.model.mybay.browse.CategoryBrowseService'
[ERROR] java.lang.Class<T> is not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' or 'java.io.Serializable' nor does it have a custom field serializer (reached via java.lang.Class<? extends com.sourcebay.shared.source.MySource>)
[ERROR] java.lang.Class<T> has no available instantiable subtypes. (reached via java.lang.Class<? extends com.sourcebay.shared.source.MySource>)
[ERROR] subtype java.lang.Class<T> is not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' or 'java.io.Serializable' nor does it have a custom field serializer (reached via java.lang.Class<? extends com.sourcebay.shared.source.MySource>)
[ERROR] Errors in 'file:/home/santaris/workspace/SourceBay/src/com/sourcebay/client/presenter/mybay/browse/CategoryBrowsePresenter.java'
[ERROR] Line 75: Failed to resolve 'com.sourcebay.client.model.mybay.browse.CategoryBrowseService' via deferred binding
The paradox is that when i am running my application through the eclipse plugin everything works fine. Could anyone help me please? I have checked to fix the problem through the DTO solution without any success. Moreover i have tried to implement a CustomFieldSerializer as Google suggests without any success too.
Thanks in advance,
Stefanos Antaris
P.S. Sorry for the huge post :-)

Well the problem is that you trying to transport a Class object over the network. I have no idea why it is working in dev mode (I've tried it on local project and it failed), but it shouldn't work. You have to use something else instead of Class name for example String, which will contain a name of class. Theoretically it can work if you create CustomFieldSerializer for Class, but using String instead of Class will be easier.

Classes with persistence annotations can work well on the server side, but if you want to pass its data to the client you must create a plain java serializable class to transport data from server to client.
As noted in the previous answer, persistence annotations are not supported in the client side, as they cannot be translated to equivalent javascript code (and it makes sense since the client doesn't have the responsability of persistence).

It could be that the persistence-related annotations in MySource.java make it implossible to translate to javascript. Try removing the annotations to see if it's related. Also make sure that MySource.java is in a package declared as translatable in the module xml file ("source" element).

Try using implements Serializable for defining you class.
I mean like this:
public class AndroidSource extends MySource implements Serializable{
public AndroidSource() {
super();
}
}

Your RPC Services must deal just with Serializable Objects. Domain classes are not translatable to JavaScript So GWT can't send and receive via network (RPC Protocole) such objects. You need to create DTO classes (wich shadows domain class) implementing java.io.Serializable and then reconfigure all your RPC Service to use in input DTOs and output DTOs. Good Luck for your project.

Related

Dynamic injection using #SpringBean in wicket

I have a form that based on collected information generates a report. I have multiple sources from which to generate reports, but the form for them is the same. I tried to implement strategy pattern using an interface implementing report generator services, but that led to wicket complaining about serialization issues of various parts of the report generator. I would like to solve this without duplicating the code contained in the form, but I have not been able to find information on dynamic injection with #SpringBean.
Here is a rough mock up of what I have
public class ReportForm extends Panel {
private IReportGenerator reportGenerator;
public ReportForm(String id, IReportGenerator reportGenerator) {
super(id);
this.reportGenerator = reportGenerator;
final Form<Void> form = new Form<Void>("form");
this.add(form);
...
form.add(new AjaxButton("button1") {
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit(AjaxRequestTarget target)
{
byte[] report = reportGenerator.getReport(...);
...
}
});
}
}
If I do it this way, wicket tries to serialize the concrete instance of reportGenerator. If I annotate the reportGenerator property with #SpringBean I receive Concrete bean could not be received from the application context for class: IReportGenerator
Edit: I have reworked implementations of IRerportGenerator to be able to annotate them with #Component and now I when I use #SpringBean annotation I get More than one bean of type [IReportGenerator] found, you have to specify the name of the bean (#SpringBean(name="foo")) or (#Named("foo") if using #javax.inject classes) in order to resolve this conflict. Which is exactly what I don't want to do.
I think the behavior you're trying to achieve can be done with a slight workaround, by introducing a Spring bean that holds all IReportGenerator instances:
#Component
public class ReportGeneratorHolder {
private final List<IReportGenerator> reportGenerators;
#Autowired
public ReportGeneratorHolder(List<IReportGenerator> reportGenerators) {
this.reportGenerators = reportGenerators;
}
public Optional<IReportGenerator> getReportGenerator(Class<? extends IReportGenerator> reportGeneratorClass) {
return reportGenerators.stream()
.filter(reportGeneratorClass::isAssignableFrom)
.findAny();
}
}
You can then inject this class into your Wicket page, and pass the desired class as a constructor-parameter. Depending on your Spring configuration you might need to introduce an interface for this as well.
public class ReportForm extends Panel {
#SpringBean
private ReportGeneratorHolder reportGeneratorHolder;
public ReportForm(String id, Class<? extends IReportGenerator> reportGeneratorClass) {
super(id);
IReportGenerator reportGenerator = reportGeneratorHolder
.getReportGenerator(reportGeneratorClass)
.orElseThrow(IllegalStateException::new);
// Form logic omitted for brevity
}
}
As far as I am able to find, looking through documentation and even the source for wicket #SpringBean annotation, this isn't possible. The closest I got is with explicitly creating a proxy for a Spring bean based on class passed. As described in 13.2.4 Using proxies from the wicket-spring project chapter in Wicket in Action.
public class ReportForm extends Panel {
private IReportGenerator reportGenerator;
private Class<? extends IReportGenerator> classType;
private static ISpringContextLocator CTX_LOCATOR = new ISpringContextLocator() {
public ApplicationContext getSpringContext() {
return ((MyApplication)MyApplication.get()).getApplicationContext();
}
};
public ReportForm(String id, Class<? extends IReportGenerator> classType) {
super(id);
this.classType = classType;
final Form<Void> form = new Form<Void>("form");
this.add(form);
...
form.add(new AjaxButton("button1") {
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit(AjaxRequestTarget target)
{
byte[] report = getReportGenerator().getReport(...);
...
}
});
}
private <T> T createProxy(Class<T> classType) {
return (T) LazyInitProxyFactory.createProxy(classType, new
SpringBeanLocator(classType, CTX_LOCATOR));
}
private IReportGenerator getReportGenerator() {
if (reportGenerator = null) {
reportGenerator = createProxy(classType);
}
return reportGenerator;
}
}

Cannot init the #FormBean field type as List<Integer> or Integer[]

import javax.ws.rs.FormParam;
import com.alibaba.fastjson.JSON;
Hi guys, The JAX-RS seems cannot init the list field in resteasy 2.x/3.x/4.x automatically,
Here is my VO used in servce
public class OrderVO {
#FormParam("id")
private long id;
#FormParam("title")
private String title;
#FormParam("product")
private List<Integer> product;
...
public String toString() {
return JSON.toJSONString(this);
}
}
And here is my service method:
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED+";charset=UTF-8")
#Path("/create")
public InsertResult saveOrder(#BeanParam OrderVO order) {
logger.debug(order.toString());
}
Other parameters are auto inited except the List or Integer[], and can anyone tell me why....

Eclipse cannot see beyond Beans' models' fields from AbstractBean/AbstractEntity in Facelets

I am running jee-2019-06 version of Eclipse. Here is my Model-Bean-Facade structure:
I am not including getters/setters for brevity.
My Identifiable:
/** Identifiable interface for Entities; used for DAO - Service transitions. */
public interface Identifiable<T extends Serializable> extends Serializable {
public T getId(); // identifiable field
public String getTitle(); // user friendly name (maybe different from actual entity's name)
public String getName(); // every entity has a name
public String getDescription(); // every entity should have a description
}
My Abstract Bean:
public abstract class AbstractBean<T extends Identifiable<?>> {
protected final transient Logger log = Logger.getLogger(this.getClass());
private final Class<T> clazz;
private T model;
public AbstractBean(final Class<T> clazz) {
this.clazz = clazz;
}
protected T createInstance() {
try {
return this.clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
this.log.error("[" + this.getClass().getSimpleName() + ".createInstance()] : Error : {} {}", e.getMessage(), e);
return null;
}
}
protected AbstractFacade<T> getFacade() {
return null;
}
}
My Abstract Facade:
#Transactional
public abstract class AbstractFacade<T extends Identifiable<?>> {
protected final transient Logger log = Logger.getLogger(this.getClass());
protected final Class<T> clazz;
public AbstractFacade(final Class<T> clazz) {
this.clazz = clazz;
}
}
My Bean:
#Named
#ViewScoped
public class CarBean extends AbstractBean<Car> {
#Inject
private CarFacade facade;
public CarBean(){
super(Car.class);
}
#Override
public CarFacade getFacade() {
return this.facade;
}
}
My AbstractEntity:
#MappedSuperclass
public abstract class AbstractEntity implements Identifiable<Integer> {
private Integer id;
private String name;
private String description;
public AbstractEntity() {
}
}
My Entity:
public class Car extends AbstractEntity {
public Car() {
}
}
I have no problems in showing the value to the user.
I have problems in validation and hyperlink in Eclipse:
<h:outputText value="#{carBean.model.name}" />
Facelet validator cannot validate name of model. It yellow underlines name. Also, I cannot Ctrl + click to activate hyperlink on name.
I saw on another developer's eclipse that both of my problems were not issues at all. I compared all the tools installed in both Eclipses and could not find anything relevant.
My question: what tools do I have to install or what settings/adjustments am I missing?
Please note: I do not want to disable the validator and I want to be able to hyperlink fields in facelet so that I will access the field using Ctrl + click.
Thank you.

How to read the new XStreamConverter parameters?

Since version 1.4.2 of XStream, the XStreamConverter annotation takes additional parameters (very good feature and just what I need).
#XStreamConverter(value=CustomXStreamConverter.class, strings={xyz"})
private List<String> phones;
But how can I read this values (xyz) in my custom converter?
public class CustomXStreamConverter implements Converter {
//?
}
I figure out the solution, just override the class constructor in order to receive the parameter.
public class CustomXStreamConverter implements Converter {
private String alias;
public ListToStringXStreamConverter(String alias) {
super();
this.alias = alias; //xyz
}
//...

How to edit a Set<? extends EntityProxy> with GWT Editor framework?

for sake of simplicity:
public class Person
{
String name;
Set<Address> addresses;
}
public class Address
{
String city;
String street;
}
with and matching
public interface PersonProxy extends EntityProxy
{
public String getName();
public Set<AdressProxy> getAddresses();
}
and
public interface AdressProxy extends EntityProxy
{
public String getCity();
public String getStreet();
}
I got UiBuinder classes to edit AddressProxy
and it clear to me how to use ListEditor in case if I got List but data is Set in the Person class
how do I use Editor Framework to edit them?
Or may be how do I convert Set to List when it becomes PersonProxy?
I did an attempt to put a kind of adapter Editor class that would implement
LeafValueEditor<Set<AddressProxy>>
and then inside of the LeafValueEditor.setValue() move to a List and start a new driver.edit() on a separate Editor hierarchy that takes care of List editing but with now luck.
You should create a CompositeEditor<Set<AddressProxy>, AddressProxy, AddressEditor>, similar to a ListEditor but handling a Set instead of a List.
I suppose you could somehow delegate to a ListEditor though I'm really not sure.
I've done it with Points and Routes (one Route contains N Points):
Route (Composite):
#UiField
TextBox name;
#Ignore
#UiField
FlexTable listPoints;
PointsEditor pointsEditor = new PointsEditor();
....
pointsEditor.add(String id);
PointsEditor:
public class PointsEditor implements HasRequestContext<List<PointProxy>>, ValueAwareEditor<List<PointProxy>> {
List<PointProxy> points = new ArrayList<PointProxy>();
public void add(String id) {
PointProxy point = ctx.create(PointProxy.class);
point.setId(id);
points.add(point);
}
Route (server side):
#Embedded
private List<Point> points = new ArrayList<Point>();
RouteProxy
public interface RouteProxy extends EntityProxy {
abstract List<PointProxy> getPoints();
abstract void setPoints(List<PointProxy> points);
PointProxy
public interface PointProxy extends ValueProxy {
...
}