Made request/response classes immutable and using builder patterns to build them.

This commit is contained in:
Inderjeet Singh 2008-09-20 13:55:52 +00:00
parent 7da18b96e8
commit 9ad4b2bc7e
14 changed files with 215 additions and 160 deletions

View File

@ -15,12 +15,7 @@
*/
package com.google.gson.wsf;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
/**
* Body of a request or response. The body contains a map of name-value pairs.
@ -29,55 +24,21 @@ import com.google.common.collect.Maps;
*
* @author inder
*/
class ContentBody {
protected final ContentBodySpec spec;
protected final Map<String, Object> contents;
ContentBody(ContentBodySpec spec) {
this.spec = spec;
this.contents = Maps.newLinkedHashMap();
}
class ContentBody extends ParamMap {
ContentBody(ContentBodySpec spec, Map<String, Object> contents) {
this.spec = spec;
this.contents = contents;
super(spec, contents);
}
public ContentBodySpec getSpec() {
return spec;
return (ContentBodySpec) spec;
}
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<T> classOfValue) {
Preconditions.checkArgument(spec.isCompatible(key, classOfValue));
return (T) contents.get(key);
}
@SuppressWarnings("unchecked")
public <T> T get(String key, Type typeOfValue) {
Preconditions.checkArgument(spec.isCompatible(key, typeOfValue));
return (T) contents.get(key);
}
public Set<Map.Entry<String, Object>> entrySet() {
return contents.entrySet();
}
public int size() {
return contents.size();
}
public String getContentType() {
return spec.getContentType();
return getSpec().getContentType();
}
public String getCharacterEncoding() {
return spec.getCharacterEncoding();
}
@Override
public String toString() {
return Util.toStringMap(contents);
return getSpec().getCharacterEncoding();
}
}

View File

