Help! How to use model objects with generics

204 views
Skip to first unread message

brt...@gmail.com

unread,
Sep 14, 2015, 3:26:02 PM9/14/15
to caelum-vraptor-en
I have inherited a database model where the primary keys are sometimes integers and sometimes strings. To simplify our domain object model, I've used a class generic to abstract away whether the "id" property of the class is a number or a string. This code works great and I've used it before in past projects.

Vraptor doesn't seem to like either the inheritance or the generic typing (I'm guessing it's the generics). If I remove the extends on the Brand class and put id, created, modified, and status right into the class, Vraptor works as advertised. If I try to use the Brand class as shown below I get the exception sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class.

What are my options?




The model looks like this

public class Brand extends AbstractPersistedObject<Integer> {
private static final long serialVersionUID = 1L;
private String name;
public Brand() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

import java.io.Serializable;
import java.util.Date;

public interface Persisted<T> extends Serializable {
public boolean isPersisted();
public boolean equalsPrimaryKey(Persisted other);
public T getId();
public void setId(T id);
public Date getCreated();
public void setCreated(Date created);
public String getCreatedBy();
public void setCreatedBy(String createdBy);
public Date getModified();
public void setModified(Date modified);
public String getModifiedBy();
public void setModifiedBy(String modifiedBy);
public Status getStatus();
public void setStatus(Status status);
}


import java.lang.reflect.ParameterizedType;
import java.util.Date;

public abstract class AbstractPersistedObject<T> implements Persisted<T> {
/**
     * Unique identifier for this object in the database. May be a String or an Integer.
*/
protected T id;
private final Class<T> type;
protected Date created;
protected String createdBy;
protected Date modified;
protected String modifiedBy;
protected Status status = Status.A;
public AbstractPersistedObject() {
this.type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
@Override
public T getId() {
return id;
}
@Override
public void setId(T id) {
this.id = id;
}
@Override
public Date getCreated() {
return created;
}

@Override
public void setCreated(Date created) {
this.created = created;
}
@Override
public String getCreatedBy() {
return createdBy;
}
@Override
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
@Override
public Date getModified() {
return modified;
}
@Override
public void setModified(Date modified) {
this.modified = modified;
}
@Override
public String getModifiedBy() {
return modifiedBy;
}
@Override
public void setModifiedBy(String modifiedBy) {
this.modifiedBy = modifiedBy;
}
@Override
public Status getStatus() {
return status;
}
@Override
public void setStatus(Status status) {
this.status = status;
}
}




The controller looks like this
List<Brand> brands = new ArrayList<>();
try {
brands = brandDao.getAllBrands();
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
}
result.include("brands", brands);
}

@Path("/admin/brand/{id}")
public void editBrand(Integer id) {
Brand brand = null;
try {
brand = brandDao.getBrandById(id);
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
}
result.include("brand", brand);
}

@Path("/admin/brand/save")
@IncludeParameters
public void saveBrand(Brand brand) {
log.debug("Saving Brand " + brand.getName());
try {
brandDao.saveBrand(brand);
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
}
result.redirectTo(this).brands();
}


The view looks like this
<form role="form" method="post" action="${linkTo[AdminController].saveBrand}">
<input type="hidden" name="brand.id" value="${brand.id}"/>
<input type="text" name="brand.name" value="${brand.name}"/>
<input type="radio" name="brand.status" value="A"/>Active
<input type="radio" name="brand.status" value="D"/>Disabled
<button type="submit" class="btn btn-default">Submit</button>
</form>



An exception occurred: java.lang.Throwable: br.com.caelum.vraptor.http.InvalidParameterException: Exception when trying to instantiate Target(name=brand, type=class com.a.domain.Brand)
Caused by: br.com.caelum.vraptor.http.InvalidParameterException: Exception when trying to instantiate Target(name=brand, type=class com.a.domain.Brand)
at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.handleException(VRaptorInstantiator.java:124)
at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:118)
at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:110)
at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator$Proxy$_$$_WeldClientProxy.instantiate(Unknown Source)
at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.instantiateOrAddError(IogiParametersProvider.java:87)
at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.instantiateParameters(IogiParametersProvider.java:80)
at br.com.caelum.vraptor.http.iogi.IogiParametersProvider.getParametersFor(IogiParametersProvider.java:72)
at br.com.caelum.vraptor.http.iogi.IogiParametersProvider$Proxy$_$$_WeldClientProxy.getParametersFor(Unknown Source)
at br.com.caelum.vraptor.observer.ParametersInstantiator.getParametersForCurrentMethod(ParametersInstantiator.java:149)
at br.com.caelum.vraptor.observer.ParametersInstantiator.instantiate(ParametersInstantiator.java:89)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:88)
at [internal classes]
at br.com.caelum.vraptor.core.DefaultInterceptorStack.start(DefaultInterceptorStack.java:90)
at br.com.caelum.vraptor.core.DefaultInterceptorStack$Proxy$_$$_WeldClientProxy.start(Unknown Source)
at br.com.caelum.vraptor.observer.RequestHandlerObserver.handle(RequestHandlerObserver.java:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:88)
at [internal classes]
at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:123)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
... 1 more
Caused by: java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
at br.com.caelum.iogi.reflection.Target.findRawClassType(Target.java:37)
at br.com.caelum.iogi.reflection.Target.getClassType(Target.java:29)
at br.com.caelum.iogi.reflection.Target.toString(Target.java:108)
at java.lang.String.valueOf(String.java:2849)
at java.lang.StringBuilder.append(StringBuilder.java:128)
at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.handleException(VRaptorInstantiator.java:124)
at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:118)
at br.com.caelum.iogi.reflection.NewObject.setProperty(NewObject.java:58)
at br.com.caelum.iogi.reflection.NewObject.populateProperties(NewObject.java:52)
at br.com.caelum.iogi.reflection.NewObject.valueWithPropertiesSet(NewObject.java:42)
at br.com.caelum.iogi.ObjectInstantiator.instantiate(ObjectInstantiator.java:30)
at br.com.caelum.iogi.MultiInstantiator.instantiate(MultiInstantiator.java:20)
at br.com.caelum.vraptor.http.iogi.VRaptorInstantiator.instantiate(VRaptorInstantiator.java:116)
... 26 more

