Hello,
We use JAX-RSSearch and Fiql parser in a JAX-RS endpoint and we have a problem
with org.apache.cxf.jaxrs.ext.search.Beanspector.
Let's consider the following pojos:
public class A {
private String value;
public String getValue(){ ... }
public void setValue(String value) { ... }
}
public class B {
private A aValue;
public A getAValue(){ ... }
public void setAValue(A avalue) { ... }
}
And assume one extends these pojos and decorates them with JPA annotations.
To leverage CXF org.apache.cxf.jaxrs.ext.search.SearchContext and
JPACriteriaQueryVisitor as explained in the docs
(http://cxf.apache.org/docs/jax-rs-search.html#JAX-RSSearch-JPA2.0) and perform
searches like
_s=aValue==*search token*
in OpenJPA one has to override the EntityB.getAValue as follows:
@Entity
// ... other JPA annotations are omitted
public class EntityB extends B {
@Override
// We need to specialize return type to EntityA to make SearchContext
work
public EntityA getAValue(){ ... }
// This method definition is needed to avoid java.lang.VerifyError from
JPA provider
public void setAValue(EntityA avalue) { ... }
}
But with this scenario, the current implementation of
org.apache.cxf.jaxrs.ext.search.Beanspector<T> fails, throwing
IllegalArgumentException: Accessor 'aValue' type mismatch, getter type is X
while setter type is Y, X and Y depending on the order of the EntityB's methods
as returned by the Class.getMethods().
This is the current implementation of Beanspector where the exception is
triggered:
@SuppressWarnings("unchecked")
private void init() {
if (tclass == null) {
tclass = (Class<T>)tobj.getClass();
}
for (Method m : tclass.getMethods()) {
if (isGetter(m)) {
getters.put(getPropertyName(m), m);
} else if (isSetter(m)) {
setters.put(getPropertyName(m), m);
}
}
// check type equality for getter-setter pairs
Set<String> pairs = new HashSet<>(getters.keySet());
pairs.retainAll(setters.keySet());
for (String accessor : pairs) {
Class<?> getterClass = getters.get(accessor).getReturnType();
Class<?> setterClass = setters.get(accessor).getParameterTypes()[0];
if (!getterClass.equals(setterClass)) {
throw new IllegalArgumentException(String
.format("Accessor '%s' type mismatch, getter type is %s while
setter type is %s",
accessor, getterClass.getName(),
setterClass.getName()));
}
}
}
And this is how we patched it:
@SuppressWarnings("unchecked")
private void init() {
if (tclass == null) {
tclass = (Class<T>)tobj.getClass();
}
for (Method m : tclass.getMethods()) {
if (isGetter(m)) {
String pname = getPropertyName(m);
if (!getters.containsKey(pname)) {
getters.put(getPropertyName(m), m);
} else {
// Prefer the getter that has the most specialized
class as a return type
Method _m = getters.get(pname);
if
(_m.getReturnType().isAssignableFrom(m.getReturnType())) {
getters.put(pname, m);
}
}
} else if (isSetter(m)) {
String pname = getPropertyName(m);
if (!setters.containsKey(pname)) {
setters.put(getPropertyName(m), m);
} else {
// Prefer the setter that has the most specialized
class as a parameter
Method _m = setters.get(pname);
if
(_m.getParameterTypes()[0].isAssignableFrom(m.getParameterTypes()[0])) {
setters.put(pname, m);
}
}
}
}
// check type equality for getter-setter pairs
Set<String> pairs = new HashSet<>(getters.keySet());
pairs.retainAll(setters.keySet());
for (String accessor : pairs) {
Class<?> getterClass = getters.get(accessor).getReturnType();
Class<?> setterClass = setters.get(accessor).getParameterTypes()[0];
if (!setterClass.isAssignableFrom(getterClass)) {
throw new IllegalArgumentException(String
.format("Accessor '%s' type mismatch, getter type is %s while
setter type is %s",
accessor, getterClass.getName(),
setterClass.getName()));
}
}
}
If you think this is OK we can create a pull request with this.
Thank you,
Matteo