View Javadoc

1   /**
2    * Licensed to jclouds, Inc. (jclouds) under one or more
3    * contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  jclouds licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.jclouds.json.config;
20  
21  import java.lang.reflect.Type;
22  import java.util.Date;
23  import java.util.Enumeration;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Properties;
27  import java.util.logging.Level;
28  import java.util.logging.Logger;
29  
30  import javax.inject.Inject;
31  import javax.inject.Singleton;
32  
33  import org.jclouds.crypto.CryptoStreams;
34  import org.jclouds.date.DateService;
35  import org.jclouds.domain.JsonBall;
36  import org.jclouds.json.Json;
37  import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
38  import org.jclouds.json.internal.GsonWrapper;
39  
40  import com.google.common.collect.ImmutableMap;
41  import com.google.common.collect.ImmutableMap.Builder;
42  import com.google.common.collect.Maps;
43  import com.google.common.primitives.Bytes;
44  import com.google.gson.Gson;
45  import com.google.gson.GsonBuilder;
46  import com.google.gson.JsonDeserializationContext;
47  import com.google.gson.JsonDeserializer;
48  import com.google.gson.JsonElement;
49  import com.google.gson.JsonLiteral;
50  import com.google.gson.JsonParseException;
51  import com.google.gson.JsonPrimitive;
52  import com.google.gson.JsonSerializationContext;
53  import com.google.gson.JsonSerializer;
54  import com.google.gson.ObjectMapTypeAdapter;
55  import com.google.gson.reflect.TypeToken;
56  import com.google.inject.AbstractModule;
57  import com.google.inject.ImplementedBy;
58  import com.google.inject.Provides;
59  import com.google.inject.TypeLiteral;
60  
61  /**
62   * Contains logic for parsing objects from Strings.
63   * 
64   * @author Adrian Cole
65   */
66  public class GsonModule extends AbstractModule {
67  
68     @SuppressWarnings("rawtypes")
69     @Provides
70     @Singleton
71     Gson provideGson(JsonBallAdapter jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
72              ByteArrayAdapter byteArrayAdapter, SerializePropertiesDefaults propertiesAdapter,
73              JsonAdapterBindings bindings) throws ClassNotFoundException, Exception {
74        GsonBuilder builder = new GsonBuilder();
75        Logger.getLogger("com.google.gson.ParameterizedTypeHandlerMap").setLevel(Level.OFF);
76        builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue());
77        builder.registerTypeHierarchyAdapter(Map.class, new ObjectMapTypeAdapter());
78        builder.registerTypeAdapter(JsonBall.class, jsonAdapter);
79        builder.registerTypeAdapter(Date.class, adapter);
80        builder.registerTypeAdapter(Properties.class, propertiesAdapter);
81        builder.registerTypeAdapter(new TypeToken<List<Byte>>() {
82        }.getType(), byteListAdapter);
83        builder.registerTypeAdapter(byte[].class, byteArrayAdapter);
84        for (Map.Entry<Type, Object> binding : bindings.getBindings().entrySet()) {
85           builder.registerTypeAdapter(binding.getKey(), binding.getValue());
86        }
87        return builder.create();
88     }
89  
90     // http://code.google.com/p/google-gson/issues/detail?id=326
91     @ImplementedBy(JsonBallAdapterImpl.class)
92     public static interface JsonBallAdapter extends JsonSerializer<JsonBall>, JsonDeserializer<JsonBall> {
93  
94     }
95  
96     @Singleton
97     public static class JsonBallAdapterImpl implements JsonBallAdapter {
98  
99        public JsonElement serialize(JsonBall src, Type typeOfSrc, JsonSerializationContext context) {
100          return new JsonLiteral(src);
101       }
102 
103       public JsonBall deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
104                throws JsonParseException {
105          return new JsonBall(json.toString());
106       }
107 
108    }
109 
110    @ImplementedBy(CDateAdapter.class)
111    public static interface DateAdapter extends JsonSerializer<Date>, JsonDeserializer<Date> {
112 
113    }
114 
115    @ImplementedBy(HexByteListAdapter.class)
116    public static interface ByteListAdapter extends JsonSerializer<List<Byte>>, JsonDeserializer<List<Byte>> {
117 
118    }
119 
120    @ImplementedBy(HexByteArrayAdapter.class)
121    public static interface ByteArrayAdapter extends JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
122 
123    }
124 
125    @Singleton
126    public static class HexByteListAdapter implements ByteListAdapter {
127 
128       @Override
129       public List<Byte> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
130                throws JsonParseException {
131          return Bytes.asList(CryptoStreams.hex(json.getAsString()));
132       }
133 
134       @Override
135       public JsonElement serialize(List<Byte> src, Type typeOfSrc, JsonSerializationContext context) {
136          return new JsonPrimitive(CryptoStreams.hex(Bytes.toArray(src)));
137       }
138 
139    }
140 
141    @Singleton
142    public static class HexByteArrayAdapter implements ByteArrayAdapter {
143 
144       @Override
145       public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
146                throws JsonParseException {
147          return CryptoStreams.hex(json.getAsString());
148       }
149 
150       @Override
151       public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
152          return new JsonPrimitive(CryptoStreams.hex(src));
153       }
154    }
155 
156    @Singleton
157    public static class Iso8601DateAdapter implements DateAdapter {
158       private final DateService dateService;
159 
160       @Inject
161       private Iso8601DateAdapter(DateService dateService) {
162          this.dateService = dateService;
163       }
164 
165       public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
166          return new JsonPrimitive(dateService.iso8601DateFormat(src));
167       }
168 
169       public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
170                throws JsonParseException {
171          String toParse = json.getAsJsonPrimitive().getAsString();
172          try {
173             return dateService.iso8601DateParse(toParse);
174          } catch (RuntimeException e) {
175             return dateService.iso8601SecondsDateParse(toParse);
176          }
177       }
178 
179    }
180 
181    @Singleton
182    public static class SerializePropertiesDefaults implements JsonSerializer<Properties> {
183       private final Json json;
184       private final Type mapType = new TypeLiteral<Map<String, String>>() {
185       }.getRawType();
186 
187       @Inject
188       public SerializePropertiesDefaults(Json json) {
189          this.json = json;
190       }
191 
192       public JsonElement serialize(Properties src, Type typeOfSrc, JsonSerializationContext context) {
193          Builder<String, String> srcMap = ImmutableMap.<String, String> builder();
194          for (Enumeration<?> propNames = src.propertyNames(); propNames.hasMoreElements();) {
195             String propName = (String) propNames.nextElement();
196             srcMap.put(propName, src.getProperty(propName));
197          }
198          return new JsonLiteral(json.toJson(srcMap.build(), mapType));
199       }
200 
201    }
202 
203    @Singleton
204    public static class CDateAdapter implements DateAdapter {
205       private final DateService dateService;
206 
207       @Inject
208       private CDateAdapter(DateService dateService) {
209          this.dateService = dateService;
210       }
211 
212       public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
213          return new JsonPrimitive(dateService.cDateFormat(src));
214       }
215 
216       public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
217                throws JsonParseException {
218          String toParse = json.getAsJsonPrimitive().getAsString();
219          Date toReturn = dateService.cDateParse(toParse);
220          return toReturn;
221       }
222 
223    }
224 
225    @Singleton
226    public static class LongDateAdapter implements DateAdapter {
227 
228       public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
229          return new JsonPrimitive(src.getTime());
230       }
231 
232       public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
233                throws JsonParseException {
234          long toParse = json.getAsJsonPrimitive().getAsLong();
235          if (toParse == -1)
236             return null;
237          Date toReturn = new Date(toParse);
238          return toReturn;
239       }
240    }
241 
242    @Singleton
243    public static class JsonAdapterBindings {
244       private final Map<Type, Object> bindings = Maps.newHashMap();
245 
246       @com.google.inject.Inject(optional = true)
247       public void setBindings(Map<Type, Object> bindings) {
248          this.bindings.putAll(bindings);
249       }
250 
251       public Map<Type, Object> getBindings() {
252          return bindings;
253       }
254    }
255 
256    @Override
257    protected void configure() {
258       bind(Json.class).to(GsonWrapper.class);
259    }
260 }