brt...@gmail.com

unread,
Sep 14, 2015, 3:46:20 PM9/14/15
to caelum-vraptor-en
Just confirmed that even if I add a simple Converter for the Brand object I still get the same exception

@Convert(Brand.class)
@ApplicationScoped
public class BrandConverter implements Converter<Brand> {

@Inject
private BrandDao brandDao;
@Override
public Brand convert(String value, Class<? extends Brand> type) {
Brand brand = new Brand();
if (StringUtils.isNumeric(value)) {
try {
brand = brandDao.getBrandById(Integer.valueOf(value));
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
}
}
return brand;
}
}



Lucas Cavalcanti

unread,
Sep 15, 2015, 6:58:10 PM9/15/15
to caelum-v...@googlegroups.com
Hi, after adding the converter try to change the input hidden to:

<input type="hidden" name="brand" value="${brand.id}"/>

this way the converter will be called, and VRaptor (with IOGI) won't try to call setId()

Other way to fix this is to open an issuer (or a Pull Request) on IOGI mentioning this:

[]'s
Lucas Cavalcanti
@lucascs

--
You received this message because you are subscribed to the Google Groups "caelum-vraptor-en" group.
To unsubscribe from this group and stop receiving emails from it, send an email to caelum-vraptor...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

brt...@gmail.com

unread,
Sep 15, 2015, 7:53:12 PM9/15/15
to caelum-vraptor-en
Lucas, you're a life saver! Thank you. It would be a cool feature request for the IOGI project to see if we couldn't figure out a way to make it work.

Curious if there is a flow chart anywhere of the Vraptor request/response lifecycle? I'm thinking something along the lines of this diagram which is part of this guy's blog on Spring Web MVC http://www.cnblogs.com/fangwenyu/archive/2012/10/11/2716665.html

Lucas Cavalcanti

unread,
Sep 15, 2015, 8:59:56 PM9/15/15
to caelum-v...@googlegroups.com
We have this diagram:


VRaptor's lifecycle is event-based, with some observers affecting the response.


[]'s
Lucas Cavalcanti
@lucascs

Reply all
Reply to author
Forward
0 new messages