diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProjectingMethodInterceptor.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProjectingMethodInterceptor.java deleted file mode 100644 index 317e7dbc8..000000000 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProjectingMethodInterceptor.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.core.CollectionFactory; -import org.springframework.data.util.ClassTypeInformation; -import org.springframework.data.util.TypeInformation; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; - -/** - * {@link MethodInterceptor} to delegate the invocation to a different {@link MethodInterceptor} but creating a - * projecting proxy in case the returned value is not of the return type of the invoked method. - * - * @author Oliver Gierke - */ -class ProjectingMethodInterceptor implements MethodInterceptor { - - private final ProjectionFactory factory; - private final MethodInterceptor delegate; - - /** - * Creates a new {@link ProjectingMethodInterceptor} using the given {@link ProjectionFactory} and delegate - * {@link MethodInterceptor}. - * - * @param factory the {@link ProjectionFactory} to use to create projections if types do not match. - * @param delegate the {@link MethodInterceptor} to trigger to create the source value. - */ - public ProjectingMethodInterceptor(ProjectionFactory factory, MethodInterceptor delegate) { - - Assert.notNull(factory, "ProjectionFactory must not be null!"); - Assert.notNull(delegate, "Delegate MethodInterceptor must not be null!"); - - this.factory = factory; - this.delegate = delegate; - } - - /* - * (non-Javadoc) - * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) - */ - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - - Object result = delegate.invoke(invocation); - - if (result == null) { - return null; - } - - TypeInformation type = ClassTypeInformation.fromReturnTypeOf(invocation.getMethod()); - - if (type.isCollectionLike()) { - return projectCollectionElements(asCollection(result), type); - } else if (type.isMap()) { - return projectMapValues((Map) result, type); - } else { - return getProjection(result, type.getType()); - } - } - - /** - * Creates projections of the given {@link Collection}'s elements if necessary and returns a new collection containing - * the projection results. - * - * @param sources must not be {@literal null}. - * @param type must not be {@literal null}. - * @return - */ - private Collection projectCollectionElements(Collection sources, TypeInformation type) { - - Collection result = CollectionFactory.createCollection(type.getType(), sources.size()); - - for (Object source : sources) { - result.add(getProjection(source, type.getComponentType().getType())); - } - - return result; - } - - /** - * Creates projections of the given {@link Map}'s values if necessary and returns an new {@link Map} with the handled - * values. - * - * @param sources must not be {@literal null}. - * @param type must not be {@literal null}. - * @return - */ - private Map projectMapValues(Map sources, TypeInformation type) { - - Map result = CollectionFactory.createMap(type.getType(), sources.size()); - - for (Entry source : sources.entrySet()) { - result.put(source.getKey(), getProjection(source.getValue(), type.getMapValueType().getType())); - } - - return result; - } - - private Object getProjection(Object result, Class returnType) { - return ClassUtils.isAssignable(returnType, result.getClass()) ? result : factory.createProjection(result, - returnType); - } - - /** - * Turns the given value into a {@link Collection}. Will create an empty {@link Collection} for {@literal null}, turn - * an array iinto a collection an wrap all other values into a single-element collection. - * - * @param source can be {@literal null}. - * @return - */ - private static Collection asCollection(Object source) { - - if (source == null) { - return Collections.emptySet(); - } else if (source instanceof Collection) { - return (Collection) source; - } else if (source.getClass().isArray()) { - return Arrays.asList((Object[]) source); - } else { - return Collections.singleton(source); - } - } -} diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProjectionFactory.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProjectionFactory.java deleted file mode 100644 index 132e40302..000000000 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProjectionFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -/** - * A factory to create projecting instances for other objects usually used to allow easy creation of representation - * projections to define which properties of a domain objects shall be exported in which way. - * - * @author Oliver Gierke - */ -public interface ProjectionFactory { - - /** - * Creates a projection of the given type for the given source object. The individual mapping strategy is defined by - * the implementations. - * - * @param source the object to create a projection for, can be {@literal null} - * @param projectionType the type to create. - * @return - */ - T createProjection(Object source, Class projectionType); -} diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptor.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptor.java deleted file mode 100644 index 1014b3270..000000000 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptor.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -import java.beans.PropertyDescriptor; -import java.lang.reflect.Method; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeanWrapper; -import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper; -import org.springframework.util.Assert; -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 - */ -class PropertyAccessingMethodInterceptor implements MethodInterceptor { - - private final BeanWrapper target; - - /** - * Creates a new {@link PropertyAccessingMethodInterceptor} for the given target object. - * - * @param target must not be {@literal null}. - */ - public PropertyAccessingMethodInterceptor(Object target) { - - Assert.notNull(target, "Proxy target must not be null!"); - this.target = new DirectFieldAccessFallbackBeanWrapper(target); - } - - /* - * (non-Javadoc) - * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) - */ - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - - Method method = invocation.getMethod(); - - if (ReflectionUtils.isObjectMethod(method)) { - return invocation.proceed(); - } - - PropertyDescriptor descriptor = BeanUtils.findPropertyForMethod(method); - - if (descriptor == null) { - throw new IllegalStateException("Invoked method is not a property accessor!"); - } - - return target.getPropertyValue(descriptor.getName()); - } -} diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProxyProjectionFactory.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProxyProjectionFactory.java deleted file mode 100644 index f942e1b90..000000000 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/ProxyProjectionFactory.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.aop.framework.Advised; -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.util.AnnotationDetectionMethodCallback; -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * A {@link ProjectionFactory} to create JDK proxies to back interfaces and handle method invocations on them. By - * default two different kinds of methods are supported: - *
    - *
  1. Bean property accessor methods - invocations will be delegated into a property lookup on the target instance.
  2. - *
  3. Arbitrary methods annotated with {@link Value} to contain a SpEL expression, which will be evaluated on - * invocation. The expressions can use {@code target} to refer to the proxy target.
  4. - *