@ -40,12 +40,12 @@ class ContentBodySpec implements ParamMapSpec {
return paramsSpec.get(paramName);
}
public boolean isCompatible(String paramName, Type type) {
public boolean checkIfCompatible(String paramName, Type type) {
return type.equals(getTypeFor(paramName));
}
public boolean isCompatible(String paramName, Object object) {
return isCompatible(paramName, object.getClass());
public boolean checkIfCompatible(String paramName, Object object) {
return checkIfCompatible(paramName, object.getClass());
}
public Set<Map.Entry<String, Type>> entrySet() {

View File

@ -17,10 +17,8 @@ package com.google.gson.wsf;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
/**
* Map of request or response header objects. There is a {@link HeaderMapSpec} associated with the
@ -28,54 +26,35 @@ import com.google.common.collect.Maps;
*
* @author inder
*/
public final class HeaderMap {
public final class HeaderMap extends ParamMap {
private final Map<String, Object> contents;
private final HeaderMapSpec spec;
public static class Builder extends ParamMap.Builder<HeaderMapSpec> {
@Inject
public Builder(HeaderMapSpec spec) {
super(spec);
}
@Override
public Builder put(String paramName, Object content) {
return (Builder) super.put(paramName, content);
}
@Override
public Builder put(String paramName, Object content, Type typeOfContent) {
return (Builder) super.put(paramName, content, typeOfContent);
}
public HeaderMap create() {
return new HeaderMap(spec, contents);
}
}
public HeaderMap(HeaderMapSpec spec) {
this.spec = spec;
contents = Maps.newHashMap();
private HeaderMap(HeaderMapSpec spec, Map<String, Object> contents) {
super(spec, contents);
}
/**
* If paramValue is a generic type, use {@link #put(String, Object, Type)} instead.
*
* @param headerName
* @param headerValue
*/
public void put(String headerName, Object headerValue) {
Preconditions.checkArgument(spec.isCompatible(headerName, headerValue));
contents.put(headerName, headerValue);
}
public void put(String headerName, Object headerValue, Type typeOfHeaderValue) {
Preconditions.checkArgument(spec.isCompatible(headerName, typeOfHeaderValue));
contents.put(headerName, headerValue);
}
public HeaderMapSpec getSpec() {
return spec;
}
public Object get(String headerName) {
return contents.get(headerName);
}
public Type getSpec(String headerName) {
return spec.getTypeFor(headerName);
}
public Set<Map.Entry<String, Object>> entrySet() {
return contents.entrySet();
}
public int size() {
return contents.size();
}
@Override
public String toString() {
return Util.toStringMap(contents);
public HeaderMapSpec getSpec() {
return (HeaderMapSpec) super.getSpec();
}
}

View File

@ -54,7 +54,7 @@ public final class HeaderMapSpec implements ParamMapSpec {
}
@Override
public boolean isCompatible(String headerName, Type targetType) {
public boolean checkIfCompatible(String headerName, Type targetType) {
Type typeOfHeader = getTypeFor(headerName);
if (typeOfHeader == null) {
return false;
@ -64,8 +64,8 @@ public final class HeaderMapSpec implements ParamMapSpec {
return rawClassOfHeader.isAssignableFrom(rawClassOfTargetType);
}
public boolean isCompatible(String headerName, Object headerValue) {
return isCompatible(headerName, headerValue.getClass());
public boolean checkIfCompatible(String headerName, Object headerValue) {
return checkIfCompatible(headerName, headerValue.getClass());
}
@Override

View File

@ -0,0 +1,75 @@
package com.google.gson.wsf;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
class ParamMap {
public static class Builder<T extends ParamMapSpec> {
protected final Map<String, Object> contents = Maps.newLinkedHashMap();
protected final T spec;
public Builder(T spec) {
this.spec = spec;
}
/**
* If value is a generic type, use {@link #put(String, Object, Type)} instead.
*
* @param key
* @param value
*/
public Builder<T> put(String paramName, Object content) {
return put(paramName, content, content.getClass());
}
public Builder<T> put(String paramName, Object content, Type typeOfContent) {
spec.checkIfCompatible(paramName, typeOfContent);
contents.put(paramName, content);
return this;
}
}
protected final Map<String, Object> contents;
protected final ParamMapSpec spec;
protected ParamMap(ParamMapSpec spec, Map<String, Object> contents) {
this.spec = spec;
this.contents = contents;
}
public ParamMapSpec getSpec() {
return spec;
}
public Object get(String paramName) {
return contents.get(paramName);
}
@SuppressWarnings("unchecked")
public <T> T get(String key, Type typeOfValue) {
Preconditions.checkArgument(spec.checkIfCompatible(key, typeOfValue));
return (T) contents.get(key);
}
public Type getSpec(String headerName) {
return spec.getTypeFor(headerName);
}
public Set<Map.Entry<String, Object>> entrySet() {
return contents.entrySet();
}
public int size() {
return contents.size();
}
@Override
public String toString() {
return Util.toStringMap(contents);
}
}

View File

@ -29,9 +29,9 @@ interface ParamMapSpec {
Type getTypeFor(String paramName);
boolean isCompatible(String paramName, Type type);
boolean checkIfCompatible(String paramName, Type type);
boolean isCompatible(String paramName, Object object);
boolean checkIfCompatible(String paramName, Object object);
public Set<Map.Entry<String, Type>> entrySet();

View File

@ -18,7 +18,6 @@ package com.google.gson.wsf;
import java.lang.reflect.Type;
import java.util.Map;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
/**
@ -31,32 +30,29 @@ import com.google.inject.Inject;
*/
public final class RequestBody extends ContentBody {
public static class Builder {
private final Map<String, Object> contents = Maps.newLinkedHashMap();
private final RequestBodySpec spec;
public static class Builder extends ParamMap.Builder<RequestBodySpec> {
@Inject
public Builder(RequestBodySpec spec) {
this.spec = spec;
super(spec);
}
public Builder add(String paramName, Object content) {
return add(paramName, content, content.getClass());
@Override
public Builder put(String paramName, Object content) {
return (Builder) super.put(paramName, content);
}
public Builder add(String paramName, Object content, Type typeOfContent) {
spec.isCompatible(paramName, typeOfContent);
contents.put(paramName, content);
return this;
@Override
public Builder put(String paramName, Object content, Type typeOfContent) {
return (Builder) super.put(paramName, content, typeOfContent);
}
public RequestBody create() {
RequestBody requestBody = new RequestBody(spec, contents);
return requestBody;
return new RequestBody(spec, contents);
}
}
public RequestBody(RequestBodySpec spec, Map<String, Object> contents) {
private RequestBody(RequestBodySpec spec, Map<String, Object> contents) {
super(spec, contents);
}

View File

@ -16,8 +16,9 @@
package com.google.gson.wsf;
import java.lang.reflect.Type;
import java.util.Map;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
/**
* body of the response. This is written out as JSON to be sent out to the client.
@ -26,28 +27,35 @@ import com.google.common.base.Preconditions;
*/
public final class ResponseBody extends ContentBody {
public ResponseBody(ResponseBodySpec spec) {
super(spec);
public static class Builder extends ParamMap.Builder<ResponseBodySpec> {
@Inject
public Builder(ResponseBodySpec spec) {
super(spec);
}
@Override
public Builder put(String paramName, Object content) {
return (Builder) super.put(paramName, content);
}
@Override
public Builder put(String paramName, Object content, Type typeOfContent) {
return (Builder) super.put(paramName, content, typeOfContent);
}
public ResponseBody create() {
return new ResponseBody(spec, contents);
}
}
private ResponseBody(ResponseBodySpec spec, Map<String, Object> contents) {
super(spec, contents);
}
@Override
public ResponseBodySpec getSpec() {
return (ResponseBodySpec) spec;
}
/**
* If value is a generic type, use {@link #put(String, Object, Type)} instead.
*
* @param key
* @param value
*/
public void put(String key, Object value) {
put(key, value, value.getClass());
}
public void put(String key, Object value, Type typeOfValue) {
Type expectedType = spec.getTypeFor(key);
Preconditions.checkArgument(Util.isAssignableFrom(typeOfValue, expectedType));
contents.put(key, value);
}
}

View File

@ -15,6 +15,8 @@
*/
package com.google.gson.wsf;
import java.lang.reflect.Type;
/**
* The data associated with a Web service response. This includes http response header parameters,
* and {@link ResponseBody}.
@ -27,10 +29,46 @@ public final class WebServiceResponse {
private final ResponseBody body;
private final ResponseSpec spec;
public WebServiceResponse(ResponseSpec spec) {
public static class Builder {
private final HeaderMap.Builder headers;
private final ResponseBody.Builder body;
private final ResponseSpec spec;
public Builder(ResponseSpec spec) {
this.spec = spec;
headers = new HeaderMap.Builder(spec.getHeadersSpec());
body = new ResponseBody.Builder(spec.getBodySpec());
}
public Builder putHeader(String paramName, Object content) {
headers.put(paramName, content);
return this;
}
public Builder putHeader(String paramName, Object content, Type typeOfContent) {
headers.put(paramName, content, typeOfContent);
return this;
}
public Builder putBody(String paramName, Object content) {
body.put(paramName, content);
return this;
}
public Builder put(String paramName, Object content, Type typeOfContent) {
body.put(paramName, content, typeOfContent);
return this;
}
public WebServiceResponse create() {
return new WebServiceResponse(spec, headers.create(), body.create());
}
}
private WebServiceResponse(ResponseSpec spec, HeaderMap headers, ResponseBody body) {
this.spec = spec;
headers = new HeaderMap(spec.getHeadersSpec());
body = new ResponseBody(spec.getBodySpec());
this.headers = headers;
this.body = body;
}
public WebServiceResponse(HeaderMap responseHeaders, ResponseBody responseBody) {

View File

@ -60,23 +60,23 @@ public final class ResponseReceiver {
}
private HeaderMap readResponseHeaders(HttpURLConnection conn, HeaderMapSpec paramsSpec) {
HeaderMap params = new HeaderMap(paramsSpec);
HeaderMap.Builder paramsBuilder = new HeaderMap.Builder(paramsSpec);
for (Map.Entry<String, Type> entry : paramsSpec.entrySet()) {
String paramName = entry.getKey();
String json = conn.getHeaderField(paramName);
if (json != null) {
Type typeOfT = paramsSpec.getTypeFor(paramName);
Object value = gson.fromJson(json, typeOfT);
params.put(paramName, value, typeOfT);
paramsBuilder.put(paramName, value, typeOfT);
}
}
return params;
return paramsBuilder.create();
}
private ResponseBody readResponseBody(HttpURLConnection conn, ResponseBodySpec bodySpec)
throws IOException {
if (bodySpec.size() == 0) {
return new ResponseBody(bodySpec);
return new ResponseBody.Builder(bodySpec).create();
}
String connContentType = conn.getContentType();
Preconditions.checkArgument(connContentType.contains(bodySpec.getContentType()));

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.wsf.inject;
package com.google.gson.wsf.inject.server;
import javax.servlet.http.HttpServletRequest;
@ -23,6 +23,7 @@ import com.google.gson.wsf.WebServiceCallSpec;
import com.google.gson.wsf.WebServiceRequest;
import com.google.gson.wsf.WebServiceResponse;
import com.google.gson.wsf.server.RequestReceiver;
import com.google.gson.wsf.server.WebServiceCallServerBuilder;
import com.google.inject.Inject;
import com.google.inject.Provider;
@ -31,13 +32,13 @@ import com.google.inject.Provider;
*
* @author inder
*/
public final class WebServiceCallProvider implements Provider<WebServiceCall> {
public final class WebServiceCallServerProvider implements Provider<WebServiceCallServerBuilder> {
private final Gson gson;
private final WebServiceCallSpec callSpec;
private final HttpServletRequest request;
@Inject
public WebServiceCallProvider(Gson gson, HttpServletRequest request,
public WebServiceCallServerProvider(Gson gson, HttpServletRequest request,
WebServiceCallSpec callSpec) {
this.callSpec = callSpec;
this.gson = gson;
@ -45,13 +46,12 @@ public final class WebServiceCallProvider implements Provider<WebServiceCall> {
}
@Override
public WebServiceCall get() {
public WebServiceCallServerBuilder get() {
RequestReceiver receiver = new RequestReceiver(gson, callSpec.getRequestSpec());
WebServiceRequest wsRequest = receiver.receive(request);
// No need to populate response headers or response body
WebServiceResponse wsResponse = new WebServiceResponse(callSpec.getResponseSpec());
return new WebServiceCall(callSpec, wsRequest, wsResponse);
WebServiceResponse.Builder responseBuilder =
new WebServiceResponse.Builder(callSpec.getResponseSpec());
return new WebServiceCallServerBuilder(callSpec, wsRequest, responseBuilder);
}
}

View File

@ -20,7 +20,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
@ -68,7 +67,7 @@ public final class RequestReceiver {
private HeaderMap buildRequestParams(HttpServletRequest request) {
HeaderMapSpec paramsSpec = this.spec.getHeadersSpec();
HeaderMap params = new HeaderMap(paramsSpec);
HeaderMap.Builder paramsBuilder = new HeaderMap.Builder(paramsSpec);
for (Map.Entry<String, Type> param : paramsSpec.entrySet()) {
String name = param.getKey();
Type type = param.getValue();
@ -79,10 +78,10 @@ public final class RequestReceiver {
}
if (header != null && !header.equals("")) {
Object value = gson.fromJson(header, type);
params.put(name, value);
paramsBuilder.put(name, value);
}
}
return params;
return paramsBuilder.create();
}
private RequestBody buildRequestBody(HttpServletRequest request) throws IOException {
@ -96,6 +95,6 @@ public final class RequestReceiver {
}
private RequestBody createEmptyRequestBody(RequestBodySpec bodySpec) {
return new RequestBody(bodySpec, new HashMap<String, Object>());
return new RequestBody.Builder(bodySpec).create();
}
}

View File

@ -16,7 +16,6 @@
package com.google.gson.wsf.typeadapters;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.InstanceCreator;
@ -65,13 +64,13 @@ public class RequestBodyGsonConverter implements JsonSerializer<RequestBody>,
String key = entry.getKey();
Type entryType = spec.getTypeFor(key);
Object value = context.deserialize(entry.getValue(), entryType);
builder.add(key, value);
builder.put(key, value);
}
return builder.create();
}
@Override
public RequestBody createInstance(Type type) {
return new RequestBody(spec, new HashMap<String, Object>());
return new RequestBody.Builder(spec).create();
}
}

View File

@ -59,18 +59,18 @@ public final class ResponseBodyGsonConverter implements JsonSerializer<ResponseB
@Override
public ResponseBody deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
ResponseBody responseBody = new ResponseBody(spec);
ResponseBody.Builder responseBodyBuilder = new ResponseBody.Builder(spec);
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
String key = entry.getKey();
Type entryType = spec.getTypeFor(key);
Object value = context.deserialize(entry.getValue(), entryType);
responseBody.put(key, value, entryType);
responseBodyBuilder.put(key, value, entryType);
}
return responseBody;
return responseBodyBuilder.create();
}
@Override
public ResponseBody createInstance(Type type) {
return new ResponseBody(spec);
return new ResponseBody.Builder(spec).create();
}
}