Package org.sonatype.nexus.proxy.maven

Source Code of org.sonatype.nexus.proxy.maven.AbstractMavenRepository

/*
* 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.proxy.maven;

import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;

import javax.inject.Inject;

import org.sonatype.configuration.ConfigurationException;
import org.sonatype.nexus.proxy.AccessDeniedException;
import org.sonatype.nexus.proxy.IllegalOperationException;
import org.sonatype.nexus.proxy.ItemNotFoundException;
import org.sonatype.nexus.proxy.LocalStorageException;
import org.sonatype.nexus.proxy.ResourceStoreRequest;
import org.sonatype.nexus.proxy.StorageException;
import org.sonatype.nexus.proxy.events.RepositoryConfigurationUpdatedEvent;
import org.sonatype.nexus.proxy.events.RepositoryEventEvictUnusedItems;
import org.sonatype.nexus.proxy.events.RepositoryEventRecreateMavenMetadata;
import org.sonatype.nexus.proxy.item.AbstractStorageItem;
import org.sonatype.nexus.proxy.item.RepositoryItemUid;
import org.sonatype.nexus.proxy.item.StorageFileItem;
import org.sonatype.nexus.proxy.item.StorageItem;
import org.sonatype.nexus.proxy.item.uid.IsHiddenAttribute;
import org.sonatype.nexus.proxy.maven.EvictUnusedMavenItemsWalkerProcessor.EvictUnusedMavenItemsWalkerFilter;
import org.sonatype.nexus.proxy.maven.packaging.ArtifactPackagingMapper;
import org.sonatype.nexus.proxy.maven.routing.Manager;
import org.sonatype.nexus.proxy.maven.routing.ProxyRequestFilter;
import org.sonatype.nexus.proxy.repository.AbstractProxyRepository;
import org.sonatype.nexus.proxy.repository.DefaultRepositoryKind;
import org.sonatype.nexus.proxy.repository.HostedRepository;
import org.sonatype.nexus.proxy.repository.MutableProxyRepositoryKind;
import org.sonatype.nexus.proxy.repository.ProxyRepository;
import org.sonatype.nexus.proxy.repository.Repository;
import org.sonatype.nexus.proxy.repository.RepositoryKind;
import org.sonatype.nexus.proxy.storage.UnsupportedStorageOperationException;
import org.sonatype.nexus.proxy.utils.RepositoryStringUtils;
import org.sonatype.nexus.proxy.walker.DefaultWalkerContext;
import org.sonatype.nexus.proxy.walker.WalkerException;

import org.codehaus.plexus.util.StringUtils;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.sonatype.nexus.proxy.ItemNotFoundException.reasonFor;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.ATTR_REMOTE_MD5;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.ATTR_REMOTE_SHA1;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.SUFFIX_MD5;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.SUFFIX_SHA1;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.doRetrieveMD5;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.doRetrieveSHA1;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.doStoreMD5;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.doStoreSHA1;
import static org.sonatype.nexus.proxy.maven.ChecksumContentValidator.newHashItem;

/**
* The abstract (layout unaware) Maven Repository.
*
* @author cstamas
*/
public abstract class AbstractMavenRepository
    extends AbstractProxyRepository
    implements MavenRepository, MavenHostedRepository, MavenProxyRepository
{

  /**
   * Metadata manager.
   */
  private MetadataManager metadataManager;

  /**
   * The artifact packaging mapper.
   */
  private ArtifactPackagingMapper artifactPackagingMapper;

  private ProxyRequestFilter proxyRequestFilter;

  private MutableProxyRepositoryKind repositoryKind;

  private ArtifactStoreHelper artifactStoreHelper;

  /**
   * if download remote index flag changed, need special handling after save
   */
  private boolean downloadRemoteIndexEnabled = false;
 
  @Inject
  public void populateAbstractMavenRepository(final MetadataManager metadataManager,
      final ArtifactPackagingMapper artifactPackagingMapper, final ProxyRequestFilter proxyRequestFilter)
  {
    this.metadataManager = checkNotNull(metadataManager);
    this.artifactPackagingMapper = checkNotNull(artifactPackagingMapper);
    this.proxyRequestFilter = checkNotNull(proxyRequestFilter);
    this.repositoryKind = new MutableProxyRepositoryKind(this, Arrays.asList(new Class<?>[] { MavenRepository.class }),
        new DefaultRepositoryKind(MavenHostedRepository.class, null), new DefaultRepositoryKind(
            MavenProxyRepository.class, null));
    this.artifactStoreHelper = new ArtifactStoreHelper(this);
  }

  @Override
  protected AbstractMavenRepositoryConfiguration getExternalConfiguration(boolean forModification) {
    return (AbstractMavenRepositoryConfiguration) super.getExternalConfiguration(forModification);
  }

  @Override
  public boolean commitChanges()
      throws ConfigurationException
  {
    boolean result = super.commitChanges();

    if (result) {
      this.downloadRemoteIndexEnabled = false;
    }

    return result;
  }

  @Override
  public boolean rollbackChanges() {
    this.downloadRemoteIndexEnabled = false;

    return super.rollbackChanges();
  }

  @Override
  protected RepositoryConfigurationUpdatedEvent getRepositoryConfigurationUpdatedEvent() {
    RepositoryConfigurationUpdatedEvent event = super.getRepositoryConfigurationUpdatedEvent();

    event.setDownloadRemoteIndexEnabled(this.downloadRemoteIndexEnabled);

    return event;
  }

  @Override
  public ArtifactStoreHelper getArtifactStoreHelper() {
    return artifactStoreHelper;
  }

  @Override
  public ArtifactPackagingMapper getArtifactPackagingMapper() {
    return artifactPackagingMapper;
  }

  protected ProxyRequestFilter getProxyRequestFilter() {
    return proxyRequestFilter;
  }

  /**
   * Override the "default" kind with Maven specifics.
   */
  @Override
  public RepositoryKind getRepositoryKind() {
    return repositoryKind;
  }

  @Override
  protected Collection<String> doEvictUnusedItems(final ResourceStoreRequest request, final long timestamp) {
    if (getRepositoryKind().isFacetAvailable(ProxyRepository.class)) {
      Collection<String> result =
          doEvictUnusedItems(request, timestamp, new EvictUnusedMavenItemsWalkerProcessor(timestamp),
              new EvictUnusedMavenItemsWalkerFilter());
      eventBus().post(new RepositoryEventEvictUnusedItems(this));
      return result;
    }
    else {
      return super.doEvictUnusedItems(request, timestamp);
    }
  }

  @Override
  public boolean recreateMavenMetadata(final ResourceStoreRequest request) {
    if (!shouldServiceOperation(request, "recreateMavenMetadata")) {
      return false;
    }
    if (!getRepositoryKind().isFacetAvailable(HostedRepository.class)) {
      log.debug("Not performing recreateMavenMetadata, {} is not hosted.", this);
      return false;
    }

    if (StringUtils.isEmpty(request.getRequestPath())) {
      request.setRequestPath(RepositoryItemUid.PATH_ROOT);
    }

    try {
      if (!getLocalStorage().containsItem(this, request)) {
        log.info(
            "Skip rebuilding Maven2 Metadata in repository {} because it does not contain path='{}'.", this,
            request.getRequestPath());
        return false;
      }
    }
    catch (LocalStorageException e) {
      log.warn("Skip rebuilding Maven2 Metadata in repository {}", this, e);
      return false;
    }
    return doRecreateMavenMetadata(request);
  }

  protected boolean doRecreateMavenMetadata(final ResourceStoreRequest request) {
    log.info("Recreating Maven2 metadata in hosted repository {} from path='{}'", this, request.getRequestPath());
    final RecreateMavenMetadataWalkerProcessor wp = new RecreateMavenMetadataWalkerProcessor(log);
    final DefaultWalkerContext ctx = new DefaultWalkerContext(this, request);
    ctx.getProcessors().add(wp);
    try {
      getWalker().walk(ctx);
    }
    catch (WalkerException e) {
      if (!(e.getWalkerContext().getStopCause() instanceof ItemNotFoundException)) {
        // everything that is not ItemNotFound should be reported,
        // otherwise just neglect it
        throw e;
      }
    }
    eventBus().post(new RepositoryEventRecreateMavenMetadata(this));
    return !ctx.isStopped();
  }

  @Override
  public boolean isDownloadRemoteIndexes() {
    return getExternalConfiguration(false).isDownloadRemoteIndex();
  }

  @Override
  public void setDownloadRemoteIndexes(boolean downloadRemoteIndexes) {
    boolean oldValue = isDownloadRemoteIndexes();
    boolean newValue = downloadRemoteIndexes;

    getExternalConfiguration(true).setDownloadRemoteIndex(downloadRemoteIndexes);

    if (oldValue == false && newValue == true) {
      this.downloadRemoteIndexEnabled = true;
    }
  }

  @Override
  public RepositoryPolicy getRepositoryPolicy() {
    return getExternalConfiguration(false).getRepositoryPolicy();
  }

  @Override
  public void setRepositoryPolicy(RepositoryPolicy repositoryPolicy) {
    getExternalConfiguration(true).setRepositoryPolicy(repositoryPolicy);
  }

  @Override
  public boolean isCleanseRepositoryMetadata() {
    return getExternalConfiguration(false).isCleanseRepositoryMetadata();
  }

  @Override
  public void setCleanseRepositoryMetadata(boolean cleanseRepositoryMetadata) {
    getExternalConfiguration(true).setCleanseRepositoryMetadata(cleanseRepositoryMetadata);
  }

  public ChecksumPolicy getChecksumPolicy() {
    return getExternalConfiguration(false).getChecksumPolicy();
  }

  public void setChecksumPolicy(ChecksumPolicy checksumPolicy) {
    getExternalConfiguration(true).setChecksumPolicy(checksumPolicy);
  }

  @Override
  public int getArtifactMaxAge() {
    return getExternalConfiguration(false).getArtifactMaxAge();
  }

  @Override
  public void setArtifactMaxAge(int maxAge) {
    getExternalConfiguration(true).setArtifactMaxAge(maxAge);
  }

  @Override
  public int getMetadataMaxAge() {
    return getExternalConfiguration(false).getMetadataMaxAge();
  }

  @Override
  public void setMetadataMaxAge(int metadataMaxAge) {
    getExternalConfiguration(true).setMetadataMaxAge(metadataMaxAge);
  }

  @Override
  public boolean isMavenArtifact(StorageItem item) {
    return isMavenArtifactPath(item.getPath());
  }

  @Override
  public boolean isMavenMetadata(StorageItem item) {
    return isMavenMetadataPath(item.getPath());
  }

  @Override
  public boolean isMavenArtifactPath(String path) {
    return getGavCalculator().pathToGav(path) != null;
  }

  @Override
  public abstract boolean isMavenMetadataPath(String path);

  public abstract boolean isMavenArtifactChecksumPath(String path);

  public abstract boolean shouldServeByPolicies(ResourceStoreRequest request);

  @Override
  public void storeItemWithChecksums(ResourceStoreRequest request, InputStream is, Map<String, String> userAttributes)
      throws UnsupportedStorageOperationException, IllegalOperationException, StorageException, AccessDeniedException
  {
    if (log.isDebugEnabled()) {
      log.debug("storeItemWithChecksums() :: " + request.getRequestPath());
    }

    getArtifactStoreHelper().storeItemWithChecksums(request, is, userAttributes);
  }

  @Override
  public void deleteItemWithChecksums(ResourceStoreRequest request)
      throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException,
             StorageException, AccessDeniedException
  {
    if (log.isDebugEnabled()) {
      log.debug("deleteItemWithChecksums() :: " + request.getRequestPath());
    }

    getArtifactStoreHelper().deleteItemWithChecksums(request);
  }

  @Override
  public void storeItemWithChecksums(boolean fromTask, AbstractStorageItem item)
      throws UnsupportedStorageOperationException, IllegalOperationException, StorageException
  {
    if (log.isDebugEnabled()) {
      log.debug("storeItemWithChecksums() :: " + item.getRepositoryItemUid().toString());
    }

    getArtifactStoreHelper().storeItemWithChecksums(fromTask, item);
  }

  @Override
  public void deleteItemWithChecksums(boolean fromTask, ResourceStoreRequest request)
      throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException, StorageException
  {
    if (log.isDebugEnabled()) {
      log.debug("deleteItemWithChecksums() :: " + request.toString());
    }

    getArtifactStoreHelper().deleteItemWithChecksums(fromTask, request);
  }

  @Override
  public MetadataManager getMetadataManager() {
    return metadataManager;
  }

  // =================================================================================
  // DefaultRepository customizations
  @Override
  protected StorageItem doRetrieveItem(ResourceStoreRequest request)
      throws IllegalOperationException, ItemNotFoundException, StorageException
  {
    if (!shouldServeByPolicies(request)) {
      if (log.isDebugEnabled()) {
        log.debug(
            "The serving of item " + request.toString() + " is forbidden by Maven repository policy.");
      }

      throw new ItemNotFoundException(reasonFor(request, this,
          "Retrieval of %s from %s is forbidden by repository policy %s.", request.getRequestPath(),
          this, getRepositoryPolicy()));
    }

    if (getRepositoryKind().isFacetAvailable(ProxyRepository.class)
        && !request.getRequestPath().startsWith("/.")) {
      if (request.getRequestPath().endsWith(SUFFIX_SHA1)) {
        return doRetrieveSHA1(this, request, doRetrieveArtifactItem(request, SUFFIX_SHA1)).getHashItem();
      }

      if (request.getRequestPath().endsWith(SUFFIX_MD5)) {
        return doRetrieveMD5(this, request, doRetrieveArtifactItem(request, SUFFIX_MD5)).getHashItem();
      }
    }

    return super.doRetrieveItem(request);
  }

  /**
   * Retrieves artifact corresponding to .sha1/.md5 request (or any request suffix).
   */
  private StorageItem doRetrieveArtifactItem(ResourceStoreRequest hashRequest, String suffix)
      throws ItemNotFoundException, StorageException, IllegalOperationException
  {
    final String hashPath = hashRequest.getRequestPath();
    final String itemPath = hashPath.substring(0, hashPath.length() - suffix.length());
    hashRequest.pushRequestPath(itemPath);
    try {
      return super.doRetrieveItem(hashRequest);
    }
    finally {
      hashRequest.popRequestPath();
    }
  }

  @Override
  protected void shouldTryRemote(final ResourceStoreRequest request)
      throws IllegalOperationException, ItemNotFoundException
  {
    // do super first
    super.shouldTryRemote(request);
    // if here, super did not throw any exception, so let's continue
    // apply autorouting filter to "normal" requests only, not hidden (which is meta or plain hidden)
    final RepositoryItemUid uid = createUid(request.getRequestPath());
    if (!uid.getBooleanAttributeValue(IsHiddenAttribute.class)) {
      // but filter it only if request is not marked as NFS
      if (!request.getRequestContext().containsKey(Manager.ROUTING_REQUEST_NFS_FLAG_KEY)) {
        final boolean proxyFilterAllowed = getProxyRequestFilter().allowed(this, request);
        if (!proxyFilterAllowed) {
          throw new ItemNotFoundException(ItemNotFoundException.reasonFor(request, this,
              "Automatic routing filter rejected remote request for path %s from %s", request.getRequestPath(),
              this));
        }
      }
    }
  }

  @Override
  public void storeItem(boolean fromTask, StorageItem item)
      throws UnsupportedStorageOperationException, IllegalOperationException, StorageException
  {
    final ResourceStoreRequest request = new ResourceStoreRequest(item); // this is local only request
    if (shouldServeByPolicies(request)) {
      if (getRepositoryKind().isFacetAvailable(ProxyRepository.class) && item instanceof StorageFileItem
          && !item.getPath().startsWith("/.")) {
        try {
          if (item.getPath().endsWith(SUFFIX_SHA1)) {
            doStoreSHA1(this, doRetrieveArtifactItem(request, SUFFIX_SHA1), (StorageFileItem) item);
          }
          else if (item.getPath().endsWith(SUFFIX_MD5)) {
            doStoreMD5(this, doRetrieveArtifactItem(request, SUFFIX_MD5), (StorageFileItem) item);
          }
          else {
            super.storeItem(fromTask, item);
          }
        }
        catch (ItemNotFoundException e) {
          // ignore storeItem request
          // this is a maven2 proxy repository, it is requested to store .sha1/.md5 file
          // and not there is not corresponding artifact
        }
      }
      else {
        super.storeItem(fromTask, item);
      }
    }
    else {
      String msg =
          "Storing of item " + item.getRepositoryItemUid().toString()
              + " is forbidden by Maven Repository policy. Because " + getId() + " is a "
              + getRepositoryPolicy().name() + " repository";

      log.info(msg);

      throw new UnsupportedStorageOperationException(msg);
    }
  }

  @Override
  public AbstractStorageItem doCacheItem(AbstractStorageItem item)
      throws LocalStorageException
  {
    final AbstractStorageItem result = super.doCacheItem(item);
    result.getRepositoryItemAttributes().remove(ATTR_REMOTE_SHA1);
    result.getRepositoryItemAttributes().remove(ATTR_REMOTE_MD5);
    return result;
  }

  @Override
  public boolean isCompatible(Repository repository) {
    if (super.isCompatible(repository) && MavenRepository.class.isAssignableFrom(repository.getClass())
        && getRepositoryPolicy().equals(((MavenRepository) repository).getRepositoryPolicy())) {
      return true;
    }

    return false;
  }

  // =================================================================================
  // DefaultRepository customizations

  @Override
  protected Collection<StorageItem> doListItems(ResourceStoreRequest request)
      throws ItemNotFoundException, StorageException
  {
    Collection<StorageItem> items = super.doListItems(request);
    if (getRepositoryKind().isFacetAvailable(ProxyRepository.class)) {
      Map<String, StorageItem> result = new TreeMap<String, StorageItem>();
      for (StorageItem item : items) {
        putChecksumItem(result, request, item, ATTR_REMOTE_SHA1, SUFFIX_SHA1);
        putChecksumItem(result, request, item, ATTR_REMOTE_MD5, SUFFIX_MD5);
      }

      for (StorageItem item : items) {
        if (!result.containsKey(item.getPath())) {
          result.put(item.getPath(), item);
        }
      }

      items = result.values();
    }
    return items;
  }

  private void putChecksumItem(Map<String, StorageItem> checksums, ResourceStoreRequest request,
                               StorageItem artifact, String attrname, String suffix)
  {
    String hash = artifact.getRepositoryItemAttributes().get(attrname);
    if (hash != null) {
      String hashPath = artifact.getPath() + suffix;
      request.pushRequestPath(hashPath);
      try {
        checksums.put(hashPath, newHashItem(this, request, artifact, hash));
      }
      finally {
        request.popRequestPath();
      }
    }
  }

  /**
   * Beside original behavior, only add to NFC when remote access is not rejected by autorouting.
   *
   * @since 2.4
   */
  @Override
  protected boolean shouldAddToNotFoundCache(final ResourceStoreRequest request) {
    boolean shouldAddToNFC = super.shouldAddToNotFoundCache(request);
    if (shouldAddToNFC && request.getRequestContext().containsKey(Manager.ROUTING_REQUEST_REJECTED_FLAG_KEY)) {
      request.getRequestContext().remove(Manager.ROUTING_REQUEST_REJECTED_FLAG_KEY);
      shouldAddToNFC = false;
      log.debug("Maven proxy repository {} autorouting rejected this request, not adding path {} to NFC.",
          RepositoryStringUtils.getHumanizedNameString(this), request.getRequestPath());
    }
    return shouldAddToNFC;
  }

  @Override
  protected boolean shouldNeglectItemNotFoundExOnDelete(ResourceStoreRequest request, ItemNotFoundException ex) {
    return isMavenArtifactChecksumPath(request.getRequestPath());
  }

  /**
   * Deletes item and regenerates Maven metadata, if repository is a hosted repository and maven-metadata.xml file is
   * present.
   *
   * @since 2.5
   */
  @Override
  protected void doDeleteItem(final ResourceStoreRequest request)
      throws UnsupportedStorageOperationException, ItemNotFoundException, StorageException
  {
    try {
      super.doDeleteItem(request);
    }
    catch (ItemNotFoundException ex) {
      // NEXUS-5773, NEXUS-5418
      // Since Nx 2.5, checksum are not stored on disk
      // but are stored as attributes. Still, upgraded systems
      // might have them on disk, so delete is attempted
      // but INFex on Checksum file in general can be neglected here.
      if (!shouldNeglectItemNotFoundExOnDelete(request, ex)) {
        throw ex;
      }
    }
    // regenerate maven metadata for parent of this item if is a hosted maven repo and it contains maven-metadata.xml
    if (request.isExternal() && getRepositoryKind().isFacetAvailable(MavenHostedRepository.class)) {
      String parentPath = request.getRequestPath();
      parentPath = parentPath.substring(0, parentPath.lastIndexOf(RepositoryItemUid.PATH_SEPARATOR));
      final String parentMetadataPath = parentPath + "/maven-metadata.xml";
      try {
        if (getLocalStorage().containsItem(this, new ResourceStoreRequest(parentMetadataPath))) {
          recreateMavenMetadata(new ResourceStoreRequest(parentPath));
        }
      }
      catch (Exception e) {
        log.warn("Could not maintain Maven metadata '{}'", parentMetadataPath, e);
      }
    }
  }

}
TOP

Related Classes of org.sonatype.nexus.proxy.maven.AbstractMavenRepository

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.