Package org.sonatype.nexus.component.services.internal.storage

Source Code of org.sonatype.nexus.component.services.internal.storage.ComponentStoreImplSupport

/*
* Sonatype Nexus (TM) Open Source Version
* Copyright (c) 2007-2014 Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
* which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
* of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
package org.sonatype.nexus.component.services.internal.storage;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;
import javax.inject.Provider;

import org.sonatype.nexus.blobstore.api.Blob;
import org.sonatype.nexus.blobstore.api.BlobId;
import org.sonatype.nexus.blobstore.api.BlobStore;
import org.sonatype.nexus.component.model.Asset;
import org.sonatype.nexus.component.model.Component;
import org.sonatype.nexus.component.model.ComponentEnvelope;
import org.sonatype.nexus.component.model.Entity;
import org.sonatype.nexus.component.model.EntityId;
import org.sonatype.nexus.component.services.adapter.AssetAdapter;
import org.sonatype.nexus.component.services.adapter.ComponentAdapter;
import org.sonatype.nexus.component.services.adapter.EntityAdapter;
import org.sonatype.nexus.component.services.adapter.EntityAdapterRegistry;
import org.sonatype.nexus.component.services.id.EntityIdFactory;
import org.sonatype.nexus.component.services.internal.query.OrientQueryBuilder;
import org.sonatype.nexus.component.services.query.MetadataQuery;
import org.sonatype.nexus.component.services.query.MetadataQueryRestriction;
import org.sonatype.nexus.component.services.storage.ComponentStore;
import org.sonatype.nexus.events.EventSubscriber;
import org.sonatype.nexus.orient.DatabaseInstance;
import org.sonatype.nexus.orient.OClassNameBuilder;
import org.sonatype.nexus.proxy.events.NexusInitializedEvent;
import org.sonatype.nexus.proxy.events.NexusStoppingEvent;
import org.sonatype.sisu.goodies.lifecycle.LifecycleSupport;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.eventbus.Subscribe;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;
import org.joda.time.DateTime;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.sonatype.nexus.component.services.adapter.AssetAdapter.P_BLOB_REFS;
import static org.sonatype.nexus.component.services.adapter.AssetAdapter.P_COMPONENT;
import static org.sonatype.nexus.component.services.adapter.AssetAdapter.P_CONTENT_TYPE;
import static org.sonatype.nexus.component.services.adapter.AssetAdapter.P_FIRST_CREATED;
import static org.sonatype.nexus.component.services.adapter.AssetAdapter.P_PATH;
import static org.sonatype.nexus.component.services.adapter.ComponentAdapter.P_ASSETS;
import static org.sonatype.nexus.component.services.adapter.EntityAdapter.P_ID;

/**
* Supporting base class for {@link ComponentStoreImpl}.
*
* @since 3.0
*/
abstract class ComponentStoreImplSupport
    extends LifecycleSupport
    implements EventSubscriber
{
  protected final EntityAdapterRegistry entityAdapterRegistry;

  // TODO: Determine how to get this when BlobStoreManager (or whatever we call it) is implemented
  private static final String localBlobStoreId = "someBlobStoreId"; // hardcoded for now..

  private final Provider<DatabaseInstance> databaseInstanceProvider;

  // TODO: Delegate to something else instead of using a BlobStore directly here...
  private final BlobStore blobStore;

  private final EntityIdFactory entityIdFactory;

  public ComponentStoreImplSupport(Provider<DatabaseInstance> databaseInstanceProvider,
                                   BlobStore blobStore,
                                   EntityAdapterRegistry entityAdapterRegistry,
                                   EntityIdFactory entityIdFactory) {
    this.databaseInstanceProvider = checkNotNull(databaseInstanceProvider);
    this.blobStore = checkNotNull(blobStore);
    this.entityAdapterRegistry = checkNotNull(entityAdapterRegistry);
    this.entityIdFactory = checkNotNull(entityIdFactory);
  }

  @Subscribe
  public void on(NexusInitializedEvent event) throws Exception {
    start();
  }

  @Subscribe
  public void on(NexusStoppingEvent event) throws Exception {
    stop();
  }

  @Override
  protected void doStart() throws Exception {
    blobStore.start();
  }

  @Override
  protected void doStop() throws Exception {
    blobStore.stop();
  }

  /**
   * Executes the given function using a database connection.
   */
  protected <T> T execute(Function<ODatabaseDocumentTx, T> function) {
    return doExecute(function, false, null);
  }

  /**
   * Executes the given function using a database connection, with optional blob deletion on success and/or failure.
   */
  protected <T> T execute(Function<ODatabaseDocumentTx, T> function, BlobTx blobTx) {
    return doExecute(function, false, blobTx);
  }

  /**
   * Executes the given function using a database connection in a transaction, with optional blob deletion
   * on success and/or failure.
   */
  protected <T> T executeInTransaction(Function<ODatabaseDocumentTx, T> function, BlobTx blobTx) {
    return doExecute(function, true, blobTx);
  }

  /**
   * Gets a new {@link BlobTx} for tracking blob creation and deletion.
   */
  protected BlobTx beginBlobTx() {
    return new BlobTx(blobStore);
  }

  /**
   * @see ComponentStore#countAssets(Class, MetadataQueryRestriction)
   * @see ComponentStore#countComponents(Class, MetadataQueryRestriction)
   */
  protected <T extends Entity> long count(final Class<T> entityClass,
                                          @Nullable final MetadataQueryRestriction restriction) {
    checkNotNull(entityClass);
    return execute(new Function<ODatabaseDocumentTx, Long>()
    {
      @Override
      public Long apply(final ODatabaseDocumentTx db) {
        OrientQueryBuilder queryBuilder = orientQueryBuilder(db, entityClass, queryWith(restriction));
        String countQuery = queryBuilder.buildQuery(entityClass, true);
        return executeCount(db, countQuery, queryBuilder.getParameters());
      }
    });
  }

  /**
   * @see ComponentStore#findAssets(Class, MetadataQuery)
   */
  protected <A extends Asset> List<A> findAssets(final ODatabaseDocumentTx db,
                                                 final Class<A> assetClass,
                                                 @Nullable final MetadataQuery metadataQuery) {
    checkAllNotNull(db, assetClass);
    OrientQueryBuilder queryBuilder = orientQueryBuilder(db, assetClass, metadataQuery);
    String query = queryBuilder.buildQuery(assetClass, false);
    return FluentIterable
        .from(executeQuery(db, query, queryBuilder.getParameters()))
        .transform(new Function<ODocument, A>()
        {
          @Override
          public A apply(final ODocument assetDocument) {
            return assetFrom(registeredAssetAdapter(assetClass), assetDocument);
          }
        })
        .toList();
  }

  /**
   * @see ComponentStore#findComponents(Class, MetadataQuery)
   */
  protected <C extends Component> List<C> findComponents(final ODatabaseDocumentTx db,
                                                         final Class<C> componentClass,
                                                         @Nullable final MetadataQuery metadataQuery) {
    checkAllNotNull(db, componentClass);
    OrientQueryBuilder queryBuilder = orientQueryBuilder(db, componentClass, metadataQuery);
    String query = queryBuilder.buildQuery(componentClass, false);
    return FluentIterable
        .from(executeQuery(db, query, queryBuilder.getParameters()))
        .transform(new Function<ODocument, C>()
        {
          @Override
          public C apply(final ODocument componentDocument) {
            return componentFrom(db, registeredComponentAdapter(componentClass), componentDocument);
          }
        })
        .toList();
  }

  /**
   * @see ComponentStore#createComponent(Component)
   */
  protected <C extends Component> C createComponent(ODatabaseDocumentTx db, C sourceComponent) {
    Iterable<Asset> sourceAssets = ImmutableSet.of();
    ComponentEnvelope<C, Asset> sourceEnvelope = new ComponentEnvelope<>(sourceComponent, sourceAssets);
    return createComponentWithAssets(db, sourceEnvelope, beginBlobTx());
  }

  /**
   * @see ComponentStore#createAsset(EntityId, Asset)
   */
  @SuppressWarnings("unchecked")
  protected <A extends Asset> A createAsset(ODatabaseDocumentTx db, EntityId componentId, A sourceAsset,
      BlobTx blobTx) {
    // check args, get adapter, and make sure the asset exists
    checkAllNotNull(db, componentId, sourceAsset, blobTx);
    Class<A> assetClass = (Class<A>) sourceAsset.getClass();
    AssetAdapter<A> assetAdapter = registeredAssetAdapter(assetClass);
    ODocument componentDocument = retrieveExistingDocument(db, Component.class, componentId);

    // generate asset id
    EntityId assetId = entityIdFactory.newId();

    // create blob from source
    Blob blob = createBlob(blobTx, sourceAsset, assetId);
    checkArgument(blob != null, "Cannot store new asset without a content stream");

    // create and save a new asset document using source and system-provided data
    ODocument assetDocument = createAssetDocument(db, assetClass, sourceAsset, assetAdapter, assetId,
        blob, componentDocument.getIdentity());

    // add asset document reference to component document and save it
    Set<ORID> assetDocumentRids = componentDocument.field(P_ASSETS);
    assetDocumentRids.add(assetDocument.getIdentity());
    componentDocument.field(P_ASSETS, assetDocumentRids);
    componentDocument.save();

    // return a new entity reflecting the stored state
    return assetFrom(assetAdapter, assetDocument, componentId, blob);
  }

  /**
   * @see ComponentStore#createComponentsWithAssets(Iterable, Predicate, int)
   */
  @SuppressWarnings("unchecked")
  protected <C extends Component, A extends Asset> C createComponentWithAssets(ODatabaseDocumentTx db,
      ComponentEnvelope<C, A> sourceEnvelope, BlobTx blobTx) {
    // check args and get adapters
    checkAllNotNull(db, sourceEnvelope, blobTx);
    C sourceComponent = sourceEnvelope.getComponent();
    Class<C> componentClass = (Class<C>) sourceComponent.getClass();
    ComponentAdapter<C> componentAdapter = registeredComponentAdapter(componentClass);

    // generate component id
    EntityId componentId = entityIdFactory.newId();

    // create component document using source data
    ODocument componentDocument = db.newInstance(new OClassNameBuilder().type(componentClass).build());
    componentAdapter.populateDocument(sourceComponent, componentDocument);

    // amend component document with system-managed data
    componentDocument.field(EntityAdapter.P_ID, componentId.asUniqueString());
    Set<ORID> assets = ImmutableSet.of();
    componentDocument.field(P_ASSETS, assets);

    // store component document
    componentDocument.save();

    // create all associated assets and hold on to them, their entity ids, and rids
    Set<EntityId> assetIds = Sets.newHashSet();
    Set<ORID> assetDocumentRids = Sets.newHashSet();
    for (A sourceAsset: sourceEnvelope.getAssets()) {
      Class<A> assetClass = (Class<A>) sourceAsset.getClass();
      AssetAdapter<A> assetAdapter = registeredAssetAdapter(assetClass);

      // generate asset id
      EntityId assetId = entityIdFactory.newId();

      // create blob from source
      Blob blob = createBlob(blobTx, sourceAsset, assetId);
      checkArgument(blob != null, "Cannot store new asset without a content stream");

      // create and save a new asset document using source and system-provided data
      ODocument assetDocument = createAssetDocument(db, assetClass, sourceAsset, assetAdapter, assetId,
          blob, componentDocument.getIdentity());

      // remember ids for later
      assetIds.add(assetId);
      assetDocumentRids.add(assetDocument.getIdentity());
    }

    // update and save the component document with asset rids
    if (assetDocumentRids.size() > 0) {
      componentDocument.field(P_ASSETS, assetDocumentRids);
      componentDocument.save();
    }

    // return a new entity reflecting the stored state
    return componentFrom(componentAdapter, componentDocument, assetIds);
  }

  /**
   * @see ComponentStore#readAsset(Class, EntityId)
   */
  protected <A extends Asset> A readAsset(ODatabaseDocumentTx db, Class<A> assetClass, EntityId assetId) {
    return assetFrom(
        registeredAssetAdapter(checkNotNull(assetClass)),
        retrieveExistingDocument(checkNotNull(db), assetClass, assetId));
  }

  /**
   * @see ComponentStore#readComponent(Class, EntityId)
   */
  protected <C extends Component> C readComponent(ODatabaseDocumentTx db, Class<C> componentClass, EntityId componentId) {
    return componentFrom(checkNotNull(db),
        registeredComponentAdapter(checkNotNull(componentClass)),
        retrieveExistingDocument(db, componentClass, componentId));
  }

  /**
   * @see ComponentStore#readComponentWithAssets(Class, Class, EntityId)
   */
  protected <C extends Component, A extends Asset> ComponentEnvelope<C, A> readComponentsWithAssets(
      ODatabaseDocumentTx db, Class<C> componentClass, Class<A> assetClass, EntityId componentId) {
    // check args, get adapters, and make sure the component exists
    checkAllNotNull(db, componentClass, assetClass, componentId);
    ComponentAdapter<C> componentAdapter = registeredComponentAdapter(componentClass);
    AssetAdapter<A> assetAdapter = registeredAssetAdapter(assetClass);
    ODocument componentDocument = retrieveExistingDocument(db, componentClass, componentId);

    // read assets
    Set<EntityId> assetIds = Sets.newHashSet();
    Set<A> assets = Sets.newHashSet();
    Set<ODocument> assetDocuments = componentDocument.field(P_ASSETS);
    for (ODocument assetDocument: assetDocuments) {
      A asset = assetFrom(assetAdapter, assetDocument);
      assets.add(asset);
      assetIds.add(asset.getId());
    }

    // return envelope with component and assets
    C component = componentFrom(componentAdapter, componentDocument, assetIds);
    return new ComponentEnvelope<>(component, assets);
  }

  /**
   * @see ComponentStore#updateComponent(EntityId, Component)
   */
  @SuppressWarnings("unchecked")
  protected <C extends Component> C updateComponent(ODatabaseDocumentTx db, EntityId componentId, C sourceComponent) {
    // check args, get adapter, and make sure the document exists
    checkAllNotNull(db, componentId, sourceComponent);
    Class<C> componentClass = (Class<C>) sourceComponent.getClass();
    ComponentAdapter<C> componentAdapter = registeredComponentAdapter(componentClass);
    ODocument componentDocument = retrieveExistingDocument(db, componentClass, componentId);

    // update and save component document using source data
    componentAdapter.populateDocument(sourceComponent, componentDocument);
    componentDocument.save();

    // return a new entity reflecting the stored state
    return componentFrom(db, componentAdapter, componentDocument);
  }

  /**
   * @see ComponentStore#updateAsset(EntityId, Asset)
   */
  @SuppressWarnings("unchecked")
  protected <A extends Asset> A updateAsset(ODatabaseDocumentTx db, EntityId assetId, A sourceAsset, BlobTx blobTx) {
    // check args, get adapter, make sure the asset and component exists, and grab the component id
    checkAllNotNull(db, assetId, sourceAsset, blobTx);
    Class<A> assetClass = (Class<A>) sourceAsset.getClass();
    AssetAdapter<A> assetAdapter = registeredAssetAdapter(assetClass);
    ODocument assetDocument = retrieveExistingDocument(db, assetClass, assetId);
    ODocument componentDocument = assetDocument.field(P_COMPONENT);
    EntityId componentId = new EntityId((String) componentDocument.field(EntityAdapter.P_ID));

    // update asset document using source data
    assetAdapter.populateDocument(sourceAsset, assetDocument);

    // grab the local blob
    Blob currentBlob = getLocalBlob(assetDocument);

    // create blob from source if it specifies a content change
    Blob newBlob = createBlob(blobTx, sourceAsset, assetId);
    if (newBlob != null) {
      blobTx.delete(currentBlob);
      currentBlob = newBlob;

      // update blob ref with new one
      Map<String, String> blobRefs = ImmutableMap.of(localBlobStoreId, currentBlob.getId().asUniqueString());
      assetDocument.field(P_BLOB_REFS, blobRefs);
    }

    // populate other base asset properties
    assetDocument.field(P_CONTENT_TYPE, sourceAsset.getContentType());
    assetDocument.field(P_PATH, sourceAsset.getPath());

    // save changes to asset document
    assetDocument.save();

    // return a new entity reflecting the stored state
    return assetFrom(assetAdapter, assetDocument, componentId, currentBlob);
  }

  /**
   * @see ComponentStore#deleteAsset(Class, EntityId)
   */
  protected <A extends Asset> boolean deleteAsset(ODatabaseDocumentTx db, Class<A> assetClass, EntityId assetId,
      BlobTx blobTx) {
    checkAllNotNull(db, assetClass, assetId, blobTx);
    ODocument assetDocument = retrieveDocument(db, assetClass, assetId);
    if (assetDocument == null) {
      return false;
    }

    ODocument componentDocument = assetDocument.field(P_COMPONENT);

    Set<ORID> assetDocumentRids = componentDocument.field(P_ASSETS);
    assetDocumentRids.remove(assetDocument.getIdentity());
    componentDocument.field(P_ASSETS, assetDocumentRids);
    componentDocument.save();

    blobTx.delete(getLocalBlob(assetDocument));
    assetDocument.delete();
    return true;
  }

  /**
   * @see ComponentStore#deleteComponent(Class, EntityId)
   */
  protected <C extends Component> boolean deleteComponent(ODatabaseDocumentTx db, Class<C> componentClass,
      EntityId componentId, BlobTx blobTx) {
    checkAllNotNull(db, componentClass, componentId, blobTx);
    ODocument componentDocument = retrieveDocument(db, componentClass, componentId);
    if (componentDocument == null) {
      return false;
    }

    Set<ODocument> assetDocuments = componentDocument.field(P_ASSETS);
    for (ODocument assetDocument: assetDocuments) {
      blobTx.delete(getLocalBlob(assetDocument));
      assetDocument.delete();
    }
    componentDocument.delete();
    return true;
  }

  /**
   * Executes the given function using a database connection, optionally in a transaction, with optional
   * blob deletion on success and/or failure.
   *
   * @param useTransaction whether an explicit transaction should be used. If {@code true}, a transaction will be
   *        started on the {@code ODatabaseDocumentTx} before the function is executed. If a runtime exception occurs
   *        in the course of execution, this transaction will be automatically rolled back. Otherwise, the transaction
   *        will be automatically committed.
   * @param blobTx if specified, indicates which blobs to delete on sucess or failure.
   * @param function the function to execute.
   * @return the value returned by the given function.
   */
  private <T> T doExecute(Function<ODatabaseDocumentTx, T> function, boolean useTransaction, @Nullable BlobTx blobTx) {
    boolean successfullyFinished = false;
    try (ODatabaseDocumentTx db = databaseInstanceProvider.get().acquire()) {
      if (useTransaction) {
        db.begin();
      }
      try {
        T result = function.apply(db);
        if (useTransaction) {
          db.commit();
        }
        successfullyFinished = true;
        return result;
      }
      finally {
        if (successfullyFinished) {
          if (blobTx != null) {
            blobTx.commit();
          }
        }
        else {
          if (useTransaction && db.getTransaction().isActive()) {
            db.rollback();
          }
          if (blobTx != null) {
            blobTx.rollback();
          }
        }
      }
    }
  }

  private List<ODocument> executeQuery(ODatabaseDocumentTx db, String query, Map<String, Object> parameters) {
    log.info("Executing query: {} with parameters: {}", query, parameters);
    return db.command(new OSQLSynchQuery<>(query)).execute(parameters);
  }

  /**
   * @see ComponentStore#countAssets(Class, MetadataQueryRestriction)
   * @see ComponentStore#countComponents(Class, MetadataQueryRestriction)
   */
  private long executeCount(ODatabaseDocumentTx db, String countQuery, Map<String, Object> parameters) {
    return executeQuery(db, countQuery, parameters).get(0).field("COUNT");
  }

  private <A extends Asset> AssetAdapter<A> registeredAssetAdapter(final Class<A> assetClass) {
    AssetAdapter<A> adapter = entityAdapterRegistry.getAssetAdapter(checkNotNull(assetClass));
    checkState(adapter != null, "No asset adapter registered for class %", assetClass);
    return adapter;
  }

  private <C extends Component> ComponentAdapter<C> registeredComponentAdapter(final Class<C> componentClass) {
    ComponentAdapter<C> adapter = entityAdapterRegistry.getComponentAdapter(componentClass);
    checkState(adapter != null, "No component adapter registered for class %", componentClass);
    return adapter;
  }

  private <A extends Asset> A assetFrom(AssetAdapter<A> assetAdapter,
      ODocument assetDocument) {
    ODocument componentDocument = assetDocument.field(P_COMPONENT);
    // TODO: For performance, consider storing componentId as a STRING directly in the asset document.
    EntityId componentId = new EntityId((String) componentDocument.field(EntityAdapter.P_ID));
    Blob blob = getLocalBlob(assetDocument);
    return assetFrom(assetAdapter, assetDocument, componentId, blob);
  }

  private <A extends Asset> A assetFrom(EntityAdapter<A> assetEntityAdapter, ODocument assetDocument,
      EntityId componentId, final Blob blob) {
    try {
      A asset = assetEntityAdapter.getEntityClass().newInstance();
      // set type-specific, then base/system-controlled properties
      assetEntityAdapter.populateEntity(assetDocument, asset);
      asset.setId(new EntityId((String) assetDocument.field(P_ID)));
      asset.setComponentId(componentId);
      asset.setContentType((String) assetDocument.field(P_CONTENT_TYPE));
      asset.setPath((String) assetDocument.field(P_PATH));
      asset.setFirstCreated(new DateTime((Date) assetDocument.field(P_FIRST_CREATED)));
      asset.setLastModified(blob.getMetrics().getCreationTime());
      asset.setContentLength(blob.getMetrics().getContentSize());
      asset.setStreamSupplier(new Supplier<InputStream>()
      {
        @Override
        public InputStream get() {
          return blob.getInputStream();
        }
      });
      return asset;
    }
    catch (Throwable t) {
      throw Throwables.propagate(t);
    }
  }

  private <C extends Component> C componentFrom(ODatabaseDocumentTx db, ComponentAdapter<C> componentAdapter,
      ODocument componentDocument) {
    // determine asset ids
    // TODO: For performance, consider redundant storage as an EMBEDDEDSET directly in the component document.
    Set<EntityId> assetIds = Sets.newHashSet();
    final String query = String.format("SELECT FROM %s WHERE %s = ?", AssetAdapter.BASE_CLASS_NAME, P_COMPONENT);
    ORID componentRid = componentDocument.getIdentity();
    List<ODocument> results = db.command(new OSQLSynchQuery<>(query)).execute(componentRid);
    for (ODocument assetDocument: results) {
      assetIds.add(new EntityId((String) assetDocument.field(EntityAdapter.P_ID)));
    }

    return componentFrom(componentAdapter, componentDocument, assetIds);
  }

  private <C extends Entity> C componentFrom(EntityAdapter<C> componentEntityAdapter, ODocument componentDocument,
      Set<EntityId> assetIds) {
    try {
      C entity = componentEntityAdapter.getEntityClass().newInstance();
      // set type-specific, then base/system-controlled properties
      componentEntityAdapter.populateEntity(componentDocument, entity);
      Component component = (Component) entity;
      component.setId(new EntityId((String) componentDocument.field(P_ID)));
      component.setAssetIds(assetIds);
      return entity;
    }
    catch (Throwable t) {
      throw Throwables.propagate(t);
    }
  }

  private OrientQueryBuilder orientQueryBuilder(ODatabaseDocumentTx db, Class<? extends Entity> entityClass,
      @Nullable MetadataQuery query) {
    if (query == null) {
      return new OrientQueryBuilder(new MetadataQuery());
    }
    else if (query.skipEntityId() == null) {
      return new OrientQueryBuilder(query);
    }
    else {
      ODocument skipDocument = retrieveExistingDocument(db, entityClass, query.skipEntityId());
      return new OrientQueryBuilder(query, skipDocument.getIdentity());
    }
  }

  private MetadataQuery queryWith(@Nullable MetadataQueryRestriction restriction) {
    if (restriction == null) {
      return new MetadataQuery();
    }
    else {
      return new MetadataQuery().restriction(restriction);
    }
  }

  private ODocument retrieveExistingDocument(ODatabaseDocumentTx db, Class<? extends Entity> entityClass,
      EntityId entityId) {
    ODocument document = retrieveDocument(db, entityClass, entityId);
    checkState(document != null, "No such %s: %s", entityClass.getSimpleName(), entityId.asUniqueString());
    return document;
  }

  @Nullable
  private ODocument retrieveDocument(ODatabaseDocumentTx db, Class<? extends Entity> entityClass,
      EntityId entityId) {
    String className = new OClassNameBuilder().type(entityClass).build();
    final String query = String.format("SELECT FROM %s WHERE %s = ?", className, EntityAdapter.P_ID);
    List<ODocument> results = db.command(new OSQLSynchQuery<>(query)).execute(entityId.asUniqueString());
    if (results.isEmpty()) {
      return null;
    }
    else {
      return results.get(0);
    }
  }

  @Nullable
  private Blob createBlob(BlobTx blobTx, Asset asset, EntityId assetId) {
    checkNotNull(asset);
    try {
      InputStream sourceStream = asset.openStream();
      if (sourceStream == null) {
        return null;
      }
      return blobTx.create(sourceStream, ImmutableMap.of(
          BlobStore.BLOB_NAME_HEADER, assetId.asUniqueString(),
          BlobStore.CREATED_BY_HEADER, "")); // TODO: Determine where this comes from
    }
    catch (IOException e) {
      throw Throwables.propagate(e);
    }
  }

  private Blob getLocalBlob(ODocument assetDocument) {
    Map<String, String> blobRefs = assetDocument.field(P_BLOB_REFS);
    checkState(blobRefs.containsKey(localBlobStoreId), "Local blobRef does not exist for that asset");
    BlobId blobId = new BlobId(blobRefs.get(localBlobStoreId));
    Blob blob = blobStore.get(new BlobId(blobRefs.get(localBlobStoreId)));
    checkState(blob != null, "Blob not found in local store: %", blobId.asUniqueString());
    return blob;
  }

  private <A extends Entity> ODocument createAssetDocument(ODatabaseDocumentTx db, Class<A> assetClass, A sourceAsset,
      EntityAdapter<A> assetEntityAdapter, EntityId assetId, Blob blob, ORID componentDocumentRid) {
    // create asset document using source data
    ODocument assetDocument = db.newInstance(new OClassNameBuilder().type(assetClass).build());
    assetEntityAdapter.populateDocument(sourceAsset, assetDocument);

    // amend asset document with base/system-controlled properties
    assetDocument.field(EntityAdapter.P_ID, assetId.asUniqueString());
    assetDocument.field(P_FIRST_CREATED, blob.getMetrics().getCreationTime().toDate());
    assetDocument.field(P_COMPONENT, componentDocumentRid);
    Map<String, String> blobRefs = ImmutableMap.of(localBlobStoreId, blob.getId().asUniqueString());
    assetDocument.field(P_BLOB_REFS, blobRefs);
    Asset asset = (Asset) sourceAsset;
    assetDocument.field(P_CONTENT_TYPE, asset.getContentType());
    assetDocument.field(P_PATH, asset.getPath());

    // store and return asset document
    return assetDocument.save();
  }

  private static void checkAllNotNull(Object... objects) {
    for (Object o: objects) {
      checkNotNull(o);
    }
  }
}
TOP

Related Classes of org.sonatype.nexus.component.services.internal.storage.ComponentStoreImplSupport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.