Package org.sonatype.nexus.proxy.maven

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

/*
* 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.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;

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.NoSuchResourceStoreException;
import org.sonatype.nexus.proxy.ResourceStoreRequest;
import org.sonatype.nexus.proxy.StorageException;
import org.sonatype.nexus.proxy.attributes.inspectors.DigestCalculatingInspector;
import org.sonatype.nexus.proxy.item.AbstractStorageItem;
import org.sonatype.nexus.proxy.item.DefaultStorageFileItem;
import org.sonatype.nexus.proxy.item.DefaultStorageLinkItem;
import org.sonatype.nexus.proxy.item.RepositoryItemUid;
import org.sonatype.nexus.proxy.item.StorageCollectionItem;
import org.sonatype.nexus.proxy.item.StorageFileItem;
import org.sonatype.nexus.proxy.item.StorageItem;
import org.sonatype.nexus.proxy.item.StorageLinkItem;
import org.sonatype.nexus.proxy.item.StringContentLocator;
import org.sonatype.nexus.proxy.maven.gav.Gav;
import org.sonatype.nexus.proxy.maven.gav.GavCalculator;
import org.sonatype.nexus.proxy.maven.packaging.ArtifactPackagingMapper;
import org.sonatype.nexus.proxy.repository.AbstractShadowRepository;
import org.sonatype.nexus.proxy.repository.DefaultRepositoryKind;
import org.sonatype.nexus.proxy.repository.IncompatibleMasterRepositoryException;
import org.sonatype.nexus.proxy.repository.Repository;
import org.sonatype.nexus.proxy.repository.RepositoryKind;
import org.sonatype.nexus.proxy.storage.UnsupportedStorageOperationException;

import com.google.common.collect.Lists;
import org.codehaus.plexus.util.StringUtils;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.sonatype.nexus.proxy.ItemNotFoundException.reasonFor;

/**
* Base class for shadows that make "gateways" from M1 to M2 lauouts and vice versa.
*
* @author cstamas
*/
public abstract class LayoutConverterShadowRepository
    extends AbstractShadowRepository
    implements MavenShadowRepository
{
  /**
   * The GAV Calculator.
   */
  private GavCalculator m1GavCalculator;

  /**
   * The GAV Calculator.
   */
  private  GavCalculator m2GavCalculator;

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

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

  /**
   * Repository kind.
   */
  private RepositoryKind repositoryKind;

  /**
   * ArtifactStoreHelper.
   */
  private ArtifactStoreHelper artifactStoreHelper;
 
  @Inject
  public void populateLayoutConverterShadowRepository(final @Named("maven1") GavCalculator m1GavCalculator,
      final @Named("maven2") GavCalculator m2GavCalculator, final MetadataManager metadataManager,
      final ArtifactPackagingMapper artifactPackagingMapper)
  {
    this.m1GavCalculator = checkNotNull(m1GavCalculator);
    this.m2GavCalculator = checkNotNull(m2GavCalculator);
    this.metadataManager = checkNotNull(metadataManager);
    this.artifactPackagingMapper = checkNotNull(artifactPackagingMapper);
    this.repositoryKind = new DefaultRepositoryKind(MavenShadowRepository.class,
        Arrays.asList(new Class<?>[] { MavenRepository.class }));
    this.artifactStoreHelper = new ArtifactStoreHelper(this);
  }

  @Override
  public RepositoryKind getRepositoryKind() {
    return repositoryKind;
  }

  @Override
  public MavenRepository getMasterRepository() {
    return super.getMasterRepository().adaptToFacet(MavenRepository.class);
  }

  @Override
  public void setMasterRepository(Repository masterRepository)
      throws IncompatibleMasterRepositoryException
  {
    // we allow only MavenRepository instances as masters
    if (!masterRepository.getRepositoryKind().isFacetAvailable(MavenRepository.class)) {
      throw new IncompatibleMasterRepositoryException(
          "This shadow repository needs master repository which implements MavenRepository interface!", this,
          masterRepository.getId());
    }

    super.setMasterRepository(masterRepository);
  }

  public GavCalculator getM1GavCalculator() {
    return m1GavCalculator;
  }

  public GavCalculator getM2GavCalculator() {
    return m2GavCalculator;
  }

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

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

  @Override
  public void setRepositoryPolicy(RepositoryPolicy repositoryPolicy) {
    throw new UnsupportedOperationException("This method is not supported on Repository of type SHADOW");
  }

  @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);

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

  @Override
  public boolean recreateMavenMetadata(final ResourceStoreRequest request) {
    return false;
  }

  @Override
  public void storeItemWithChecksums(final ResourceStoreRequest request, final InputStream is,
                                     final Map<String, String> userAttributes)
      throws UnsupportedStorageOperationException, IllegalOperationException, StorageException, AccessDeniedException
  {
    String originalPath = request.getRequestPath();

    if (log.isDebugEnabled()) {
      log.debug("storeItemWithChecksums() :: " + request.getRequestPath());
    }

    try {
      try {
        storeItem(request, is, userAttributes);
      }
      catch (IOException e) {
        throw new LocalStorageException("Could not get the content from the ContentLocator!", e);
      }

      StorageFileItem storedFile = (StorageFileItem) retrieveItem(false, request);

      String sha1Hash = storedFile.getRepositoryItemAttributes().get(DigestCalculatingInspector.DIGEST_SHA1_KEY);

      String md5Hash = storedFile.getRepositoryItemAttributes().get(DigestCalculatingInspector.DIGEST_MD5_KEY);

      if (!StringUtils.isEmpty(sha1Hash)) {
        request.setRequestPath(storedFile.getPath() + ".sha1");

        storeItem(false, new DefaultStorageFileItem(this, request, true, true, new StringContentLocator(
            sha1Hash)));
      }

      if (!StringUtils.isEmpty(md5Hash)) {
        request.setRequestPath(storedFile.getPath() + ".md5");

        storeItem(false, new DefaultStorageFileItem(this, request, true, true, new StringContentLocator(
            md5Hash)));
      }
    }
    catch (ItemNotFoundException e) {
      throw new LocalStorageException("Storage inconsistency!", e);
    }
    finally {
      request.setRequestPath(originalPath);
    }
  }

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

    try {
      deleteItem(request);
    }
    catch (ItemNotFoundException e) {
      if (request.getRequestPath().endsWith(".asc")) {
        // Do nothing no guarantee that the .asc files will exist
      }
      else {
        throw e;
      }
    }

    String originalPath = request.getRequestPath();

    request.setRequestPath(originalPath + ".sha1");

    try {
      deleteItem(request);
    }
    catch (ItemNotFoundException e) {
      // ignore not found
    }

    request.setRequestPath(originalPath + ".md5");

    try {
      deleteItem(request);
    }
    catch (ItemNotFoundException e) {
      // ignore not found
    }

    // Now remove the .asc files, and the checksums stored with them as well
    // Note this is a recursive call, hence the check for .asc
    if (!originalPath.endsWith(".asc")) {
      request.setRequestPath(originalPath + ".asc");

      deleteItemWithChecksums(request);
    }
  }

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

    try {
      try {
        storeItem(fromTask, item);
      }
      catch (IOException e) {
        throw new LocalStorageException("Could not get the content from the ContentLocator!", e);
      }

      StorageFileItem storedFile = (StorageFileItem) retrieveItem(fromTask, new ResourceStoreRequest(item));

      ResourceStoreRequest req = new ResourceStoreRequest(storedFile);

      String sha1Hash = storedFile.getRepositoryItemAttributes().get(DigestCalculatingInspector.DIGEST_SHA1_KEY);

      String md5Hash = storedFile.getRepositoryItemAttributes().get(DigestCalculatingInspector.DIGEST_MD5_KEY);

      if (!StringUtils.isEmpty(sha1Hash)) {
        req.setRequestPath(item.getPath() + ".sha1");

        storeItem(fromTask, new DefaultStorageFileItem(this, req, true, true, new StringContentLocator(
            sha1Hash)));
      }

      if (!StringUtils.isEmpty(md5Hash)) {
        req.setRequestPath(item.getPath() + ".md5");

        storeItem(fromTask, new DefaultStorageFileItem(this, req, true, true, new StringContentLocator(
            md5Hash)));
      }
    }
    catch (ItemNotFoundException e) {
      throw new LocalStorageException("Storage inconsistency!", e);
    }
  }

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

    deleteItem(fromTask, request);

    request.pushRequestPath(request.getRequestPath() + ".sha1");
    try {
      deleteItem(fromTask, request);
    }
    catch (ItemNotFoundException e) {
      // ignore not found
    }
    finally {
      request.popRequestPath();
    }

    request.pushRequestPath(request.getRequestPath() + ".md5");
    try {
      deleteItem(fromTask, request);
    }
    catch (ItemNotFoundException e) {
      // ignore not found
    }
    finally {
      request.popRequestPath();
    }
  }

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

  // =================================================================================
  // ShadowRepository customizations

  /**
   * Transforms a full artifact path from M1 layout to M2 layout.
   */
  protected List<String> transformM1toM2(final String path) {
    final Gav gav = getM1GavCalculator().pathToGav(path);

    // Unsupported path
    if (gav == null) {
      return null;
    }
    // m2 repo is layouted as:
    // g/i/d
    // aid
    // version
    // files

    StringBuilder sb = new StringBuilder(RepositoryItemUid.PATH_ROOT);
    sb.append(gav.getGroupId().replaceAll("\\.", "/"));
    sb.append(RepositoryItemUid.PATH_SEPARATOR);
    sb.append(gav.getArtifactId());
    sb.append(RepositoryItemUid.PATH_SEPARATOR);
    sb.append(gav.getVersion());
    sb.append(RepositoryItemUid.PATH_SEPARATOR);
    sb.append(gav.getName());
    return Collections.singletonList(sb.toString());
  }

  /**
   * Transforms a full artifact path from M2 layout to M1 layout.
   */
  protected List<String> transformM2toM1(final String path,
                                         final List<String> extraFolders)
  {
    final Gav gav = getM2GavCalculator().pathToGav(path);

    // Unsupported path
    if (gav == null) {
      return null;
    }

    final List<String> exts = Lists.newArrayList();
    exts.add(gav.getExtension() + "s");
    if ((extraFolders != null) && !extraFolders.isEmpty()) {
      exts.addAll(extraFolders);
    }
    final List<String> result = Lists.newArrayListWithCapacity(exts.size());
    for (final String ext : exts) {
      // m1 repo is layouted as:
      // g.i.d
      // poms/jars/java-sources/licenses
      // files
      final StringBuilder sb = new StringBuilder(RepositoryItemUid.PATH_ROOT);
      sb.append(gav.getGroupId());
      sb.append(RepositoryItemUid.PATH_SEPARATOR);
      sb.append(ext);
      sb.append(RepositoryItemUid.PATH_SEPARATOR);

      // NEXUS-6185: Fix for M2 timestamped snapshots
      if (gav.isSnapshot()) {
        // we're dealing with files like
        // artifact-1.2-20131207.174838-3.jar
        // goal is
        // artifact-1.2-SNAPSHOT
        sb.append(gav.getArtifactId()).append("-").append(gav.getBaseVersion());

        if (!StringUtils.isEmpty(gav.getClassifier())) {
          sb.append("-").append(gav.getClassifier());
        }
        sb.append(".").append(gav.getExtension());
        if (gav.isHash()) {
          sb.append(".").append(gav.getHashType());
        }
        if (gav.isSignature()) {
          sb.append(".").append(gav.getSignatureType());
        }
      }
      else {
        sb.append(gav.getName());
      }
      result.add(sb.toString());
    }
    return result;
  }

  @Override
  protected StorageLinkItem createLink(final StorageItem item)
      throws UnsupportedStorageOperationException, IllegalOperationException, StorageException
  {
    List<String> shadowPaths = transformMaster2Shadow(item.getPath());

    if (shadowPaths != null && !shadowPaths.isEmpty()) {
      ResourceStoreRequest req = new ResourceStoreRequest(shadowPaths.get(0));
      req.getRequestContext().setParentContext(item.getItemContext());

      DefaultStorageLinkItem link =
          new DefaultStorageLinkItem(this, req, true, true, item.getRepositoryItemUid());

      storeItem(false, link);

      return link;
    }
    else {
      return null;
    }
  }

  @Override
  protected void deleteLink(final StorageItem item)
      throws UnsupportedStorageOperationException, IllegalOperationException, ItemNotFoundException, StorageException
  {
    List<String> shadowPaths = transformMaster2Shadow(item.getPath());

    if (shadowPaths != null && !shadowPaths.isEmpty()) {
      ResourceStoreRequest req = new ResourceStoreRequest(shadowPaths.get(0));
      req.getRequestContext().setParentContext(item.getItemContext());
      try {
        deleteItem(false, req);
      }
      catch (ItemNotFoundException e) {
        // NEXUS-5673: just ignore it silently, this might happen when
        // link to be deleted was not found in shadow (like a parent folder was deleted
        // or M2 checksum file in master)
        // simply ignoring this is okay, as our initial goal is to lessen log spam.
        // If parent cleanup fails below, thats fine too, superclass with handle the
        // exception. This catch here is merely to logic continue with empty parent cleanup
      }

      // we need to clean up empty shadow parent directories
      String parentPath =
          req.getRequestPath().substring(0, req.getRequestPath().lastIndexOf(item.getName()));
      ResourceStoreRequest parentRequest = new ResourceStoreRequest(parentPath);

      while (parentRequest != null) {
        StorageItem parentItem = null;
        parentItem = this.retrieveItem(false, parentRequest);

        // this should be a collection Item
        if (StorageCollectionItem.class.isInstance(parentItem)) {
          StorageCollectionItem parentCollectionItem = (StorageCollectionItem) parentItem;
          try {
            if (parentCollectionItem.list().size() == 0) {
              deleteItem(false, parentRequest);
              parentRequest = new ResourceStoreRequest(parentCollectionItem.getParentPath());
            }
            else {
              // exit loop
              parentRequest = null;
            }
          }
          catch (AccessDeniedException e) {
            this.log.debug(
                "Failed to delete shadow parent: " + this.getId() + ":" + parentItem.getPath()
                    + " Access Denied", e);
            // exit loop
            parentRequest = null;
          }
          catch (NoSuchResourceStoreException e) {
            this.log.debug(
                "Failed to delete shadow parent: " + this.getId() + ":" + parentItem.getPath()
                    + " does not exist", e);
            // exit loop
            parentRequest = null;
          }
        }
        else {
          this.log.debug("ExpectedCollectionItem, found: " + parentItem.getClass() + ", ignoring.");
        }
      }
    }
  }

  /**
   * Gets the shadow path from master path. If path is not transformable, return null.
   *
   * @param path the path
   * @return the shadow path
   */
  protected abstract List<String> transformMaster2Shadow(String path);

  @Override
  protected StorageItem doRetrieveItem(final ResourceStoreRequest request)
      throws IllegalOperationException, ItemNotFoundException, StorageException
  {
    StorageItem result = null;

    try {
      result = super.doRetrieveItem(request);

      return result;
    }
    catch (ItemNotFoundException e) {
      // if it is thrown by super.doRetrieveItem()
      List<String> transformedPaths = transformShadow2Master(request.getRequestPath());

      if (transformedPaths == null || transformedPaths.isEmpty()) {
        throw new ItemNotFoundException(reasonFor(request, this,
            "Request path %s is not transformable to master.", request.getRequestPath()));
      }

      for (String transformedPath : transformedPaths) {
        // delegate the call to the master
        request.pushRequestPath(transformedPath);
        try {
          result = doRetrieveItemFromMaster(request);

          // try to create link on the fly
          try {
            StorageLinkItem link = createLink(result);

            if (link != null) {
              return link;
            }
            else {
              // fallback to result, but will not happen, see above
              return result;
            }
          }
          catch (Exception e1) {
            // fallback to result, but will not happen, see above
            return result;
          }
        }
        catch (ItemNotFoundException ex) {
          // neglect, we might try another transformed path
        }
        finally {
          request.popRequestPath();
        }
      }

      throw new ItemNotFoundException(request, this);
    }
  }

  /**
   * Gets the master path from shadow path. If path is not transformable, return null.
   *
   * @param path the path
   * @return the master path
   */
  protected abstract List<String> transformShadow2Master(String path);
}
TOP

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

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.