GsonDefinitionModelStore.java

  1. /*
  2.  * Copyright © 2017, Salesforce.com, Inc
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions are met:
  7.  *     * Redistributions of source code must retain the above copyright
  8.  *       notice, this list of conditions and the following disclaimer.
  9.  *     * Redistributions in binary form must reproduce the above copyright
  10.  *       notice, this list of conditions and the following disclaimer in the
  11.  *       documentation and/or other materials provided with the distribution.
  12.  *     * Neither the name of the <organization> nor the
  13.  *       names of its contributors may be used to endorse or promote products
  14.  *       derived from this software without specific prior written permission.
  15.  *
  16.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19.  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  20.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26.  */
  27. package com.salesforce.apt.graph.model.storage.classpath;

  28. import java.io.IOException;
  29. import java.io.InputStreamReader;
  30. import java.io.OutputStream;
  31. import java.io.OutputStreamWriter;
  32. import java.io.Reader;
  33. import java.nio.charset.StandardCharsets;
  34. import java.security.DigestInputStream;
  35. import java.security.DigestOutputStream;
  36. import java.security.MessageDigest;
  37. import java.security.NoSuchAlgorithmException;
  38. import java.util.ArrayList;
  39. import java.util.List;

  40. import com.google.gson.Gson;
  41. import com.google.gson.GsonBuilder;
  42. import com.salesforce.apt.graph.model.DefinitionModel;
  43. import com.salesforce.apt.graph.model.storage.DefinitionModelStore;
  44. import com.salesforce.apt.graph.model.storage.DefinitionOutputStreamProvider;
  45. import com.salesforce.apt.graph.model.storage.Resource;
  46. import com.salesforce.apt.graph.model.storage.ResourceLoader;

  47. public class GsonDefinitionModelStore implements DefinitionModelStore {
  48.  
  49.   private final ResourceLoader resourceLocator;
  50.  
  51.   private final DefinitionOutputStreamProvider definitionModelToStore;

  52.   private final Gson gson;
  53.  
  54.   protected Gson getGson() {
  55.     return gson;
  56.   }

  57.   protected DefinitionOutputStreamProvider getDefinitionOutputStreamProvider() {
  58.     return definitionModelToStore;
  59.   }

  60.   protected ResourceLoader getResourceLocator() {
  61.     return resourceLocator;
  62.   }
  63.  
  64.   public GsonDefinitionModelStore(ResourceLoader resourceLocator,
  65.       DefinitionOutputStreamProvider definitionModelToStore) {
  66.     gson = new GsonBuilder().setPrettyPrinting().create();
  67.     this.resourceLocator = resourceLocator;
  68.     this.definitionModelToStore = definitionModelToStore;
  69.   }
  70.  
  71.   /**
  72.    * Find definitions by name.
  73.    *
  74.    * @param name the name of the definition to find.
  75.    * @return A list of all definitions that happen to have the name (from multiple jars?)
  76.    */
  77.   @Override
  78.   public List<DefinitionModel> lookup(String name) {
  79.     List<DefinitionModel> output = new ArrayList<>();
  80.     for (Resource resource : resourceLocator.getEntries(name)) {    
  81.       try (DigestInputStream digestInputStream = new DigestInputStream(resource.getInputStream(), getSha256Digest());
  82.           Reader reader = new InputStreamReader(digestInputStream, StandardCharsets.UTF_8)) {
  83.         DefinitionModel definitionModel = gson.fromJson(reader, DefinitionModel.class);
  84.         definitionModel.setSourceLocation(resource.getLocation());
  85.         definitionModel.setSha256(bytesToHex(digestInputStream.getMessageDigest().digest()));
  86.         output.add(definitionModel);
  87.       } catch (IOException ex) {
  88.         return null;
  89.       }
  90.     }
  91.     return output;
  92.   }
  93.  
  94.   /**
  95.    * Hex encoded bytes from the input array. presented as a String
  96.    * @param bytes to convert
  97.    * @return String of hex values representing the byte array.
  98.    */
  99.   public static String bytesToHex(byte[] bytes) {
  100.     final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
  101.     char[] hexChars = new char[bytes.length * 2];
  102.     for (int j = 0; j < bytes.length; j++) {
  103.       int value = bytes[j] & 0xFF;
  104.       hexChars[j * 2] = hexArray[value >>> 4];
  105.       hexChars[j * 2 + 1] = hexArray[value & 0x0F];
  106.     }
  107.     return new String(hexChars);
  108.   }

  109.   @Override
  110.   public boolean store(DefinitionModel model) {
  111.     OutputStream stream = null;
  112.     try {
  113.       stream = definitionModelToStore.store(model);
  114.       DigestOutputStream digesterStream = new DigestOutputStream(stream, getSha256Digest());
  115.       try (OutputStreamWriter writer = new OutputStreamWriter(digesterStream, StandardCharsets.UTF_8)) {
  116.         gson.toJson(model, writer);
  117.       }
  118.       digesterStream.flush();
  119.       model.setSha256(bytesToHex(digesterStream.getMessageDigest().digest()));

  120.       //when in incremental mode, the underlying streams throw exceptions if closed multiple times.
  121.       try {
  122.         digesterStream.close();
  123.       } catch (IOException ioe) {
  124.         //prevent m2e not letting the underlying stream be closed multiple times from
  125.         //causing an IOException that would cause storage to appear to break.
  126.       }
  127.       try {
  128.         stream.close();
  129.       } catch (IOException ioe) {
  130.         //prevent m2e not letting the underlying stream be closed multiple times from
  131.         //causing an IOException that would cause storage to appear to break.
  132.       }
  133.       return true;
  134.     } catch (IOException ex) {
  135.       //indicate that the file could not be stored.
  136.       return false;
  137.     }
  138.   }
  139.  
  140.   /**
  141.    * Get the standard SHA-256 message digest.   Every implementation of MessageDigest must
  142.    * provide SHA-256 message digest
  143.    * @return SHA-256 message digest
  144.    */
  145.   protected MessageDigest getSha256Digest() {
  146.     try {
  147.       return MessageDigest.getInstance("SHA-256");
  148.     } catch (NoSuchAlgorithmException nsae) {
  149.       throw new IllegalStateException("Your jvm doesn't implement the default MessageDigesters... namely sha256.  Fail.");
  150.     }
  151.   }
  152. }