Skip to content

Commit e868577

Browse files
mp911deodrotbohm
authored andcommitted
DATACMNS-820 - Allow setter invocations in projections backed by beans.
Invocations to setter methods are now passed to the backing bean. Original pull request: #155.
1 parent 1a97627 commit e868577

File tree

3 files changed

+111
-2
lines changed

3 files changed

+111
-2
lines changed

src/main/java/org/springframework/data/projection/PropertyAccessingMethodInterceptor.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2015 the original author or authors.
2+
* Copyright 2014-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
* Method interceptor to forward a delegation to bean property accessor methods to the property of a given target.
3131
*
3232
* @author Oliver Gierke
33+
* @author Mark Paluch
3334
* @since 1.10
3435
*/
3536
class PropertyAccessingMethodInterceptor implements MethodInterceptor {
@@ -66,6 +67,20 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
6667
throw new IllegalStateException("Invoked method is not a property accessor!");
6768
}
6869

70+
if (isSetterMethod(method, descriptor)) {
71+
if (invocation.getArguments().length != 1) {
72+
throw new IllegalStateException("Invoked setter method requires exactly one argument!");
73+
}
74+
75+
target.setPropertyValue(descriptor.getName(), invocation.getArguments()[0]);
76+
return null;
77+
}
78+
6979
return target.getPropertyValue(descriptor.getName());
7080
}
81+
82+
private boolean isSetterMethod(Method method, PropertyDescriptor descriptor) {
83+
return method.equals(descriptor.getWriteMethod());
84+
}
85+
7186
}

src/test/java/org/springframework/data/projection/PropertyAccessingMethodInterceptorUnitTests.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.mockito.Mock;
2727
import org.mockito.runners.MockitoJUnitRunner;
2828
import org.springframework.beans.NotReadablePropertyException;
29+
import org.springframework.beans.NotWritablePropertyException;
2930

3031
/**
3132
* Unit tests for {@link PropertyAccessingMethodInterceptor}.
@@ -62,6 +63,50 @@ public void throwsAppropriateExceptionIfThePropertyCannotBeFound() throws Throwa
6263
new PropertyAccessingMethodInterceptor(new Source()).invoke(invocation);
6364
}
6465

66+
/**
67+
* @see DATACMNS-820
68+
*/
69+
@Test
70+
public void triggersWritePropertyAccessOnTarget() throws Throwable {
71+
72+
Source source = new Source();
73+
source.firstname = "Dave";
74+
75+
when(invocation.getMethod()).thenReturn(Projection.class.getMethod("setFirstname", String.class));
76+
when(invocation.getArguments()).thenReturn(new Object[] { "Carl" });
77+
MethodInterceptor interceptor = new PropertyAccessingMethodInterceptor(source);
78+
79+
interceptor.invoke(invocation);
80+
81+
assertThat(source.firstname, is((Object) "Carl"));
82+
}
83+
84+
/**
85+
* @see DATACMNS-820
86+
*/
87+
@Test(expected = IllegalStateException.class)
88+
public void throwsAppropriateExceptionIfTheInvocationHasNoArguments() throws Throwable {
89+
90+
Source source = new Source();
91+
92+
when(invocation.getMethod()).thenReturn(Projection.class.getMethod("setFirstname", String.class));
93+
when(invocation.getArguments()).thenReturn(new Object[0]);
94+
MethodInterceptor interceptor = new PropertyAccessingMethodInterceptor(source);
95+
96+
interceptor.invoke(invocation);
97+
}
98+
99+
/**
100+
* @see DATACMNS-820
101+
*/
102+
@Test(expected = NotWritablePropertyException.class)
103+
public void throwsAppropriateExceptionIfThePropertyCannotWritten() throws Throwable {
104+
105+
when(invocation.getMethod()).thenReturn(Projection.class.getMethod("setGarbage", String.class));
106+
when(invocation.getArguments()).thenReturn(new Object[] { "Carl" });
107+
new PropertyAccessingMethodInterceptor(new Source()).invoke(invocation);
108+
}
109+
65110
/**
66111
* @see DATAREST-221
67112
*/
@@ -91,6 +136,10 @@ interface Projection {
91136

92137
String getFirstname();
93138

139+
void setFirstname(String firstname);
140+
141+
void setGarbage(String garbage);
142+
94143
String getLastname();
95144

96145
String someGarbage();

src/test/java/org/springframework/data/projection/SpelAwareProxyProjectionFactoryUnitTests.java

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015 the original author or authors.
2+
* Copyright 2015-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,13 +22,15 @@
2222

2323
import org.junit.Before;
2424
import org.junit.Test;
25+
import org.springframework.beans.NotWritablePropertyException;
2526
import org.springframework.beans.factory.annotation.Value;
2627

2728
/**
2829
* Unit tests for {@link SpelAwareProxyProjectionFactory}.
2930
*
3031
* @author Oliver Gierke
3132
* @author Thomas Darimont
33+
* @author Mark Paluch
3234
*/
3335
public class SpelAwareProxyProjectionFactoryUnitTests {
3436

@@ -53,6 +55,42 @@ public void exposesSpelInvokingMethod() {
5355
assertThat(excerpt.getFullName(), is("Dave Matthews"));
5456
}
5557

58+
/**
59+
* @see DATACMNS-820
60+
*/
61+
@Test
62+
public void setsValueUsingProjection() {
63+
64+
Customer customer = new Customer();
65+
customer.firstname = "Dave";
66+
67+
CustomerExcerpt excerpt = factory.createProjection(CustomerExcerpt.class, customer);
68+
excerpt.setFirstname("Carl");
69+
70+
assertThat(customer.firstname, is("Carl"));
71+
}
72+
73+
/**
74+
* @see DATACMNS-820
75+
*/
76+
@Test
77+
public void settingNotWriteablePropertyFails() {
78+
79+
Customer customer = new Customer();
80+
customer.firstname = "Dave";
81+
82+
ProjectionWithNotWriteableProperty projection = factory.createProjection(ProjectionWithNotWriteableProperty.class,
83+
customer);
84+
85+
try {
86+
projection.setFirstName("Carl");
87+
fail("Missing NotWritablePropertyException");
88+
} catch (NotWritablePropertyException e) {
89+
assertThat(e, instanceOf(NotWritablePropertyException.class));
90+
}
91+
92+
}
93+
5694
/**
5795
* @see DATACMNS-630
5896
*/
@@ -87,5 +125,12 @@ interface CustomerExcerpt {
87125
String getFullName();
88126

89127
String getFirstname();
128+
129+
void setFirstname(String firstname);
130+
}
131+
132+
interface ProjectionWithNotWriteableProperty {
133+
134+
void setFirstName(String firstname);
90135
}
91136
}

0 commit comments

Comments
 (0)