1616
1717package org .springframework .kafka .support .serializer ;
1818
19+ import java .lang .reflect .InvocationTargetException ;
20+ import java .lang .reflect .Method ;
1921import java .nio .charset .Charset ;
2022import java .nio .charset .StandardCharsets ;
23+ import java .util .Map ;
2124import java .util .function .BiFunction ;
2225import java .util .function .Function ;
2326
2427import org .apache .kafka .common .header .Headers ;
2528import org .apache .kafka .common .serialization .Deserializer ;
2629
2730import org .springframework .util .Assert ;
31+ import org .springframework .util .ClassUtils ;
2832
2933/**
3034 * Generic {@link org.apache.kafka.common.serialization.Deserializer Deserializer} for deserialization of entity from
3842 */
3943public class ParseStringDeserializer <T > implements Deserializer <T > {
4044
41- private final BiFunction <String , Headers , T > parser ;
45+ /**
46+ * Property for the key parser method.
47+ */
48+ public static final String KEY_PARSER = "spring.message.key.parser" ;
49+
50+ /**
51+ * Property for the key parser method.
52+ */
53+ public static final String VALUE_PARSER = "spring.message.value.parser" ;
54+
55+ private static final BiFunction <String , Headers , ?> NO_PARSER = (str , headers ) -> {
56+ throw new IllegalStateException ("A parser must be provided either via a constructor or consumer properties" );
57+ };
58+
59+ private BiFunction <String , Headers , T > parser = (BiFunction <String , Headers , T >) NO_PARSER ;
4260
4361 private Charset charset = StandardCharsets .UTF_8 ;
4462
63+ /**
64+ * Construct an instance with no parser function; a static method name must be
65+ * provided in the consumer config {@link #KEY_PARSER} or {@link #VALUE_PARSER}
66+ * properties.
67+ */
68+ public ParseStringDeserializer () {
69+ }
70+
4571 /**
4672 * Construct an instance with the supplied parser function.
4773 * @param parser the function.
@@ -58,6 +84,67 @@ public ParseStringDeserializer(BiFunction<String, Headers, T> parser) {
5884 this .parser = parser ;
5985 }
6086
87+ @ SuppressWarnings ("unchecked" )
88+ @ Override
89+ public void configure (Map <String , ?> configs , boolean isKey ) {
90+ if (NO_PARSER .equals (this .parser )) {
91+ String parserMethod = (String ) configs .get (isKey ? KEY_PARSER : VALUE_PARSER );
92+ Assert .state (parserMethod != null ,
93+ "A parser must be provided either via a constructor or consumer properties" );
94+ int lastDotPosn = parserMethod .lastIndexOf ("." );
95+ Assert .state (lastDotPosn > 1 ,
96+ "the parser method needs to be a class name followed by the method name, separated by '.'" );
97+ Class <?> clazz ;
98+ try {
99+ clazz = ClassUtils .forName (parserMethod .substring (0 , lastDotPosn ),
100+ getClass ().getClassLoader ());
101+ }
102+ catch (ClassNotFoundException | LinkageError e ) {
103+ throw new IllegalStateException (e );
104+ }
105+ parserMethod = parserMethod .substring (lastDotPosn + 1 );
106+ Method method ;
107+ try {
108+ method = clazz .getDeclaredMethod (parserMethod , String .class , Headers .class );
109+ }
110+ catch (@ SuppressWarnings ("unused" ) NoSuchMethodException e ) {
111+ try {
112+ method = clazz .getDeclaredMethod (parserMethod , String .class );
113+ }
114+ catch (NoSuchMethodException e1 ) {
115+ throw new IllegalStateException ("the parser method must take '(String, Headers)' or '(String)'" );
116+ }
117+ catch (SecurityException e1 ) {
118+ throw new IllegalStateException (e1 );
119+ }
120+ }
121+ catch (SecurityException e ) {
122+ throw new IllegalStateException (e );
123+ }
124+ Method parseMethod = method ;
125+ if (method .getParameters ().length > 1 ) {
126+ this .parser = (str , headers ) -> {
127+ try {
128+ return (T ) parseMethod .invoke (null , str , headers );
129+ }
130+ catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) {
131+ throw new IllegalStateException (e );
132+ }
133+ };
134+ }
135+ else {
136+ this .parser = (str , headers ) -> {
137+ try {
138+ return (T ) parseMethod .invoke (null , str );
139+ }
140+ catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) {
141+ throw new IllegalStateException (e );
142+ }
143+ };
144+ }
145+ }
146+ }
147+
61148 @ Override
62149 public T deserialize (String topic , byte [] data ) {
63150 return deserialize (topic , null , data );
0 commit comments