1919import static org .springframework .util .Assert .notNull ;
2020
2121import com .github .sonus21 .rqueue .utils .SerializationUtils ;
22+ import java .lang .reflect .Field ;
2223import java .lang .reflect .TypeVariable ;
2324import java .util .Collection ;
2425import java .util .List ;
3738
3839/**
3940 * A converter to turn the payload of a {@link Message} from serialized form to a typed String and
40- * vice versa. This class does not support generic class except {@link List},even for list the
41- * entries should be non generic .
41+ * vice versa. Supports {@link List} and single-level generic envelope types (e.g. {@code Event<T>})
42+ * where type parameters are non-generic and can be resolved from non-null field values .
4243 */
4344@ Slf4j
4445public class GenericMessageConverter implements SmartMessageConverter {
@@ -137,7 +138,12 @@ private String getClassNameForCollection(String name, Collection<?> payload) {
137138 if (payload .isEmpty ()) {
138139 return null ;
139140 }
140- String itemClassName = getClassName (((List <?>) payload ).get (0 ));
141+ Object firstItem = ((List <?>) payload ).get (0 );
142+ // Only support non-generic item classes in lists to avoid ambiguous encoding
143+ if (firstItem .getClass ().getTypeParameters ().length > 0 ) {
144+ return null ;
145+ }
146+ String itemClassName = getClassName (firstItem );
141147 if (itemClassName == null ) {
142148 return null ;
143149 }
@@ -146,12 +152,40 @@ private String getClassNameForCollection(String name, Collection<?> payload) {
146152 return null ;
147153 }
148154
149- private String getGenericFieldBasedClassName (Class <?> clazz ) {
155+ private Class <?> resolveTypeVariable (Class <?> clazz , TypeVariable <?> tv , Object payload ) {
156+ // TypeVariable instances are scoped to the class that declares them, so
157+ // field.getGenericType().equals(tv) can only match fields declared on clazz itself.
158+ // Superclass fields reference their own TypeVariable instances, which are distinct objects.
159+ for (Field field : clazz .getDeclaredFields ()) {
160+ if (field .getGenericType ().equals (tv )) {
161+ field .setAccessible (true );
162+ try {
163+ Object value = field .get (payload );
164+ if (value != null ) {
165+ return value .getClass ();
166+ }
167+ } catch (IllegalAccessException e ) {
168+ log .debug ("Cannot access field {}" , field .getName (), e );
169+ }
170+ }
171+ }
172+ return null ;
173+ }
174+
175+ private String getGenericFieldBasedClassName (Class <?> clazz , Object payload ) {
150176 TypeVariable <?>[] typeVariables = clazz .getTypeParameters ();
151177 if (typeVariables .length == 0 ) {
152178 return clazz .getName ();
153179 }
154- return null ;
180+ StringBuilder sb = new StringBuilder (clazz .getName ());
181+ for (TypeVariable <?> tv : typeVariables ) {
182+ Class <?> resolved = resolveTypeVariable (clazz , tv , payload );
183+ if (resolved == null || resolved .getTypeParameters ().length > 0 ) {
184+ return null ;
185+ }
186+ sb .append ('#' ).append (resolved .getName ());
187+ }
188+ return sb .toString ();
155189 }
156190
157191 private String getClassName (Object payload ) {
@@ -160,7 +194,7 @@ private String getClassName(Object payload) {
160194 if (payload instanceof Collection ) {
161195 return getClassNameForCollection (name , (Collection <?>) payload );
162196 }
163- return getGenericFieldBasedClassName (payloadClass );
197+ return getGenericFieldBasedClassName (payloadClass , payload );
164198 }
165199
166200 private JavaType getTargetType (Msg msg ) throws ClassNotFoundException {
0 commit comments