public static <T extends JsonTemplate> T readJsonFromFileWithLock(
      Path jsonFile, Class<T> templateClass) throws IOException {
    // channel is closed by inputStream.close()
    FileChannel channel = FileChannel.open(jsonFile, StandardOpenOption.READ);
    channel.lock(0, Long.MAX_VALUE, true); // shared lock, released by channel close
    try (InputStream inputStream = Channels.newInputStream(channel)) {
      return objectMapper.readValue(inputStream, templateClass);
    }
  }
@Test
  public void testReadJsonWithLock() throws IOException, URISyntaxException, DigestException {
    Path jsonFile = Paths.get(Resources.getResource("core/json/basic.json").toURI());

    // Deserializes into a metadata JSON object.
    TestJson testJson = JsonTemplateMapper.readJsonFromFileWithLock(jsonFile, TestJson.class);

    MatcherAssert.assertThat(testJson.number, CoreMatchers.is(54));
    MatcherAssert.assertThat(testJson.text, CoreMatchers.is("crepecake"));
    MatcherAssert.assertThat(
        testJson.digest,
        CoreMatchers.is(
            DescriptorDigest.fromDigest(
                "sha256:8c662931926fa990b41da3c9f42663a537ccd498130030f9149173a0493832ad")));
    MatcherAssert.assertThat(
        testJson.innerObject, CoreMatchers.instanceOf(TestJson.InnerObject.class));
    MatcherAssert.assertThat(testJson.innerObject.number, CoreMatchers.is(23));
    MatcherAssert.assertThat(
        testJson.innerObject.texts, CoreMatchers.is(Arrays.asList("first text", "second text")));
    MatcherAssert.assertThat(
        testJson.innerObject.digests,
        CoreMatchers.is(
            Arrays.asList(
                DescriptorDigest.fromDigest(
                    "sha256:91e0cae00b86c289b33fee303a807ae72dd9f0315c16b74e6ab0cdbe9d996c10"),
                DescriptorDigest.fromHash(
                    "4945ba5011739b0b98c4a41afe224e417f47c7c99b2ce76830999c9a0861b236"))));
    // ignore testJson.list
  }