Coverage Report - org.apache.fulcrum.json.gson.GSONBuilderService
 
Classes in this File Line Coverage Branch Coverage Complexity
GSONBuilderService
57%
45/78
35%
7/20
1,676
GSONBuilderService$1
0%
0/1
N/A
1,676
GSONBuilderService$2
100%
7/7
N/A
1,676
GSONBuilderService$2$1
100%
2/2
N/A
1,676
GSONBuilderService$3
100%
9/9
100%
6/6
1,676
GSONBuilderService$4
92%
13/14
58%
7/12
1,676
 
 1  
     package org.apache.fulcrum.json.gson;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.lang.reflect.Type;
 23  
 import java.text.DateFormat;
 24  
 import java.text.SimpleDateFormat;
 25  
 import java.util.Collection;
 26  
 import java.util.Collections;
 27  
 import java.util.Date;
 28  
 import java.util.EnumSet;
 29  
 import java.util.Enumeration;
 30  
 import java.util.HashSet;
 31  
 import java.util.Hashtable;
 32  
 import java.util.Set;
 33  
 import java.util.concurrent.Callable;
 34  
 
 35  
 import org.apache.avalon.framework.activity.Initializable;
 36  
 import org.apache.avalon.framework.configuration.Configurable;
 37  
 import org.apache.avalon.framework.configuration.Configuration;
 38  
 import org.apache.avalon.framework.configuration.ConfigurationException;
 39  
 import org.apache.avalon.framework.logger.AbstractLogEnabled;
 40  
 import org.apache.fulcrum.json.JsonService;
 41  
 
 42  
 import com.google.gson.ExclusionStrategy;
 43  
 import com.google.gson.FieldAttributes;
 44  
 import com.google.gson.Gson;
 45  
 import com.google.gson.GsonBuilder;
 46  
 import com.google.gson.reflect.TypeToken;
 47  
 import com.jayway.jsonpath.Option;
 48  
 import com.jayway.jsonpath.spi.json.GsonJsonProvider;
 49  
 import com.jayway.jsonpath.spi.json.JsonProvider;
 50  
 import com.jayway.jsonpath.spi.mapper.GsonMappingProvider;
 51  
 import com.jayway.jsonpath.spi.mapper.MappingProvider;
 52  
 
 53  
 /**
 54  
  * 
 55  
  * By default multiple serialization of the same object in a single thread is
 56  
  * not support (e.g adapter + default for the same bean / object).
 57  
  * 
 58  
  * 
 59  
  * @author gk
 60  
  * @version $Id: GSONBuilderService.java 1800593 2017-07-03 06:36:53Z gk $
 61  
  * 
 62  
  */
 63  40
 public class GSONBuilderService extends AbstractLogEnabled implements
 64  
         JsonService, Initializable, Configurable {
 65  
 
 66  
     private static final String GLOBAL_ADAPTERS = "globalAdapters";
 67  
 
 68  
     private static final String DATE_FORMAT = "dateFormat";
 69  
     
 70  
     private static final String USEJSONPATH = "useJsonPath";
 71  
 
 72  
     private String dateFormat;
 73  
 
 74  23
     private Hashtable<String, String> adapters = null;
 75  
 
 76  23
     private boolean useJsonPath = false;
 77  
     
 78  
     GsonBuilder gson;
 79  
 
 80  
     @Override
 81  
     public String ser(Object src) throws Exception {
 82  17
         getLogger().debug("ser" + src);
 83  17
         return gson.create().toJson(src);
 84  
     }
 85  
 
 86  
     @Override
 87  
     public <T> String ser(Object src, Class<T> type) throws Exception {
 88  0
         getLogger().debug("ser::" + src + " with type" + type);
 89  
 
 90  0
         Type collectionType = new TypeToken<T>() {
 91  
         }.getType();
 92  0
         return gson.create().toJson(src, collectionType);
 93  
     }
 94  
 
 95  
     @Override
 96  
     public <T> T deSer(String json, Class<T> type) throws Exception {
 97  
         // TODO Auto-generated method stub
 98  2
         getLogger().debug("deser:" + json);
 99  2
         return gson.create().fromJson(json, type);
 100  
     }
 101  
     
 102  
     @Override
 103  
     public <T> Collection<T> deSerCollection(String json, Object collectionType,
 104  
             Class<T> elementType) throws Exception {
 105  1
         getLogger().debug("deser:" + json);
 106  1
         getLogger().debug("collectionType:" + collectionType);
 107  1
         return  gson.create().fromJson(json, (Type)collectionType);
 108  
     }
 109  
 
 110  
     @Override
 111  
     public <T> String serializeOnlyFilter(Object src, String... filterAttr)
 112  
             throws Exception {
 113  1
         return  gson
 114  
                 .addSerializationExclusionStrategy(
 115  
                         include(null,filterAttr)).create().toJson(src);
 116  
     }
 117  
 
 118  
     @Override
 119  
     public <T> String serializeOnlyFilter(Object src, Boolean notused,
 120  
             String... filterAttr) throws Exception {
 121  0
         return  gson
 122  
                 .addSerializationExclusionStrategy(
 123  
                         include(null,filterAttr)).create().toJson(src);
 124  
     }
 125  
 
 126  
     @Override
 127  
     public <T> String serializeOnlyFilter(Object src, Class<T> filterClass,
 128  
             String... filterAttr) throws Exception {
 129  1
         return  gson
 130  
         .addSerializationExclusionStrategy(
 131  
                 include(filterClass, filterAttr)).create().toJson(src);
 132  
     }
 133  
     
 134  
     @Override
 135  
     public <T> String serializeOnlyFilter(Object arg0, Class<T> arg1,
 136  
             Boolean arg2, String... arg3) throws Exception {
 137  0
         throw new Exception("Not yet implemented!");
 138  
     }
 139  
 
 140  
     /**
 141  
      * registering an adapter 
 142  
      * 
 143  
      * @see GsonBuilder#registerTypeAdapter(Type, Object)
 144  
      */
 145  
     @Override
 146  
     public JsonService addAdapter(String name, Class target, Object adapter)
 147  
             throws Exception {
 148  2
         gson.registerTypeAdapter(target, adapter);
 149  2
         return this;
 150  
     }
 151  
 
 152  
     /**
 153  
      * registering an adapter. Unregistering could be only done by reinitialize {@link GsonBuilder} 
 154  
      * using @link {@link GSONBuilderService#initialize()}, although a new Adapter with the same target overwrites the previously defined.
 155  
      * 
 156  
      * @see GsonBuilder#registerTypeAdapter(Type, Object)
 157  
      */
 158  
     @Override
 159  
     public JsonService addAdapter(String name, Class target, Class adapter)
 160  
             throws Exception {
 161  3
         gson.registerTypeAdapter(target, adapter.getConstructor().newInstance());
 162  3
         return null;
 163  
     }
 164  
 
 165  
     @Override
 166  
     public <T> String serializeAllExceptFilter(Object src,
 167  
             Class<T> filterClass, String... filterAttr) throws Exception {
 168  3
         return gson
 169  
                 .addSerializationExclusionStrategy(
 170  
                         exclude(filterClass, filterAttr)).create().toJson(src);
 171  
     }
 172  
     
 173  
     @Override
 174  
     public <T> String serializeAllExceptFilter(Object src, Class<T> filterClass,
 175  
             Boolean clearCache, String... filterAttr) throws Exception {
 176  0
         throw new Exception("Not yet implemented!");
 177  
     }
 178  
     
 179  
     @Override
 180  
     public <T> String serializeAllExceptFilter(Object src, String... filterAttr)
 181  
             throws Exception {
 182  2
         return gson
 183  
                 .addSerializationExclusionStrategy(
 184  
                         exclude(null, filterAttr)).create().toJson(src);
 185  
     }
 186  
 
 187  
     @Override
 188  
     public <T> String serializeAllExceptFilter(Object src, Boolean notused,
 189  
             String... filterAttr) throws Exception {
 190  0
         return gson
 191  
                 .addSerializationExclusionStrategy(
 192  
                         exclude(null, filterAttr)).create().toJson(src);
 193  
     }
 194  
     
 195  
     @Override
 196  
     public String ser(Object src, Boolean refreshCache) throws Exception {
 197  0
         throw new Exception("Not implemented!");
 198  
     }
 199  
 
 200  
     @Override
 201  
     public <T> String ser(Object src, Class<T> type, Boolean refreshCache)
 202  
             throws Exception {
 203  0
         throw new Exception("Not implemented!");
 204  
     }
 205  
 
 206  
     public JsonService registerTypeAdapter(Object serdeser, Type type) {
 207  0
         gson.registerTypeAdapter(type, serdeser);
 208  0
         return this;
 209  
     }
 210  
     
 211  
     /**
 212  
      * Alternative method to calling {@link #gson#registerTypeAdapter(Object, Type)}.
 213  
      * Note: Always use either this direct format call or the other adapter register call,
 214  
      * otherwise inconsistencies may occur!
 215  
      * 
 216  
      * @param dformat
 217  
      */
 218  
     public void setDateFormat(final String dfStr) {
 219  0
         gson.setDateFormat(dfStr);
 220  0
     }
 221  
 
 222  
     @Override
 223  
     public void setDateFormat(final DateFormat df) {
 224  26
         DateTypeAdapter dateTypeAdapter = new DateTypeAdapter();
 225  26
         dateTypeAdapter.setCustomDateFormat(df);
 226  26
         gson.registerTypeAdapter(Date.class,dateTypeAdapter);
 227  26
     }
 228  
 
 229  
     public void getJsonService() throws InstantiationException {
 230  
         // gson.registerTypeAdapter(Date.class, ser).
 231  
         // addSerializationExclusionStrategy( exclude(ObjectKey.class) ).
 232  
         // addSerializationExclusionStrategy( exclude(ComboKey.class) );
 233  
         // return gson.create().toJson( src );
 234  0
     }
 235  
 
 236  
     @Override
 237  
     public void configure(Configuration conf) throws ConfigurationException {
 238  
 
 239  23
         getLogger().debug("conf.getName()" + conf.getName());
 240  23
         final Configuration configuredDateFormat = conf.getChild(DATE_FORMAT,
 241  
                 false);
 242  23
         if (configuredDateFormat != null) {
 243  23
             this.dateFormat = configuredDateFormat.getValue();// DEFAULTDATEFORMAT);
 244  
         }
 245  23
         final Configuration configuredAdapters = conf.getChild(GLOBAL_ADAPTERS,
 246  
                 true);
 247  23
         if (configuredAdapters != null) {
 248  23
             Configuration[] nameVal = configuredAdapters.getChildren();
 249  23
             for (int i = 0; i < nameVal.length; i++) {
 250  0
                 String key = nameVal[i].getName();
 251  0
                 getLogger().debug("configured key: " + key);
 252  0
                 if (key.equals("adapter")) {
 253  0
                     String forClass = nameVal[i].getAttribute("forClass");
 254  0
                     this.adapters = new Hashtable<String, String>();
 255  0
                     this.adapters.put(forClass, nameVal[i].getValue());
 256  
                 }
 257  
             }
 258  
         }
 259  
         // TODO provide configurable Type Adapters
 260  23
         final Configuration configuredjsonPath = conf.getChild(
 261  
                 USEJSONPATH, false);
 262  23
         if (configuredjsonPath != null) {
 263  23
             this.useJsonPath  = configuredjsonPath.getValueAsBoolean();
 264  
         }
 265  23
     }
 266  
 
 267  
     @Override
 268  
     public void initialize() throws Exception {
 269  24
         gson = new GsonBuilder();
 270  24
         getLogger().debug("initialized: gson:" + gson);
 271  24
         if (dateFormat != null) {
 272  24
             getLogger().info("setting date format to: " + dateFormat);
 273  24
             setDateFormat(new SimpleDateFormat(dateFormat));
 274  
             //setDateFormat(dateFormat);
 275  
         }
 276  
 
 277  24
         if (adapters != null) {
 278  0
             Enumeration<String> enumKey = adapters.keys();
 279  0
             while (enumKey.hasMoreElements()) {
 280  0
                 String forClass = enumKey.nextElement();
 281  0
                 String avClass = adapters.get(forClass);
 282  0
                 if (avClass != null) {
 283  
                     try {
 284  0
                         getLogger().debug(
 285  
                                 "initializing: adapters " + avClass
 286  
                                         + " forClass:" + forClass);
 287  0
                         Class adapterForClass = Class.forName(forClass);
 288  0
                         Class adapterClass = Class.forName(avClass);
 289  0
                         addAdapter("Test Adapter", adapterForClass,
 290  
                                 adapterClass);
 291  
 
 292  0
                     } catch (Exception e) {
 293  0
                         throw new Exception(
 294  
                                 "JsonMapperService: Error instantiating "
 295  
                                         + avClass + " for " + forClass);
 296  0
                     }
 297  
                 }
 298  0
             }
 299  
         }
 300  
         
 301  24
         if (useJsonPath) {
 302  
             // set it before runtime
 303  24
             com.jayway.jsonpath.Configuration.setDefaults(new com.jayway.jsonpath.Configuration.Defaults() {
 304  
                 
 305  24
                 private Callable<Gson> gsonFuture = new Callable<Gson>() {
 306  
                     @Override
 307  
                     public Gson call() {
 308  4
                         return GSONBuilderService.this.gson.create();
 309  
                     }
 310  
                 };
 311  
 
 312  24
                 private final JsonProvider jsonProvider = new GsonJsonProvider(GSONBuilderService.this.gson.create());
 313  24
                 private final MappingProvider mappingProvider = new GsonMappingProvider(gsonFuture);
 314  
 
 315  
                 @Override
 316  
                 public JsonProvider jsonProvider() {
 317  5
                     return jsonProvider;
 318  
                 }
 319  
 
 320  
                 @Override
 321  
                 public MappingProvider mappingProvider() {
 322  5
                     return mappingProvider;
 323  
                 }
 324  
 
 325  
                 @Override
 326  
                 public Set<Option> options() {
 327  5
                     return EnumSet.noneOf(Option.class);
 328  
                 }
 329  
             });
 330  
         }
 331  24
     }
 332  
 
 333  
     /**
 334  
      * Simple Exclusion strategy to filter class or fields used by this service
 335  
      * for serialization (not yet deserialization).
 336  
      * 
 337  
      * @param clazz
 338  
      *            The class to be filtered out.
 339  
      * @param filterAttrs
 340  
      *            The fieldnames to be filtered as string
 341  
      * @return the strategy applied by GSON
 342  
      */
 343  
     private ExclusionStrategy exclude(Class clazz, String... filterAttrs) {
 344  10
         return new ExclusionStrategy() {
 345  
 
 346  
             public Class<?> excludedThisClass;
 347  
             public HashSet<String> excludedAttributes;
 348  
 
 349  
             private ExclusionStrategy init(Class<?> excludedThisClass,
 350  
                     String... filterAttrs) {
 351  5
                 this.excludedThisClass = excludedThisClass;
 352  5
                 if (filterAttrs != null) {
 353  3
                     this.excludedAttributes = new HashSet<String>(
 354  
                             filterAttrs.length);
 355  3
                     Collections.addAll(this.excludedAttributes, filterAttrs);
 356  
                 } else
 357  2
                     this.excludedAttributes = new HashSet<String>();
 358  
 
 359  5
                 return this;
 360  
             }
 361  
 
 362  
             @Override
 363  
             public boolean shouldSkipClass(Class<?> clazz) {
 364  30
                 return (excludedThisClass != null) ? excludedThisClass
 365  
                         .equals(clazz) : false;
 366  
             }
 367  
 
 368  
             @Override
 369  
             public boolean shouldSkipField(FieldAttributes paramFieldAttributes) {
 370  
                 // return paramFieldAttributes.getDeclaringClass() ==
 371  
                 // excludedThisClass &&
 372  
                 // excludesAttributes.contains(paramFieldAttributes.getName());
 373  10
                 return !excludedAttributes.isEmpty() ? this.excludedAttributes
 374  
                         .contains(paramFieldAttributes.getName()) : false;
 375  
             }
 376  
         }.init(clazz, filterAttrs);
 377  
     }
 378  
     
 379  
     private ExclusionStrategy include(Class clazz, String... filterAttrs) {
 380  4
         return new ExclusionStrategy() {
 381  
 
 382  
             private Class<?> includeThisClass;
 383  
             private HashSet<String> includedAttributes;
 384  
 
 385  
             private ExclusionStrategy init(Class<?> includeThisClass,
 386  
                     String... filterAttrs) {
 387  2
                 this.includeThisClass = includeThisClass;
 388  2
                 if (filterAttrs != null) {
 389  2
                     this.includedAttributes = new HashSet<String>(
 390  
                             filterAttrs.length);
 391  2
                     getLogger().debug(" ... adding includedAttributes:" + filterAttrs.length);
 392  2
                     Collections.addAll(this.includedAttributes, filterAttrs);
 393  2
                     for (String includedAttribute : includedAttributes) {
 394  2
                         getLogger().debug("includedAttribute:" +includedAttribute);
 395  2
                     }
 396  
                 } else
 397  0
                     this.includedAttributes = new HashSet<String>();
 398  
 
 399  2
                 return this;
 400  
             }
 401  
 
 402  
             /**
 403  
              * skip is current class is not equal provided class
 404  
              */
 405  
             @Override
 406  
             public boolean shouldSkipClass(Class<?> clazz) {
 407  13
                 getLogger().debug(includeThisClass+ ": comparing include class:" + clazz);
 408  13
                 return includeThisClass != null ? !includeThisClass
 409  
                         .equals(clazz) : false;
 410  
             }
 411  
 
 412  
             /**
 413  
              * skip if current field attribute is not included are skip else
 414  
              */
 415  
             @Override
 416  
             public boolean shouldSkipField(FieldAttributes paramFieldAttributes) { 
 417  6
                 return !includedAttributes.isEmpty() ? !this.includedAttributes
 418  
                         .contains(paramFieldAttributes.getName()) : true;        
 419  
 
 420  
             }
 421  
         }.init(clazz, filterAttrs);
 422  
     }
 423  
 
 424  
 }