PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/core/infinit.e.data_model/src/com/ikanow/infinit/e/data_model/store/BaseDbPojo.java

https://github.com/IKANOW/Infinit.e
Java | 278 lines | 220 code | 13 blank | 45 comment | 19 complexity | a60917dfc01fa342842f8d09ee213e61 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*******************************************************************************
  2. * Copyright 2012 The Infinit.e Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. ******************************************************************************/
  16. package com.ikanow.infinit.e.data_model.store;
  17. import java.lang.reflect.ParameterizedType;
  18. import java.lang.reflect.Type;
  19. import java.util.Collection;
  20. import java.util.Date;
  21. import java.util.GregorianCalendar;
  22. import java.util.List;
  23. import java.util.SimpleTimeZone;
  24. import org.bson.types.ObjectId;
  25. import com.google.gson.GsonBuilder;
  26. import com.google.gson.JsonDeserializationContext;
  27. import com.google.gson.JsonDeserializer;
  28. import com.google.gson.JsonElement;
  29. import com.google.gson.JsonObject;
  30. import com.google.gson.JsonParseException;
  31. import com.google.gson.JsonSerializationContext;
  32. import com.google.gson.JsonSerializer;
  33. import com.google.gson.reflect.TypeToken;
  34. import com.ikanow.infinit.e.data_model.utils.ThreadSafeSimpleDateFormat;
  35. import com.mongodb.DBCursor;
  36. import com.mongodb.DBObject;
  37. public class BaseDbPojo {
  38. // Every Pojo should "override" this static function (stick the actual class in the 2x <>s) for readability
  39. static public <S> TypeToken<List<S>> listType() { return new TypeToken<List<S>>(){}; }
  40. // Override this function to perform custom serialization (see BasePojoApiMap)
  41. public GsonBuilder extendBuilder(GsonBuilder gp) {
  42. return extendBuilder_internal(gp);
  43. }
  44. // Allows API owner to enforce some custom serializations
  45. final public static GsonBuilder getDefaultBuilder() {
  46. return new GsonBuilder()
  47. .registerTypeAdapter(ObjectId.class, new ObjectIdDeserializer())
  48. .registerTypeAdapter(Date.class, new DateDeserializer())
  49. .registerTypeAdapter(Long.class, new LongDeserializer())
  50. .registerTypeAdapter(Integer.class, new IntegerDeserializer())
  51. .registerTypeAdapter(ObjectId.class, new ObjectIdSerializer())
  52. .registerTypeAdapter(Date.class, new DateSerializer());
  53. }
  54. private static GsonBuilder extendBuilder_internal(GsonBuilder gp) {
  55. return gp;
  56. }
  57. //________________________________________________________________________________________________
  58. // Won't normally override these (but can)
  59. // DB format conversion
  60. // For BaseDbPojos
  61. // 2 versions, 1 where you need dynamic mapping (eg runtime-specific)
  62. // (in both cases for single objects, you can use the nicer class<X> or
  63. // the nastier Google TypeToken, the latter has 2 advantages:
  64. // a. consisntency with list types, where you have to use the TypeToken
  65. // b. the class<X> doesn't work where X is generic)
  66. //
  67. // Note - the code is a bit unpleasant in places....
  68. /////////////////////////////////////////////////////////////////////////////////////
  69. // To the API JSON From a single Object
  70. public static<S extends BaseDbPojo> DBObject toDb(S s) {
  71. return toDb(s, null);
  72. }
  73. // (Nicer version of the static "toApi")
  74. public DBObject toDb() {
  75. return toDb(this);
  76. }
  77. public static<S extends BaseDbPojo> DBObject toDb(S s, BasePojoDbMap<S> dynamicMap) {
  78. GsonBuilder gb = s.extendBuilder(BaseDbPojo.getDefaultBuilder());
  79. if (null != dynamicMap) {
  80. gb = dynamicMap.extendBuilder(gb);
  81. }
  82. return (DBObject) MongoDbUtil.encode(gb.create().toJsonTree(s).getAsJsonObject());
  83. }
  84. /////////////////////////////////////////////////////////////////////////////////////
  85. // From the API JSON To a single Object
  86. public static<S extends BaseDbPojo> S fromDb(DBObject s, Class<S> type) {
  87. return fromDb(s, type, null);
  88. }
  89. public static<S extends BaseDbPojo> S fromDb(DBObject s, TypeToken<S> type) {
  90. return fromDb(s, type, null);
  91. }
  92. public static<S extends BaseDbPojo> S fromDb(DBObject s, Class<S> type, BasePojoDbMap<S> dynamicMap) {
  93. if (null == s) return null;
  94. // Create a new instance of the class in order to override it
  95. GsonBuilder gb = null;
  96. try {
  97. gb = type.newInstance().extendBuilder(BaseDbPojo.getDefaultBuilder());
  98. } catch (Exception e) {
  99. return null;
  100. }
  101. if (null != dynamicMap) {
  102. gb = dynamicMap.extendBuilder(gb);
  103. }
  104. return gb.create().fromJson(MongoDbUtil.encode(s), type);
  105. }
  106. @SuppressWarnings("unchecked")
  107. public static<S extends BaseDbPojo> S fromDb(DBObject s, TypeToken<S> type, BasePojoDbMap<S> dynamicMap) {
  108. if (null == s) return null;
  109. GsonBuilder gb = null;
  110. try {
  111. Class<S> clazz = (Class<S>)type.getType();
  112. gb = ((S)clazz.newInstance()).extendBuilder(BaseDbPojo.getDefaultBuilder());
  113. } catch (Exception e) {
  114. return null;
  115. }
  116. if (null != dynamicMap) {
  117. gb = dynamicMap.extendBuilder(gb);
  118. }
  119. return (S)gb.create().fromJson(MongoDbUtil.encode(s), type.getType());
  120. }
  121. /////////////////////////////////////////////////////////////////////////////////////
  122. // To the API JSON From a list of objects
  123. public static <S extends BaseDbPojo> DBObject listToDb(Collection<S> list, TypeToken<? extends Collection<S>> listType) {
  124. return listToDb(list, listType, null);
  125. }
  126. public static <S extends BaseDbPojo> DBObject listToDb(Collection<S> list, TypeToken<? extends Collection<S>> listType, BasePojoDbMap<S> dynamicMap) {
  127. GsonBuilder gb = null;
  128. try {
  129. if (!list.isEmpty()) {
  130. gb = list.iterator().next().extendBuilder(BaseDbPojo.getDefaultBuilder());
  131. }
  132. } catch (Exception e) {
  133. return null;
  134. }
  135. return (DBObject) MongoDbUtil.encodeArray(gb.create().toJsonTree(list, listType.getType()).getAsJsonArray());
  136. }
  137. /////////////////////////////////////////////////////////////////////////////////////
  138. // From the API JSON to a list of objects
  139. public static <S extends BaseDbPojo, L extends Collection<S>> L listFromDb(DBCursor bson, TypeToken<? extends L> listType) {
  140. return listFromDb(bson, listType, null);
  141. }
  142. @SuppressWarnings("unchecked")
  143. public static <S extends BaseDbPojo, L extends Collection<S>> L listFromDb(DBCursor bson, TypeToken<? extends L> listType, BasePojoDbMap<S> dynamicMap) {
  144. GsonBuilder gb = null;
  145. try {
  146. Class<S> clazz = (Class<S>)((ParameterizedType)listType.getType()).getActualTypeArguments()[0];
  147. // (know this works because of construction of listType)
  148. gb = (clazz.newInstance()).extendBuilder(BaseDbPojo.getDefaultBuilder());
  149. } catch (Exception e) {
  150. return null;
  151. }
  152. if (null != dynamicMap) {
  153. gb = dynamicMap.extendBuilder(gb);
  154. }
  155. return (L)gb.create().fromJson(MongoDbUtil.encode(bson), listType.getType());
  156. }
  157. //___________________________________________________________________
  158. // Default MongoDB serialization/deserialization rules:
  159. // 1. Object Ids
  160. protected static class ObjectIdSerializer implements JsonSerializer<ObjectId>
  161. {
  162. @Override
  163. public JsonElement serialize(ObjectId id, Type typeOfT, JsonSerializationContext context)
  164. {
  165. JsonObject jo = new JsonObject();
  166. jo.addProperty("$oid", id.toStringMongod());
  167. return jo;
  168. }
  169. }
  170. protected static class ObjectIdDeserializer implements JsonDeserializer<ObjectId>
  171. {
  172. @Override
  173. public ObjectId deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
  174. {
  175. if (json.isJsonPrimitive()) {
  176. return new ObjectId(json.getAsString());
  177. }
  178. else {
  179. try {
  180. return new ObjectId(json.getAsJsonObject().get("$oid").getAsString());
  181. }
  182. catch (Exception e) {
  183. return null;
  184. }
  185. }
  186. }
  187. }
  188. // Dates:
  189. protected static class DateDeserializer implements JsonDeserializer<Date>
  190. {
  191. private static ThreadSafeSimpleDateFormat _format = new ThreadSafeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
  192. private static ThreadSafeSimpleDateFormat _format2 = new ThreadSafeSimpleDateFormat("MMM d, yyyy hh:mm:ss a");
  193. @Override
  194. public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
  195. throws JsonParseException
  196. {
  197. Date d = null;
  198. if (json.isJsonPrimitive()) {
  199. try {
  200. d = _format2.parse(json.getAsString());
  201. }
  202. catch (Exception e) {
  203. try {
  204. d = _format.parse(json.getAsString());
  205. }
  206. catch (Exception e2) {
  207. d = null;
  208. }
  209. }
  210. }
  211. else {
  212. try {
  213. d = _format.parse(json.getAsJsonObject().get("$date").getAsString());
  214. }
  215. catch (Exception e) {
  216. d = null;
  217. }
  218. }
  219. return d;
  220. }
  221. }
  222. protected static class DateSerializer implements JsonSerializer<Date>
  223. {
  224. private static ThreadSafeSimpleDateFormat _format = null;
  225. @Override
  226. public JsonElement serialize(Date date, Type typeOfT, JsonSerializationContext context)
  227. {
  228. Date d = (Date)date;
  229. if (null == _format) {
  230. _format = new ThreadSafeSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
  231. _format.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "UTC")));
  232. }
  233. JsonObject jo = new JsonObject();
  234. jo.addProperty("$date", _format.format(d));
  235. return jo;
  236. }
  237. }
  238. // OK MongoDB is very keen on turning Long/Integer into doubles (eg from the console) - these converts them back
  239. protected static class LongDeserializer implements JsonDeserializer<Long>
  240. {
  241. @Override
  242. public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
  243. {
  244. try {
  245. return json.getAsLong();
  246. }
  247. catch (Exception e) {
  248. return (long)json.getAsNumber().doubleValue();
  249. }
  250. }
  251. }
  252. protected static class IntegerDeserializer implements JsonDeserializer<Integer>
  253. {
  254. @Override
  255. public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
  256. {
  257. try {
  258. return json.getAsInt();
  259. }
  260. catch (Exception e) {
  261. return (int)json.getAsNumber().doubleValue();
  262. }
  263. }
  264. }
  265. }