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.
This commit is contained in:
Mark Paluch
2016-02-20 16:14:55 +01:00
committed by Oliver Gierke
parent 1a97627196
commit e868577def
3 changed files with 111 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import org.springframework.util.ReflectionUtils;
* Method interceptor to forward a delegation to bean property accessor methods to the property of a given target.
*
* @author Oliver Gierke
* @author Mark Paluch
* @since 1.10
*/
class PropertyAccessingMethodInterceptor implements MethodInterceptor {
@@ -66,6 +67,20 @@ class PropertyAccessingMethodInterceptor implements MethodInterceptor {
throw new IllegalStateException("Invoked method is not a property accessor!");
}
if (isSetterMethod(method, descriptor)) {
if (invocation.getArguments().length != 1) {
throw new IllegalStateException("Invoked setter method requires exactly one argument!");
}
target.setPropertyValue(descriptor.getName(), invocation.getArguments()[0]);
return null;
}
return target.getPropertyValue(descriptor.getName());
}
private boolean isSetterMethod(Method method, PropertyDescriptor descriptor) {
return method.equals(descriptor.getWriteMethod());
}
}

View File

@@ -26,6 +26,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.beans.NotWritablePropertyException;
/**
* Unit tests for {@link PropertyAccessingMethodInterceptor}.
@@ -62,6 +63,50 @@ public class PropertyAccessingMethodInterceptorUnitTests {
new PropertyAccessingMethodInterceptor(new Source()).invoke(invocation);
}
/**
* @see DATACMNS-820
*/
@Test
public void triggersWritePropertyAccessOnTarget() throws Throwable {
Source source = new Source();
source.firstname = "Dave";
when(invocation.getMethod()).thenReturn(Projection.class.getMethod("setFirstname", String.class));
when(invocation.getArguments()).thenReturn(new Object[] { "Carl" });
MethodInterceptor interceptor = new PropertyAccessingMethodInterceptor(source);
interceptor.invoke(invocation);
assertThat(source.firstname, is((Object) "Carl"));
}
/**
* @see DATACMNS-820
*/
@Test(expected = IllegalStateException.class)
public void throwsAppropriateExceptionIfTheInvocationHasNoArguments() throws Throwable {
Source source = new Source();
when(invocation.getMethod()).thenReturn(Projection.class.getMethod("setFirstname", String.class));
when(invocation.getArguments()).thenReturn(new Object[0]);
MethodInterceptor interceptor = new PropertyAccessingMethodInterceptor(source);
interceptor.invoke(invocation);
}
/**
* @see DATACMNS-820
*/
@Test(expected = NotWritablePropertyException.class)
public void throwsAppropriateExceptionIfThePropertyCannotWritten() throws Throwable {
when(invocation.getMethod()).thenReturn(Projection.class.getMethod("setGarbage", String.class));
when(invocation.getArguments()).thenReturn(new Object[] { "Carl" });
new PropertyAccessingMethodInterceptor(new Source()).invoke(invocation);
}
/**
* @see DATAREST-221
*/
@@ -91,6 +136,10 @@ public class PropertyAccessingMethodInterceptorUnitTests {
String getFirstname();
void setFirstname(String firstname);
void setGarbage(String garbage);
String getLastname();
String someGarbage();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.factory.annotation.Value;
/**
@@ -29,6 +30,7 @@ import org.springframework.beans.factory.annotation.Value;
*
* @author Oliver Gierke
* @author Thomas Darimont
* @author Mark Paluch
*/
public class SpelAwareProxyProjectionFactoryUnitTests {
@@ -53,6 +55,42 @@ public class SpelAwareProxyProjectionFactoryUnitTests {
assertThat(excerpt.getFullName(), is("Dave Matthews"));
}
/**
* @see DATACMNS-820
*/
@Test
public void setsValueUsingProjection() {
Customer customer = new Customer();
customer.firstname = "Dave";
CustomerExcerpt excerpt = factory.createProjection(CustomerExcerpt.class, customer);
excerpt.setFirstname("Carl");
assertThat(customer.firstname, is("Carl"));
}
/**
* @see DATACMNS-820
*/
@Test
public void settingNotWriteablePropertyFails() {
Customer customer = new Customer();
customer.firstname = "Dave";
ProjectionWithNotWriteableProperty projection = factory.createProjection(ProjectionWithNotWriteableProperty.class,
customer);
try {
projection.setFirstName("Carl");
fail("Missing NotWritablePropertyException");
} catch (NotWritablePropertyException e) {
assertThat(e, instanceOf(NotWritablePropertyException.class));
}
}
/**
* @see DATACMNS-630
*/
@@ -87,5 +125,12 @@ public class SpelAwareProxyProjectionFactoryUnitTests {
String getFullName();
String getFirstname();
void setFirstname(String firstname);
}
interface ProjectionWithNotWriteableProperty {
void setFirstName(String firstname);
}
}