Skip to content

Commit 9d85df0

Browse files
committed
Properties of inherited models can be used on different child models
1 parent 6d5f740 commit 9d85df0

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,33 @@ public void setTenantValue(EntityBean entityBean, Object tenantId) {
668668
setValue(entityBean, tenantId);
669669
}
670670

671+
/**
672+
* By default, getIntercept and setIntercept will check, if the passed bean is an instance of descriptor type.
673+
* <p>
674+
* If the property is not part of the type hierarchy (i.e. is this property from this descriptor)
675+
* an UnsuppoertedOperation is thrown.
676+
* <p>
677+
* If inheritance is involved, this method returns false instead of an exception, if the property might exist in
678+
* one of the child beans. This is necessary for getIntercept, as it returns <code>null</code> in this case.
679+
* @return true if property can be
680+
*/
681+
private boolean checkPropertyAccess(EntityBean bean) {
682+
if (bean == null || descriptor.type().isInstance(bean)) { // null = fall through - NPE is catched later.
683+
return true;
684+
}
685+
InheritInfo inheritInfo = descriptor.inheritInfo();
686+
if (inheritInfo == null || inheritInfo.isRoot() || !inheritInfo.getRoot().getType().isInstance(bean)) {
687+
throw new IllegalArgumentException(propertyIncomatibleMsg(bean));
688+
} else {
689+
return false;
690+
}
691+
}
692+
693+
private String propertyIncomatibleMsg(EntityBean bean) {
694+
String beanType = bean == null ? "null" : bean.getClass().getName();
695+
return "Property " + name + " on [" + descriptor + "] is incompatible with type[" + beanType + "]";
696+
}
697+
671698
/**
672699
* Set the value of the property without interception or
673700
* PropertyChangeSupport.
@@ -684,6 +711,9 @@ public void setValue(EntityBean bean, Object value) {
684711
* Set the value of the property.
685712
*/
686713
public void setValueIntercept(EntityBean bean, Object value) {
714+
if (!checkPropertyAccess(bean)) {
715+
throw new IllegalArgumentException(propertyIncomatibleMsg(bean));
716+
}
687717
try {
688718
setter.setIntercept(bean, value);
689719
} catch (Exception ex) {
@@ -795,6 +825,9 @@ public Object getValue(EntityBean bean) {
795825
}
796826

797827
public Object getValueIntercept(EntityBean bean) {
828+
if (!checkPropertyAccess(bean)) {
829+
return null;
830+
}
798831
try {
799832
return getter.getIntercept(bean);
800833
} catch (Exception ex) {
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package org.tests.inheritance;
2+
3+
import io.ebean.DB;
4+
import io.ebean.plugin.ExpressionPath;
5+
import org.junit.jupiter.api.Test;
6+
import org.tests.model.basic.*;
7+
8+
import java.sql.Timestamp;
9+
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
12+
13+
/**
14+
* @author Roland Praml, FOCONIS AG
15+
*/
16+
public class TestPropertyAccess {
17+
18+
19+
private ExpressionPath custCretime = DB.getDefault().pluginApi().beanType(Customer.class).expressionPath("cretime");
20+
private ExpressionPath custName = DB.getDefault().pluginApi().beanType(Customer.class).expressionPath("name");
21+
// Note: Animal.name is only present in "Cat" not in "Dog"
22+
private ExpressionPath animalName = DB.getDefault().pluginApi().beanType(Animal.class).expressionPath("name");
23+
private ExpressionPath productName = DB.getDefault().pluginApi().beanType(Product.class).expressionPath("name");
24+
private ExpressionPath animalSpecies = DB.getDefault().pluginApi().beanType(Animal.class).expressionPath("species");
25+
26+
@Test
27+
void testOnInheritance() {
28+
Cat cat = new Cat();
29+
cat.setName("Tom");
30+
DB.save(cat);
31+
32+
Dog dog = new Dog();
33+
dog.setRegistrationNumber("FOO");
34+
DB.save(dog);
35+
36+
37+
Animal animal = DB.find(Animal.class, cat.getId());
38+
assertThat(animalName.pathGet(animal)).isEqualTo("Tom");
39+
40+
animal = DB.find(Animal.class, dog.getId());
41+
assertThat(animalName.pathGet(animal)).isNull();
42+
43+
animalName.pathSet(cat, "Jerry");
44+
assertThat(cat.getName()).isEqualTo("Jerry");
45+
46+
assertThatThrownBy(() -> animalName.pathSet(dog, "Jerry"))
47+
.isInstanceOf(IllegalArgumentException.class);
48+
49+
animalSpecies.pathSet(cat, "Angora");
50+
animalSpecies.pathSet(dog, "Bulldog");
51+
52+
assertThat(cat.getSpecies()).isEqualTo("Angora");
53+
assertThat(dog.getSpecies()).isEqualTo("Bulldog");
54+
}
55+
56+
@Test
57+
void testOnMappedSuperClass() {
58+
Customer cust = new Customer();
59+
60+
Timestamp ts = new Timestamp(123);
61+
custCretime.pathSet(cust, ts);
62+
assertThat(custCretime.pathGet(cust)).isEqualTo(ts);
63+
64+
custName.pathSet(cust, "Roland");
65+
assertThat(custName.pathGet(cust)).isEqualTo("Roland");
66+
67+
}
68+
69+
@Test
70+
void testOnPlainBean() {
71+
Product product = new Product();
72+
73+
productName.pathSet(product, "Roland");
74+
assertThat(productName.pathGet(product)).isEqualTo("Roland");
75+
}
76+
77+
@Test
78+
void testOnContactGroup() {
79+
ContactGroup cg = new ContactGroup();
80+
81+
// CHECKEM: Ist it OK to us the "custCretime" on "contactGroup"
82+
assertThatThrownBy(() -> custCretime.pathGet(cg)).isInstanceOf(IllegalArgumentException.class);
83+
}
84+
85+
@Test
86+
void testOnCrossUsage() {
87+
Product product = new Product();
88+
Customer cust = new Customer();
89+
Cat cat = new Cat();
90+
Dog dog = new Dog();
91+
92+
93+
assertThatThrownBy(() -> custCretime.pathGet(product)).isInstanceOf(IllegalArgumentException.class);
94+
custCretime.pathGet(cust);
95+
assertThatThrownBy(() -> custCretime.pathGet(cat)).isInstanceOf(IllegalArgumentException.class);
96+
assertThatThrownBy(() -> custCretime.pathGet(dog)).isInstanceOf(IllegalArgumentException.class);
97+
98+
assertThatThrownBy(() -> custName.pathGet(product)).isInstanceOf(IllegalArgumentException.class);
99+
custName.pathGet(cust);
100+
assertThatThrownBy(() -> custName.pathGet(cat)).isInstanceOf(IllegalArgumentException.class);
101+
assertThatThrownBy(() -> custName.pathGet(dog)).isInstanceOf(IllegalArgumentException.class);
102+
103+
assertThatThrownBy(() -> animalName.pathGet(product)).isInstanceOf(IllegalArgumentException.class);
104+
assertThatThrownBy(() -> animalName.pathGet(cust)).isInstanceOf(IllegalArgumentException.class);
105+
animalName.pathGet(cat);
106+
animalName.pathGet(dog);
107+
108+
productName.pathGet(product);
109+
assertThatThrownBy(() -> productName.pathGet(cust)).isInstanceOf(IllegalArgumentException.class);
110+
assertThatThrownBy(() -> productName.pathGet(cat)).isInstanceOf(IllegalArgumentException.class);
111+
assertThatThrownBy(() -> productName.pathGet(dog)).isInstanceOf(IllegalArgumentException.class);
112+
113+
assertThatThrownBy(() -> animalSpecies.pathGet(product)).isInstanceOf(IllegalArgumentException.class);
114+
assertThatThrownBy(() -> animalSpecies.pathGet(cust)).isInstanceOf(IllegalArgumentException.class);
115+
animalSpecies.pathGet(cat);
116+
animalSpecies.pathGet(dog);
117+
}
118+
}

0 commit comments

Comments
 (0)