@Transactional(retryOn = {ONeedRetryException.class, ORecordDuplicatedException.class})
  protected void doPutContent(
      final String path, final Supplier<InputStream> streamSupplier, final Payload payload)
      throws IOException {
    StorageTx tx = UnitOfWork.currentTransaction();

    final Bucket bucket = tx.getBucket();
    Component component = getComponent(tx, path, bucket);
    Asset asset;
    if (component == null) {
      // CREATE
      component =
          tx.createComponent(bucket, getRepository().getFormat())
              .group(getGroup(path))
              .name(getName(path));

      // Set attributes map to contain "raw" format-specific metadata (in this case, path)
      component.formatAttributes().set(P_PATH, path);
      tx.saveComponent(component);

      asset = tx.createAsset(bucket, component);
      asset.name(component.name());
    } else {
      // UPDATE
      asset = tx.firstAsset(component);
    }

    AttributesMap contentAttributes = null;
    if (payload instanceof Content) {
      contentAttributes = ((Content) payload).getAttributes();
    }
    Content.applyToAsset(asset, Content.maintainLastModified(asset, contentAttributes));
    tx.setBlob(asset, path, streamSupplier.get(), hashAlgorithms, null, payload.getContentType());
    tx.saveAsset(asset);
  }
 @Test
 public void nothingToExtract() {
   NestedAttributesMap attributes =
       new NestedAttributesMap(P_ATTRIBUTES, Maps.<String, Object>newHashMap());
   Asset asset = mock(Asset.class);
   when(asset.attributes()).thenReturn(attributes);
   CacheInfo cacheInfo = CacheInfo.extractFromAsset(asset);
   assertThat(cacheInfo, nullValue());
 }
 @Test
 public void cacheTokenOnlyExtract() {
   final String cacheToken = "foo-bar";
   NestedAttributesMap attributes =
       new NestedAttributesMap(P_ATTRIBUTES, Maps.<String, Object>newHashMap());
   attributes.child(CacheInfo.CACHE).set(CacheInfo.CACHE_TOKEN, cacheToken);
   Asset asset = mock(Asset.class);
   when(asset.attributes()).thenReturn(attributes);
   CacheInfo cacheInfo = CacheInfo.extractFromAsset(asset);
   assertThat(cacheInfo, nullValue());
 }
 @Test
 public void lastVerifiedOnlyExtract() {
   final DateTime now = DateTime.now();
   NestedAttributesMap attributes =
       new NestedAttributesMap(P_ATTRIBUTES, Maps.<String, Object>newHashMap());
   attributes.child(CacheInfo.CACHE).set(CacheInfo.LAST_VERIFIED, now.toDate());
   Asset asset = mock(Asset.class);
   when(asset.attributes()).thenReturn(attributes);
   CacheInfo cacheInfo = CacheInfo.extractFromAsset(asset);
   assertThat(cacheInfo, notNullValue());
   assertThat(cacheInfo.getLastVerified(), equalTo(now));
 }
  @Nullable
  @Override
  @Transactional(retryOn = IllegalStateException.class)
  public Content get(final String path) {
    StorageTx tx = UnitOfWork.currentTransaction();

    final Component component = getComponent(tx, path, tx.getBucket());
    if (component == null) {
      return null;
    }

    final Asset asset = tx.firstAsset(component);
    final Blob blob = tx.requireBlob(asset.requireBlobRef());

    return marshall(asset, blob);
  }
 @Test
 public void lastVerifiedAndCacheTokenApply() {
   final DateTime now = DateTime.now();
   final String cacheToken = "foo-bar";
   NestedAttributesMap attributes =
       new NestedAttributesMap(P_ATTRIBUTES, Maps.<String, Object>newHashMap());
   Asset asset = mock(Asset.class);
   when(asset.attributes()).thenReturn(attributes);
   CacheInfo cacheInfo = new CacheInfo(now, cacheToken);
   CacheInfo.applyToAsset(asset, cacheInfo);
   assertThat(
       asset.attributes().child(CacheInfo.CACHE).get(CacheInfo.LAST_VERIFIED, Date.class),
       equalTo(now.toDate()));
   assertThat(
       asset.attributes().child(CacheInfo.CACHE).get(CacheInfo.CACHE_TOKEN, String.class),
       equalTo(cacheToken));
 }
 private Content marshall(final Asset asset, final Blob blob) {
   final Content content = new Content(new BlobPayload(blob, asset.requireContentType()));
   Content.extractFromAsset(asset, hashAlgorithms, content.getAttributes());
   return content;
 }