diff --git a/src/main/java/org/springframework/data/projection/PropertyAccessingMethodInterceptor.java b/src/main/java/org/springframework/data/projection/PropertyAccessingMethodInterceptor.java index 2d7f7b15e..de2c0d0c5 100644 --- a/src/main/java/org/springframework/data/projection/PropertyAccessingMethodInterceptor.java +++ b/src/main/java/org/springframework/data/projection/PropertyAccessingMethodInterceptor.java @@ -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()); + } + } diff --git a/src/test/java/org/springframework/data/projection/PropertyAccessingMethodInterceptorUnitTests.java b/src/test/java/org/springframework/data/projection/PropertyAccessingMethodInterceptorUnitTests.java index efcc1c5b8..163589ef1 100644 --- a/src/test/java/org/springframework/data/projection/PropertyAccessingMethodInterceptorUnitTests.java +++ b/src/test/java/org/springframework/data/projection/PropertyAccessingMethodInterceptorUnitTests.java @@ -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(); diff --git a/src/test/java/org/springframework/data/projection/SpelAwareProxyProjectionFactoryUnitTests.java b/src/test/java/org/springframework/data/projection/SpelAwareProxyProjectionFactoryUnitTests.java index 6269561eb..bcaf1e974 100644 --- a/src/test/java/org/springframework/data/projection/SpelAwareProxyProjectionFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/projection/SpelAwareProxyProjectionFactoryUnitTests.java @@ -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); } }