1919import java .io .IOException ;
2020import java .io .InputStreamReader ;
2121import java .io .OutputStreamWriter ;
22- import java .io .StringWriter ;
23- import java .io .UnsupportedEncodingException ;
2422import java .lang .reflect .Method ;
2523import java .nio .charset .Charset ;
2624import java .util .concurrent .ConcurrentHashMap ;
3937import org .springframework .http .converter .AbstractHttpMessageConverter ;
4038import org .springframework .http .converter .HttpMessageNotReadableException ;
4139import org .springframework .http .converter .HttpMessageNotWritableException ;
42- import org .springframework .util .Assert ;
4340import org .springframework .util .FileCopyUtils ;
4441
4542
4643/**
47- * Implementation of {@link org.springframework.http.converter. HttpMessageConverter}
48- * that can read and write Protobuf {@code Message}s using the
49- * <a href="https://developers.google.com/protocol-buffers/">Google Protocol buffers</a> library .
44+ * An {@code HttpMessageConverter} that can read and write Protobuf
45+ * {@link com.google.protobuf. Message} using
46+ * <a href="https://developers.google.com/protocol-buffers/">Google Protocol buffers</a>.
5047 *
51- * <p>By default, it supports {@code application/json}, {@code application/xml}, {@code text/plain}
52- * and {@code application/x-protobuf}. {@code text/html} is only supported when writing messages.
48+ * <p>By default it supports {@code "application/json"}, {@code "application/xml"},
49+ * {@code "text/plain"} and {@code "application/x-protobuf"} while writing also
50+ * supports {@code "text/html"}
51+ *
52+ * <p>To generate Message Java classes you need to install the protoc binary.
5353 *
54- * <p>In order to generate Message Java classes, you should install the protoc binary on your system.
5554 * <p>Tested against Protobuf version 2.5.0.
5655 *
5756 * @author Alex Antonov
@@ -65,60 +64,63 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<M
6564 public static final MediaType PROTOBUF = new MediaType ("application" , "x-protobuf" , DEFAULT_CHARSET );
6665
6766 public static final String X_PROTOBUF_SCHEMA_HEADER = "X-Protobuf-Schema" ;
67+
6868 public static final String X_PROTOBUF_MESSAGE_HEADER = "X-Protobuf-Message" ;
6969
70- private static final ConcurrentHashMap <Class <?>, Method > newBuilderMethodCache =
71- new ConcurrentHashMap < Class <?>, Method >();
70+ private static final ConcurrentHashMap <Class <?>, Method > methodCache = new ConcurrentHashMap < Class <?>, Method >();
71+
7272
7373 private ExtensionRegistry extensionRegistry = ExtensionRegistry .newInstance ();
7474
75+
7576 /**
76- * Construct a new {@code ProtobufHttpMessageConverter} .
77+ * Construct a new instance .
7778 */
7879 public ProtobufHttpMessageConverter () {
7980 this (null );
8081 }
8182
8283 /**
83- * Construct a new {@code ProtobufHttpMessageConverter} with a {@link ExtensionRegistryInitializer},
84- * allowing to register message extensions.
84+ * Construct a new instance with an {@link ExtensionRegistryInitializer}
85+ * that allows the registration of message extensions.
8586 */
8687 public ProtobufHttpMessageConverter (ExtensionRegistryInitializer registryInitializer ) {
87-
88- //TODO: should we handle "*+json", "*+xml" as well?
89- super (PROTOBUF , MediaType .TEXT_PLAIN ,
90- MediaType .APPLICATION_XML , MediaType .APPLICATION_JSON );
91- Assert .notNull (registryInitializer , "ExtensionRegistryInitializer must not be null" );
92- registryInitializer .initializeExtensionRegistry (extensionRegistry );
88+ super (PROTOBUF , MediaType .TEXT_PLAIN , MediaType .APPLICATION_XML , MediaType .APPLICATION_JSON );
89+ if (this .extensionRegistry != null ) {
90+ registryInitializer .initializeExtensionRegistry (this .extensionRegistry );
91+ }
9392 }
9493
94+
9595 @ Override
9696 protected boolean supports (Class <?> clazz ) {
9797 return Message .class .isAssignableFrom (clazz );
9898 }
9999
100-
101100 @ Override
102- protected Message readInternal (Class <? extends Message > clazz , HttpInputMessage inputMessage ) throws IOException , HttpMessageNotReadableException {
101+ protected Message readInternal (Class <? extends Message > clazz , HttpInputMessage inputMessage )
102+ throws IOException , HttpMessageNotReadableException {
103+
103104 MediaType contentType = inputMessage .getHeaders ().getContentType ();
104- contentType = contentType != null ? contentType : PROTOBUF ;
105+ contentType = ( contentType != null ? contentType : PROTOBUF ) ;
105106
106- InputStreamReader reader = new InputStreamReader (inputMessage .getBody (), getCharset (inputMessage .getHeaders ()));
107+ Charset charset = getCharset (inputMessage .getHeaders ());
108+ InputStreamReader reader = new InputStreamReader (inputMessage .getBody (), charset );
107109
108110 try {
109- Message .Builder builder = createMessageBuilder (clazz );
111+ Message .Builder builder = getMessageBuilder (clazz );
110112
111113 if (MediaType .APPLICATION_JSON .isCompatibleWith (contentType )) {
112- JsonFormat .merge (reader , extensionRegistry , builder );
114+ JsonFormat .merge (reader , this . extensionRegistry , builder );
113115 }
114116 else if (MediaType .TEXT_PLAIN .isCompatibleWith (contentType )) {
115- TextFormat .merge (reader , extensionRegistry , builder );
117+ TextFormat .merge (reader , this . extensionRegistry , builder );
116118 }
117119 else if (MediaType .APPLICATION_XML .isCompatibleWith (contentType )) {
118- XmlFormat .merge (reader , extensionRegistry , builder );
120+ XmlFormat .merge (reader , this . extensionRegistry , builder );
119121 }
120- else { // ProtobufHttpMessageConverter.PROTOBUF
121- builder .mergeFrom (inputMessage .getBody (), extensionRegistry );
122+ else {
123+ builder .mergeFrom (inputMessage .getBody (), this . extensionRegistry );
122124 }
123125 return builder .build ();
124126 }
@@ -138,13 +140,13 @@ private Charset getCharset(HttpHeaders headers) {
138140 * Create a new {@code Message.Builder} instance for the given class.
139141 * <p>This method uses a ConcurrentHashMap for caching method lookups.
140142 */
141- private Message .Builder createMessageBuilder (Class <? extends Message > clazz ) throws Exception {
142- Method m = newBuilderMethodCache .get (clazz );
143- if (m == null ) {
144- m = clazz .getMethod ("newBuilder" );
145- newBuilderMethodCache .put (clazz , m );
143+ private Message .Builder getMessageBuilder (Class <? extends Message > clazz ) throws Exception {
144+ Method method = methodCache .get (clazz );
145+ if (method == null ) {
146+ method = clazz .getMethod ("newBuilder" );
147+ methodCache .put (clazz , method );
146148 }
147- return (Message .Builder ) m .invoke (clazz );
149+ return (Message .Builder ) method .invoke (clazz );
148150 }
149151
150152 /**
@@ -157,7 +159,9 @@ protected boolean canWrite(MediaType mediaType) {
157159 }
158160
159161 @ Override
160- protected void writeInternal (Message message , HttpOutputMessage outputMessage ) throws IOException , HttpMessageNotWritableException {
162+ protected void writeInternal (Message message , HttpOutputMessage outputMessage )
163+ throws IOException , HttpMessageNotWritableException {
164+
161165 MediaType contentType = outputMessage .getHeaders ().getContentType ();
162166 Charset charset = getCharset (contentType );
163167 OutputStreamWriter writer = new OutputStreamWriter (outputMessage .getBody (), charset );
@@ -185,15 +189,12 @@ private Charset getCharset(MediaType contentType) {
185189 }
186190
187191 /**
188- * Set the "X-Protobuf-*" HTTP headers when responding with
189- * a message of ContentType "application/x-protobuf"
192+ * Set the "X-Protobuf-*" HTTP headers when responding with a message of
193+ * content type "application/x-protobuf"
190194 */
191- private void setProtoHeader (final HttpOutputMessage response , final Message message ) {
192- response .getHeaders ().set (X_PROTOBUF_SCHEMA_HEADER ,
193- message .getDescriptorForType ().getFile ().getName ());
194-
195- response .getHeaders ().set (X_PROTOBUF_MESSAGE_HEADER ,
196- message .getDescriptorForType ().getFullName ());
195+ private void setProtoHeader (HttpOutputMessage response , Message message ) {
196+ response .getHeaders ().set (X_PROTOBUF_SCHEMA_HEADER , message .getDescriptorForType ().getFile ().getName ());
197+ response .getHeaders ().set (X_PROTOBUF_MESSAGE_HEADER , message .getDescriptorForType ().getFullName ());
197198 }
198199
199200 @ Override
0 commit comments