protected Binary storeAndCheck(int contentIndex, Class<? extends Binary> valueClass)
      throws Exception {
    String content = CONTENT[contentIndex];
    String sha1 = CONTENT_HASHES[contentIndex];
    InputStream stream = new ByteArrayInputStream(content.getBytes());

    Stopwatch sw = new Stopwatch();
    sw.start();
    Binary binary = store.storeValue(stream, false);
    sw.stop();
    if (print) System.out.println("Time to store 18MB file: " + sw.getTotalDuration());

    if (valueClass != null) {
      assertThat(binary, is(instanceOf(valueClass)));
    }
    if (content.length() == 0) {
      assertThat(binary, is(instanceOf(EmptyBinaryValue.class)));
    } else if (content.length() < MIN_BINARY_SIZE) {
      assertThat(binary, is(instanceOf(InMemoryBinaryValue.class)));
    } else {
      assertThat(binary, is(instanceOf(StoredBinaryValue.class)));
    }
    assertThat(binary.getHexHash(), is(sha1));
    String binaryContent = IoUtil.read(binary.getStream());
    assertThat(binaryContent, is(content));
    return binary;
  }
  protected void storeAndCheckResource(
      String resourcePath, String expectedSha1, String desc, long numBytes) throws Exception {
    InputStream content = getClass().getClassLoader().getResourceAsStream(resourcePath);
    assertThat(content, is(notNullValue()));

    Stopwatch sw = new Stopwatch();
    sw.start();
    Binary binary = store.storeValue(content, false);
    sw.stop();
    if (print) System.out.println("Time to store " + desc + ": " + sw.getTotalDuration());

    if (numBytes == 0) {
      assertThat(binary, is(instanceOf(EmptyBinaryValue.class)));
    } else if (numBytes < MIN_BINARY_SIZE) {
      assertThat(binary, is(instanceOf(InMemoryBinaryValue.class)));
    } else {
      assertThat(binary, is(instanceOf(StoredBinaryValue.class)));
    }
    assertThat(binary.getHexHash(), is(expectedSha1));
    assertThat(binary.getSize(), is(numBytes));

    // Now try reading and comparing the two streams ...
    InputStream expected = getClass().getClassLoader().getResourceAsStream(resourcePath);
    InputStream actual = binary.getStream();
    byte[] buffer1 = new byte[1024];
    byte[] buffer2 = new byte[1024];
    int numRead = 0;
    while ((numRead = expected.read(buffer1)) == actual.read(buffer2)) {
      if (numRead == -1) break;
      for (int i = 0; i != numRead; ++i) {
        assertThat(buffer1[i], is(buffer2[i]));
      }
    }

    if (print) {
      // And try measuring how fast we can read the file ...
      sw = new Stopwatch();
      sw.start();
      while (-1 != actual.read(buffer2)) {}
      sw.stop();
      System.out.println("Time to read " + desc + ": " + sw.getTotalDuration());
    }
  }
  @Test
  public void multipleThreadsShouldReadTheSameFile() throws Exception {
    final String textBase = "The quick brown fox jumps over the lazy dog";
    StringBuilder builder = new StringBuilder();
    Random rand = new Random();
    while (builder.length() <= MIN_BINARY_SIZE) {
      builder.append(textBase.substring(0, rand.nextInt(textBase.length())));
    }
    final String text = builder.toString();
    final Binary storedValue = store.storeValue(new ByteArrayInputStream(text.getBytes()), false);
    ExecutorService executor = Executors.newFixedThreadPool(3);
    Callable<String> readingTask =
        new Callable<String>() {
          @Override
          public String call() throws Exception {
            File tempFile = File.createTempFile("test-binary-store", "bin");
            try {
              FileOutputStream fos = new FileOutputStream(tempFile);
              InputStream is = storedValue.getStream();
              byte[] buff = new byte[100];
              int available;
              while ((available = is.read(buff)) != -1) {
                fos.write(buff, 0, available);
              }
              fos.close();

              return IoUtil.read(tempFile);
            } finally {
              tempFile.delete();
            }
          }
        };
    List<Callable<String>> tasks = Arrays.asList(readingTask, readingTask, readingTask);
    List<Future<String>> futures = executor.invokeAll(tasks, 5, TimeUnit.SECONDS);
    for (Future<String> future : futures) {
      assertEquals(text, future.get());
    }
  }
  @Override
  public BinaryValue storeValue(InputStream stream) throws BinaryStoreException {
    // store into temporary file system store and get SHA-1
    BinaryValue temp = cache.storeValue(stream);
    try {
      // prepare new binary key based on SHA-1
      BinaryKey key = new BinaryKey(temp.getKey().toString());

      // check for duplicate content
      if (this.contentExists(key, ALIVE)) {
        return new StoredBinaryValue(this, key, temp.getSize());
      }

      // check unused content
      if (this.contentExists(key, UNUSED)) {
        session.execute("UPDATE modeshape.binary SET usage=1 WHERE cid='" + key + "';");
        return new StoredBinaryValue(this, key, temp.getSize());
      }

      // store content
      PreparedStatement query =
          session.prepare(
              "INSERT INTO modeshape.binary (cid, usage_time, payload, usage) VALUES ( ?,?,?,1 );");
      BoundStatement statement = new BoundStatement(query);
      session.execute(statement.bind(key.toString(), new Date(), buffer(stream)));
      return new StoredBinaryValue(this, key, temp.getSize());
    } catch (BinaryStoreException e) {
      throw e;
    } catch (IOException e) {
      throw new BinaryStoreException(e);
    } catch (RuntimeException e) {
      throw new BinaryStoreException(e);
    } finally {
      // remove content from temp store
      cache.markAsUnused(temp.getKey());
    }
  }