- * In case the dlegating lookups result in an object of different type that the projection interface method's return - * type, another projection will be created to transparently mitigate between the types. - * - * @author Oliver Gierke - */ -public class ProxyProjectionFactory implements ProjectionFactory { - - private final Map, Boolean> typeCache = new HashMap, Boolean>(); - - private BeanFactory beanFactory; - - /** - * Creates a new {@link ProxyProjectionFactory} using the given {@link BeanFactory}. - * - * @param beanFactory can be {@literal null}. If {@literal null}, SpEL expressions at projection interfaces cannot use - * bean references. - */ - public ProxyProjectionFactory(BeanFactory beanFactory) { - this.beanFactory = beanFactory; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.core.projection.ProjectionFactory#createProjection(java.lang.Object, java.lang.Class) - */ - @Override - @SuppressWarnings("unchecked") - public T createProjection(Object source, Class projectionType) { - - Assert.isTrue(projectionType.isInterface(), "Projection type must be an interface!"); - - if (source == null) { - return null; - } - - ProxyFactory factory = new ProxyFactory(); - factory.setTarget(source); - factory.setOpaque(true); - factory.setInterfaces(projectionType, TargetClassAware.class); - - factory.addAdvice(new TargetClassAwareMethodInterceptor(source.getClass())); - factory.addAdvice(getMethodInterceptor(source, projectionType)); - - return (T) factory.getProxy(); - } - - /** - * Returns the {@link MethodInterceptor} to add to the proxy. - * - * @param source must not be {@literal null}. - * @param projectionType must not be {@literal null}. - * @return - */ - private MethodInterceptor getMethodInterceptor(Object source, Class projectionType) { - - MethodInterceptor propertyInvocationInterceptor = new PropertyAccessingMethodInterceptor(source); - return new ProjectingMethodInterceptor(this, getSpelMethodInterceptorIfNecessary(source, projectionType, - propertyInvocationInterceptor)); - } - - /** - * Inspects the given target type for methods with {@link Value} annotations and caches the result. Will create a - * {@link SpelEvaluatingMethodInterceptor} if an annotation was found or return the delegate as is if not. - * - * @param source The backing source object. - * @param projectionType the proxy target type. - * @param delegate the root {@link MethodInterceptor}. - * @return - */ - private MethodInterceptor getSpelMethodInterceptorIfNecessary(Object source, Class projectionType, - MethodInterceptor delegate) { - - if (!typeCache.containsKey(projectionType)) { - - AnnotationDetectionMethodCallback callback = new AnnotationDetectionMethodCallback(Value.class); - ReflectionUtils.doWithMethods(projectionType, callback); - - typeCache.put(projectionType, callback.hasFoundAnnotation()); - } - - return typeCache.get(projectionType) ? new SpelEvaluatingMethodInterceptor(delegate, source, beanFactory) - : delegate; - } - - /** - * Extension of {@link org.springframework.aop.TargetClassAware} to be able to ignore the getter on JSON rendering. - * - * @author Oliver Gierke - */ - public static interface TargetClassAware extends org.springframework.aop.TargetClassAware { - - @JsonIgnore - Class getTargetClass(); - } - - /** - * Custom {@link MethodInterceptor} to expose the proxy target class even if we set - * {@link ProxyFactory#setOpaque(boolean)} to true to prevent properties on {@link Advised} to be rendered. - * - * @author Oliver Gierke - */ - private static class TargetClassAwareMethodInterceptor implements MethodInterceptor { - - private static final Method GET_TARGET_CLASS_METHOD; - private final Class targetClass; - - static { - try { - GET_TARGET_CLASS_METHOD = TargetClassAware.class.getMethod("getTargetClass"); - } catch (NoSuchMethodException e) { - throw new IllegalStateException(e); - } - } - - /** - * Creates a new {@link TargetClassAwareMethodInterceptor} with the given target class. - * - * @param targetClass must not be {@literal null}. - */ - public TargetClassAwareMethodInterceptor(Class targetClass) { - - Assert.notNull(targetClass, "Target class must not be null!"); - this.targetClass = targetClass; - } - - /* - * (non-Javadoc) - * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) - */ - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - - if (invocation.getMethod().equals(GET_TARGET_CLASS_METHOD)) { - return targetClass; - } - - return invocation.proceed(); - } - } -} diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/SpelEvaluatingMethodInterceptor.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/SpelEvaluatingMethodInterceptor.java deleted file mode 100644 index 2b8a9e542..000000000 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/projection/SpelEvaluatingMethodInterceptor.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -import java.lang.reflect.Method; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.Expression; -import org.springframework.expression.ParserContext; -import org.springframework.expression.common.TemplateParserContext; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * {@link MethodInterceptor} to invoke a SpEL expression to compute the method result. Will forward the resolution to a - * delegate {@link MethodInterceptor} if no {@link Value} annotation is found. - * - * @author Oliver Gierke - */ -class SpelEvaluatingMethodInterceptor implements MethodInterceptor { - - private final SpelExpressionParser parser; - private final ParserContext parserContext; - private final EvaluationContext evaluationContext; - private final MethodInterceptor delegate; - - /** - * Creates a new {@link SpelEvaluatingMethodInterceptor} delegating to the given {@link MethodInterceptor} as fallback - * and exposing the given target object via {@code target} to the SpEl expressions. If a {@link BeanFactory} is given, - * bean references in SpEl expressions can be resolved as well. - * - * @param delegate must not be {@literal null}. - * @param target must not be {@literal null}. - * @param beanFactory can be {@literal null}. - */ - public SpelEvaluatingMethodInterceptor(MethodInterceptor delegate, Object target, BeanFactory beanFactory) { - - Assert.notNull(delegate, "Delegate MethodInterceptor must not be null!"); - Assert.notNull(target, "TargetObject must not be null!"); - - StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new TargetWrapper(target)); - - if (beanFactory != null) { - evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); - } - - this.evaluationContext = evaluationContext; - this.parser = new SpelExpressionParser(); - this.parserContext = new TemplateParserContext(); - this.delegate = delegate; - } - - /* - * (non-Javadoc) - * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) - */ - @Override - public Object invoke(MethodInvocation invocation) throws Throwable { - - Method method = invocation.getMethod(); - Value annotation = method.getAnnotation(Value.class); - - if (annotation == null || !StringUtils.hasText(annotation.value())) { - return delegate.invoke(invocation); - } - - Expression expression = parser.parseExpression(annotation.value(), parserContext); - return expression.getValue(evaluationContext); - } - - /** - * Wrapper class to expose an object to the SpEL expression as {@code target}. - * - * @author Oliver Gierke - */ - static class TargetWrapper { - - private final Object target; - - public TargetWrapper(Object target) { - this.target = target; - } - - /** - * @return the target - */ - public Object getTarget() { - return target; - } - } -} diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProjectingMethodInterceptorUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProjectingMethodInterceptorUnitTests.java deleted file mode 100644 index bfccc110f..000000000 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProjectingMethodInterceptorUnitTests.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.hamcrest.Matchers; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -/** - * Unit tests for {@link ProjectingMethodInterceptor}. - * - * @author Oliver Gierke - * @author Saulo Medeiros de Araujo - */ -@RunWith(MockitoJUnitRunner.class) -public class ProjectingMethodInterceptorUnitTests { - - @Mock MethodInterceptor interceptor; - @Mock MethodInvocation invocation; - @Mock ProjectionFactory factory; - - /** - * @see DATAREST-221 - */ - @Test - public void wrapsDelegateResultInProxyIfTypesDontMatch() throws Throwable { - - MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(new ProxyProjectionFactory(null), interceptor); - - when(invocation.getMethod()).thenReturn(Helper.class.getMethod("getHelper")); - when(interceptor.invoke(invocation)).thenReturn("Foo"); - - assertThat(methodInterceptor.invoke(invocation), is(instanceOf(Helper.class))); - } - - /** - * @see DATAREST-221 - */ - @Test - public void retunsDelegateResultAsIsIfTypesMatch() throws Throwable { - - MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(factory, interceptor); - - when(invocation.getMethod()).thenReturn(Helper.class.getMethod("getString")); - when(interceptor.invoke(invocation)).thenReturn("Foo"); - - assertThat(methodInterceptor.invoke(invocation), is((Object) "Foo")); - } - - /** - * @see DATAREST-221 - */ - @Test - public void returnsNullAsIs() throws Throwable { - - MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(factory, interceptor); - - when(interceptor.invoke(invocation)).thenReturn(null); - - assertThat(methodInterceptor.invoke(invocation), is(nullValue())); - } - - /** - * @see DATAREST-221 - */ - @Test - public void considersPrimitivesAsWrappers() throws Throwable { - - MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(factory, interceptor); - - when(invocation.getMethod()).thenReturn(Helper.class.getMethod("getPrimitive")); - when(interceptor.invoke(invocation)).thenReturn(1L); - - assertThat(methodInterceptor.invoke(invocation), is((Object) 1L)); - verify(factory, times(0)).createProjection(anyObject(), (Class) anyObject()); - } - - /** - * @see DATAREST-394, DATAREST-408 - */ - @Test - @SuppressWarnings("unchecked") - public void appliesProjectionToNonEmptySets() throws Throwable { - - MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(new ProxyProjectionFactory(null), interceptor); - Object result = methodInterceptor.invoke(mockInvocationOf("getHelperCollection", - Collections.singleton(mock(Helper.class)))); - - assertThat(result, is(instanceOf(Set.class))); - - Set projections = (Set) result; - assertThat(projections, hasSize(1)); - assertThat(projections, hasItem(instanceOf(HelperProjection.class))); - } - - /** - * @see DATAREST-394, DATAREST-408 - */ - @Test - @SuppressWarnings("unchecked") - public void appliesProjectionToNonEmptyLists() throws Throwable { - - MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(new ProxyProjectionFactory(null), interceptor); - Object result = methodInterceptor.invoke(mockInvocationOf("getHelperList", - Collections.singletonList(mock(Helper.class)))); - - assertThat(result, is(instanceOf(List.class))); - - List projections = (List) result; - - assertThat(projections, hasSize(1)); - assertThat(projections, hasItem(instanceOf(HelperProjection.class))); - } - - /** - * @see DATAREST-394, DATAREST-408 - */ - @Test - @SuppressWarnings("unchecked") - public void allowsMaskingAnArrayIntoACollection() throws Throwable { - - MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(new ProxyProjectionFactory(null), interceptor); - Object result = methodInterceptor.invoke(mockInvocationOf("getHelperArray", new Helper[] { mock(Helper.class) })); - - assertThat(result, is(instanceOf(Collection.class))); - - Collection projections = (Collection) result; - - assertThat(projections, hasSize(1)); - assertThat(projections, hasItem(instanceOf(HelperProjection.class))); - } - - /** - * @see DATAREST-394, DATAREST-408 - */ - @Test - @SuppressWarnings("unchecked") - public void appliesProjectionToNonEmptyMap() throws Throwable { - - MethodInterceptor methodInterceptor = new ProjectingMethodInterceptor(new ProxyProjectionFactory(null), interceptor); - - Object result = methodInterceptor.invoke(mockInvocationOf("getHelperMap", - Collections.singletonMap("foo", mock(Helper.class)))); - - assertThat(result, is(instanceOf(Map.class))); - - Map projections = (Map) result; - assertThat(projections.entrySet(), is(Matchers.> iterableWithSize(1))); - assertThat(projections, hasEntry(is("foo"), instanceOf(HelperProjection.class))); - } - - /** - * Mocks the {@link Helper} method of the given name to return the given value. - * - * @param methodName - * @param returnValue - * @return - * @throws Throwable - */ - private MethodInvocation mockInvocationOf(String methodName, Object returnValue) throws Throwable { - - when(invocation.getMethod()).thenReturn(Helper.class.getMethod(methodName)); - when(interceptor.invoke(invocation)).thenReturn(returnValue); - - return invocation; - } - - interface Helper { - - Helper getHelper(); - - String getString(); - - long getPrimitive(); - - Collection getHelperCollection(); - - List getHelperList(); - - Set getHelperSet(); - - Map getHelperMap(); - - Collection getHelperArray(); - } - - interface HelperProjection { - Helper getHelper(); - - String getString(); - } -} diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptorUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptorUnitTests.java deleted file mode 100644 index a43a0eff4..000000000 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/PropertyAccessingMethodInterceptorUnitTests.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.springframework.beans.NotReadablePropertyException; - -/** - * Unit tests for {@link PropertyAccessingMethodInterceptor}. - * - * @author Oliver Gierke - */ -@RunWith(MockitoJUnitRunner.class) -public class PropertyAccessingMethodInterceptorUnitTests { - - @Mock MethodInvocation invocation; - - /** - * @see DATAREST-221 - */ - @Test - public void triggersPropertyAccessOnTarget() throws Throwable { - - Source source = new Source(); - source.firstname = "Dave"; - - when(invocation.getMethod()).thenReturn(Projection.class.getMethod("getFirstname")); - MethodInterceptor interceptor = new PropertyAccessingMethodInterceptor(source); - - assertThat(interceptor.invoke(invocation), is((Object) "Dave")); - } - - /** - * @see DATAREST-221 - */ - @Test(expected = NotReadablePropertyException.class) - public void throwsAppropriateExceptionIfThePropertyCannotBeFound() throws Throwable { - - when(invocation.getMethod()).thenReturn(Projection.class.getMethod("getLastname")); - new PropertyAccessingMethodInterceptor(new Source()).invoke(invocation); - } - - /** - * @see DATAREST-221 - */ - @Test - public void forwardsObjectMethodInvocation() throws Throwable { - - when(invocation.getMethod()).thenReturn(Object.class.getMethod("toString")); - new PropertyAccessingMethodInterceptor(new Source()).invoke(invocation); - } - - static class Source { - - String firstname; - } - - interface Projection { - - String getFirstname(); - - String getLastname(); - } -} diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProxyProjectionFactoryUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProxyProjectionFactoryUnitTests.java deleted file mode 100644 index 79ea6ded1..000000000 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/ProxyProjectionFactoryUnitTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; - -import org.junit.Test; -import org.springframework.aop.TargetClassAware; -import org.springframework.beans.factory.annotation.Value; - -/** - * Unit tests for {@link ProxyProjectionFactory}. - * - * @author Oliver Gierke - */ -public class ProxyProjectionFactoryUnitTests { - - ProjectionFactory factory = new ProxyProjectionFactory(null); - - /** - * @see DATAREST-221 - */ - @Test - public void createsProjectingProxy() { - - Customer customer = new Customer(); - customer.firstname = "Dave"; - customer.lastname = "Matthews"; - - customer.address = new Address(); - customer.address.city = "New York"; - customer.address.zipCode = "ZIP"; - - CustomerExcerpt excerpt = factory.createProjection(customer, CustomerExcerpt.class); - - assertThat(excerpt, is(instanceOf(TargetClassAware.class))); - assertThat(excerpt.getFirstname(), is("Dave")); - assertThat(excerpt.getAddress().getZipCode(), is("ZIP")); - } - - /** - * @see DATAREST-221 - */ - @Test - public void proxyExposesTargetClassAware() { - assertThat(factory.createProjection(new Object(), CustomerExcerpt.class), is(instanceOf(TargetClassAware.class))); - } - - /** - * @see DATAREST-221 - */ - @Test(expected = IllegalArgumentException.class) - public void rejectsNonInterfacesAsProjectionTarget() { - factory.createProjection(new Object(), Object.class); - } - - /** - * @see DATAREST-221 - */ - @Test - public void exposesSpelInvokingMethod() { - - Customer customer = new Customer(); - customer.firstname = "Dave"; - customer.lastname = "Matthews"; - - CustomerExcerpt excerpt = factory.createProjection(customer, CustomerExcerpt.class); - assertThat(excerpt.getFullName(), is("Dave Matthews")); - } - - static class Customer { - - public String firstname, lastname; - public Address address; - } - - static class Address { - - public String zipCode, city; - } - - interface CustomerExcerpt { - - String getFirstname(); - - AddressExcerpt getAddress(); - - @Value("#{target.firstname + ' ' + target.lastname}") - String getFullName(); - } - - interface AddressExcerpt { - - String getZipCode(); - } -} diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/SpelEvaluatingMethodInterceptorUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/SpelEvaluatingMethodInterceptorUnitTests.java deleted file mode 100644 index b42dee267..000000000 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/projection/SpelEvaluatingMethodInterceptorUnitTests.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2014 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.rest.core.projection; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; - -/** - * Unit tests for {@link SpelEvaluatingMethodInterceptor}. - * - * @author Oliver Gierke - */ -@RunWith(MockitoJUnitRunner.class) -public class SpelEvaluatingMethodInterceptorUnitTests { - - @Mock MethodInterceptor delegate; - @Mock MethodInvocation invocation; - - /** - * @see DATAREST-221 - */ - @Test - public void invokesMethodOnTarget() throws Throwable { - - when(invocation.getMethod()).thenReturn(Projection.class.getMethod("propertyFromTarget")); - - MethodInterceptor interceptor = new SpelEvaluatingMethodInterceptor(delegate, new Target(), null); - - assertThat(interceptor.invoke(invocation), is((Object) "property")); - } - - /** - * @see DATAREST-221 - */ - @Test - public void invokesMethodOnBean() throws Throwable { - - when(invocation.getMethod()).thenReturn(Projection.class.getMethod("invokeBean")); - - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - factory.registerSingleton("someBean", new SomeBean()); - - SpelEvaluatingMethodInterceptor interceptor = new SpelEvaluatingMethodInterceptor(delegate, new Target(), factory); - - assertThat(interceptor.invoke(invocation), is((Object) "value")); - } - - interface Projection { - - @Value("#{target.property}") - String propertyFromTarget(); - - @Value("#{@someBean.value}") - String invokeBean(); - } - - static class Target { - - public String getProperty() { - return "property"; - } - } - - static class SomeBean { - - public String getValue() { - return "value"; - } - } -} diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceAssemblerArgumentResolver.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceAssemblerArgumentResolver.java index 238f62511..4dfe880e7 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceAssemblerArgumentResolver.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/PersistentEntityResourceAssemblerArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -16,10 +16,10 @@ package org.springframework.data.rest.webmvc.config; import org.springframework.core.MethodParameter; +import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.projection.ProjectionDefinitions; -import org.springframework.data.rest.core.projection.ProjectionFactory; import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler; import org.springframework.data.rest.webmvc.support.PersistentEntityProjector; import org.springframework.hateoas.EntityLinks; diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java index f4c5c8aba..e2b02c2a6 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java @@ -46,6 +46,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.geo.GeoModule; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.context.PersistentEntities; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.support.DefaultRepositoryInvokerFactory; import org.springframework.data.repository.support.Repositories; import org.springframework.data.repository.support.RepositoryInvokerFactory; @@ -59,7 +60,6 @@ import org.springframework.data.rest.core.event.ValidatingRepositoryEventListene import org.springframework.data.rest.core.mapping.RepositoryResourceMappings; import org.springframework.data.rest.core.mapping.ResourceDescription; import org.springframework.data.rest.core.mapping.ResourceMappings; -import org.springframework.data.rest.core.projection.ProxyProjectionFactory; import org.springframework.data.rest.core.support.DomainObjectMerger; import org.springframework.data.rest.core.support.RepositoryRelProvider; import org.springframework.data.rest.webmvc.BasePathAwareController; @@ -624,9 +624,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon private List defaultMethodArgumentResolvers() { + SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory(); + projectionFactory.setBeanFactory(applicationContext); + PersistentEntityResourceAssemblerArgumentResolver peraResolver = new PersistentEntityResourceAssemblerArgumentResolver( - repositories(), entityLinks(), config().projectionConfiguration(), new ProxyProjectionFactory( - applicationContext), resourceMappings()); + repositories(), entityLinks(), config().projectionConfiguration(), projectionFactory, resourceMappings()); HateoasPageableHandlerMethodArgumentResolver pageableResolver = pageableResolver(); HandlerMethodArgumentResolver defaultedPageableResolver = new DefaultedPageableHandlerMethodArgumentResolver( diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjector.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjector.java index 5502c21fd..dacef1c7e 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjector.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjector.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -15,10 +15,10 @@ */ package org.springframework.data.rest.webmvc.support; +import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMetadata; import org.springframework.data.rest.core.projection.ProjectionDefinitions; -import org.springframework.data.rest.core.projection.ProjectionFactory; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -67,7 +67,7 @@ public class PersistentEntityProjector implements Projector { } Class projectionType = projectionDefinitions.getProjectionType(source.getClass(), projection); - return projectionType == null ? source : factory.createProjection(source, projectionType); + return projectionType == null ? source : factory.createProjection(projectionType, source); } /* @@ -86,7 +86,7 @@ public class PersistentEntityProjector implements Projector { return project(source); } - return projection == null ? source : factory.createProjection(source, projection); + return projection == null ? source : factory.createProjection(projection, source); } /* diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/ResourceProcessorHandlerMethodReturnValueHandlerUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/ResourceProcessorHandlerMethodReturnValueHandlerUnitTests.java index d60602d17..dfa83d96c 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/ResourceProcessorHandlerMethodReturnValueHandlerUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/ResourceProcessorHandlerMethodReturnValueHandlerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2015 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. @@ -36,9 +36,9 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.core.MethodParameter; -import org.springframework.data.rest.core.projection.ProxyProjectionFactory; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.rest.webmvc.ResourceProcessorHandlerMethodReturnValueHandler.ResourcesProcessorWrapper; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; @@ -233,8 +233,8 @@ public class ResourceProcessorHandlerMethodReturnValueHandlerUnitTests { ProjectionProcessor projectionProcessor = new ProjectionProcessor(); resourceProcessors.add(projectionProcessor); - ProxyProjectionFactory factory = new ProxyProjectionFactory(new DefaultListableBeanFactory()); - SampleProjection projection = factory.createProjection(new Sample(), SampleProjection.class); + ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); + SampleProjection projection = factory.createProjection(SampleProjection.class, new Sample()); Resource resource = new Resource(projection); invokeReturnValueHandler("object", is(resource), resource); diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/ProjectionJacksonIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/ProjectionJacksonIntegrationTests.java index 6a87c4636..b41add6db 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/ProjectionJacksonIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/ProjectionJacksonIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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,8 +22,8 @@ import java.util.Arrays; import org.junit.Before; import org.junit.Test; -import org.springframework.data.rest.core.projection.ProjectionFactory; -import org.springframework.data.rest.core.projection.ProxyProjectionFactory; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.hateoas.Resources; import org.springframework.hateoas.core.EvoInflectorRelProvider; import org.springframework.hateoas.hal.Jackson2HalModule; @@ -41,7 +41,7 @@ import com.jayway.jsonpath.JsonPath; public class ProjectionJacksonIntegrationTests { ObjectMapper mapper; - ProjectionFactory factory = new ProxyProjectionFactory(null); + ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); @Before public void setUp() { @@ -62,7 +62,7 @@ public class ProjectionJacksonIntegrationTests { customer.lastname = "Matthews"; customer.address = new Address(); - CustomerProjection projection = factory.createProjection(customer, CustomerProjection.class); + CustomerProjection projection = factory.createProjection(CustomerProjection.class, customer); String result = mapper.writeValueAsString(projection); assertThat(JsonPath.read(result, "$firstname"), is((Object) "Dave")); @@ -83,7 +83,7 @@ public class ProjectionJacksonIntegrationTests { customer.lastname = "Matthews"; customer.address = new Address(); - CustomerProjection projection = factory.createProjection(customer, CustomerProjection.class); + CustomerProjection projection = factory.createProjection(CustomerProjection.class, customer); Resources resources = new Resources(Arrays.asList(projection)); String result = mapper.writeValueAsString(resources); diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjectorUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjectorUnitTests.java index abde640da..9a8118789 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjectorUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/support/PersistentEntityProjectorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -24,9 +24,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration; import org.springframework.data.rest.core.mapping.ResourceMappings; -import org.springframework.data.rest.core.projection.ProjectionFactory; /** * Unit tests for {@link PersistentEntityProjector}. @@ -69,7 +69,7 @@ public class PersistentEntityProjectorUnitTests { Object source = new Object(); projector.project(source); - verify(factory, times(1)).createProjection(source, Sample.class); + verify(factory, times(1)).createProjection(Sample.class, source); } interface Sample {