From 9f3b8a2430a94c3c4672c06f910e334bec58c935 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 31 Oct 2013 15:28:20 +0100 Subject: [PATCH] GenericTypeResolver's resolveTypeArguments needs to return null for raw types (for backwards compatibility with 3.2) Issue: SPR-11052 --- .../core/GenericTypeResolver.java | 6 +- .../springframework/core/ResolvableType.java | 4 +- .../core/GenericTypeResolverTests.java | 15 ++-- .../ConvertingEncoderDecoderSupport.java | 74 +++++++++---------- 4 files changed, 48 insertions(+), 51 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java index bec2f7542a..9b8731a752 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -240,12 +240,12 @@ public abstract class GenericTypeResolver { * @return the resolved type of each argument, with the array size matching the * number of actual type arguments, or {@code null} if not resolvable */ - public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) { + public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) { ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc); - if (!type.hasGenerics()) { + if (!type.hasGenerics() || type.hasUnresolvableGenerics()) { return null; } - return type.resolveGenerics(Object.class); + return type.resolveGenerics(); } /** diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 7ec5f032d9..ff1993dcff 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -567,7 +567,7 @@ public final class ResolvableType implements Serializable { this.resolved = resolveClass(); this.isResolved = true; } - return (this.resolved == null ? fallback : this.resolved); + return (this.resolved != null ? this.resolved : fallback); } private Class resolveClass() { @@ -576,7 +576,7 @@ public final class ResolvableType implements Serializable { } if (this.type instanceof GenericArrayType) { Class resolvedComponent = getComponentType().resolve(); - return (resolvedComponent == null ? null : Array.newInstance(resolvedComponent, 0).getClass()); + return (resolvedComponent != null ? Array.newInstance(resolvedComponent, 0).getClass() : null); } return resolveType().resolve(); } diff --git a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java index b6b42006fb..fc060cc8a3 100644 --- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java @@ -138,10 +138,17 @@ public class GenericTypeResolverTests { } @Test - public void getGenericsCannotBeResovled() throws Exception { + public void getGenericsCannotBeResolved() throws Exception { // SPR-11030 - Class[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class); - assertThat(resolved, equalTo(new Class[] { Object.class })); + Class[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class); + assertNull(resolved); + } + + @Test + public void getRawMapTypeCannotBeResolved() throws Exception { + // SPR-11052 + Class[] resolved = GenericTypeResolver.resolveTypeArguments(Map.class, Map.class); + assertNull(resolved); } @Test @@ -300,11 +307,9 @@ public class GenericTypeResolverTests { static abstract class WithArrayBase { public abstract T[] array(T... args); - } static abstract class WithArray extends WithArrayBase { - } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/ConvertingEncoderDecoderSupport.java b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/ConvertingEncoderDecoderSupport.java index 5158eeac7e..a2c2b98e82 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/ConvertingEncoderDecoderSupport.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/ConvertingEncoderDecoderSupport.java @@ -17,7 +17,6 @@ package org.springframework.web.socket.adapter; import java.nio.ByteBuffer; - import javax.websocket.DecodeException; import javax.websocket.Decoder; import javax.websocket.EncodeException; @@ -65,10 +64,8 @@ import org.springframework.web.context.ContextLoader; * * @author Phillip Webb * @since 4.0 - * - * @param The type being converted to (for Encoder) or from (for Decoder) - * @param The WebSocket message type ({@link String} or {@link ByteBuffer}) - * + * @param the type being converted to (for Encoder) or from (for Decoder) + * @param the WebSocket message type ({@link String} or {@link ByteBuffer}) * @see ConvertingEncoderDecoderSupport.BinaryEncoder * @see ConvertingEncoderDecoderSupport.BinaryDecoder * @see ConvertingEncoderDecoderSupport.TextEncoder @@ -107,15 +104,13 @@ public abstract class ConvertingEncoderDecoderSupport { */ protected ConversionService getConversionService() { ApplicationContext applicationContext = getApplicationContext(); - Assert.state(applicationContext != null, - "Unable to locate the Spring ApplicationContext"); + Assert.state(applicationContext != null, "Unable to locate the Spring ApplicationContext"); try { return applicationContext.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class); } catch (BeansException ex) { - throw new IllegalStateException( - "Unable to find ConversionService, please configure a '" - + CONVERSION_SERVICE_BEAN_NAME + "' or override getConversionService()", ex); + throw new IllegalStateException("Unable to find ConversionService: please configure a '" + + CONVERSION_SERVICE_BEAN_NAME + "' or override the getConversionService() method", ex); } } @@ -148,8 +143,12 @@ public abstract class ConvertingEncoderDecoderSupport { } private Class[] resolveTypeArguments() { - return GenericTypeResolver.resolveTypeArguments(getClass(), - ConvertingEncoderDecoderSupport.class); + Class[] resolved = GenericTypeResolver.resolveTypeArguments(getClass(), ConvertingEncoderDecoderSupport.class); + if (resolved == null) { + throw new IllegalStateException("ConvertingEncoderDecoderSupport's generic types T and M " + + "need to be substituted in subclass: " + getClass()); + } + return resolved; } /** @@ -185,12 +184,12 @@ public abstract class ConvertingEncoderDecoderSupport { } catch (ConversionException ex) { if (message instanceof String) { - throw new DecodeException((String) message, "Unable to decode " + - "websocket message using ConversionService", ex); + throw new DecodeException((String) message, + "Unable to decode websocket message using ConversionService", ex); } if (message instanceof ByteBuffer) { - throw new DecodeException((ByteBuffer) message, "Unable to decode " + - "websocket message using ConversionService", ex); + throw new DecodeException((ByteBuffer) message, + "Unable to decode websocket message using ConversionService", ex); } throw ex; } @@ -198,50 +197,43 @@ public abstract class ConvertingEncoderDecoderSupport { /** - * A Binary {@link javax.websocket.Encoder.Binary javax.websocket.Encoder} that - * delegates to Spring's conversion service. See - * {@link ConvertingEncoderDecoderSupport} for details. - * - * @param The type that this Encoder can convert to. + * A binary {@link javax.websocket.Encoder.Binary javax.websocket.Encoder} that delegates + * to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for details. + * @param the type that this Encoder can convert to */ - public static abstract class BinaryEncoder extends - ConvertingEncoderDecoderSupport implements Encoder.Binary { + public static abstract class BinaryEncoder extends ConvertingEncoderDecoderSupport + implements Encoder.Binary { } /** - * A Binary {@link javax.websocket.Encoder.Binary javax.websocket.Encoder} that delegates - * to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for - * details. - * - * @param The type that this Decoder can convert from. + * A binary {@link javax.websocket.Encoder.Binary javax.websocket.Encoder} that delegates + * to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for details. + * @param the type that this Decoder can convert from */ - public static abstract class BinaryDecoder extends - ConvertingEncoderDecoderSupport implements Decoder.Binary { + public static abstract class BinaryDecoder extends ConvertingEncoderDecoderSupport + implements Decoder.Binary { } /** - * A Text {@link javax.websocket.Encoder.Text javax.websocket.Encoder} that delegates + * A text {@link javax.websocket.Encoder.Text javax.websocket.Encoder} that delegates * to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for * details. - * - * @param The type that this Encoder can convert to. + * @param the type that this Encoder can convert to */ - public static abstract class TextEncoder extends - ConvertingEncoderDecoderSupport implements Encoder.Text { + public static abstract class TextEncoder extends ConvertingEncoderDecoderSupport + implements Encoder.Text { } /** * A Text {@link javax.websocket.Encoder.Text javax.websocket.Encoder} that delegates - * to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for - * details. - * - * @param The type that this Decoder can convert from. + * to Spring's conversion service. See {@link ConvertingEncoderDecoderSupport} for details. + * @param the type that this Decoder can convert from */ - public static abstract class TextDecoder extends - ConvertingEncoderDecoderSupport implements Decoder.Text { + public static abstract class TextDecoder extends ConvertingEncoderDecoderSupport + implements Decoder.Text { } }