Package org.sonatype.nexus.proxy.maven.maven2

Source Code of org.sonatype.nexus.proxy.maven.maven2.M2Repository

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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.enterprise.inject.Typed;
import javax.inject.Inject;
import javax.inject.Named;

import org.sonatype.nexus.configuration.Configurator;
import org.sonatype.nexus.configuration.model.CRepository;
import org.sonatype.nexus.configuration.model.CRepositoryExternalConfigurationHolderFactory;
import org.sonatype.nexus.proxy.IllegalOperationException;
import org.sonatype.nexus.proxy.IllegalRequestException;
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.access.AccessManager;
import org.sonatype.nexus.proxy.access.Action;
import org.sonatype.nexus.proxy.item.AbstractStorageItem;
import org.sonatype.nexus.proxy.item.ByteArrayContentLocator;
import org.sonatype.nexus.proxy.item.ContentLocator;
import org.sonatype.nexus.proxy.item.DefaultStorageFileItem;
import org.sonatype.nexus.proxy.item.StorageFileItem;
import org.sonatype.nexus.proxy.item.StorageItem;
import org.sonatype.nexus.proxy.maven.AbstractMavenRepository;
import org.sonatype.nexus.proxy.maven.MavenRepositoryMetadataManager;
import org.sonatype.nexus.proxy.maven.RepositoryPolicy;
import org.sonatype.nexus.proxy.maven.gav.Gav;
import org.sonatype.nexus.proxy.maven.gav.GavCalculator;
import org.sonatype.nexus.proxy.maven.gav.M2ArtifactRecognizer;
import org.sonatype.nexus.proxy.maven.metadata.operations.MetadataBuilder;
import org.sonatype.nexus.proxy.maven.metadata.operations.ModelVersionUtility;
import org.sonatype.nexus.proxy.maven.metadata.operations.ModelVersionUtility.Version;
import org.sonatype.nexus.proxy.registry.ContentClass;
import org.sonatype.nexus.proxy.repository.Repository;
import org.sonatype.nexus.proxy.repository.RepositoryMetadataManager;
import org.sonatype.nexus.util.AlphanumComparator;
import org.sonatype.nexus.util.DigesterUtils;
import org.sonatype.nexus.util.io.StreamSupport;

import org.apache.maven.artifact.repository.metadata.Metadata;
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader;
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.sisu.Description;

import static com.google.common.base.Preconditions.checkNotNull;

/**
* The default M2Repository.
*
* @author cstamas
*/
@Named(M2Repository.ID)
@Typed(Repository.class)
@Description("Maven2 Repository")
public class M2Repository
    extends AbstractMavenRepository
{
  /**
   * This "mimics" the @Named("maven2")
   */
  public static final String ID = Maven2ContentClass.ID;

  private final ContentClass contentClass;

  /**
   * The GAV Calculator.
   */
  private final GavCalculator gavCalculator;

  private final M2RepositoryConfigurator m2RepositoryConfigurator;

  private final MavenRepositoryMetadataManager mavenRepositoryMetadataManager;

  @Inject
  public M2Repository(final @Named(Maven2ContentClass.ID) ContentClass contentClass,
                      final @Named("maven2") GavCalculator gavCalculator,
                      final M2RepositoryConfigurator m2RepositoryConfigurator)
  {
    this.contentClass = checkNotNull(contentClass);
    this.gavCalculator = checkNotNull(gavCalculator);
    this.m2RepositoryConfigurator = checkNotNull(m2RepositoryConfigurator);
    this.mavenRepositoryMetadataManager = new MavenRepositoryMetadataManager(this);
  }

  @Override
  public RepositoryMetadataManager getRepositoryMetadataManager() {
    return mavenRepositoryMetadataManager;
  }

  @Override
  protected M2RepositoryConfiguration getExternalConfiguration(boolean forWrite) {
    return (M2RepositoryConfiguration) super.getExternalConfiguration(forWrite);
  }

  @Override
  protected CRepositoryExternalConfigurationHolderFactory<?> getExternalConfigurationHolderFactory() {
    return new CRepositoryExternalConfigurationHolderFactory<M2RepositoryConfiguration>()
    {
      public M2RepositoryConfiguration createExternalConfigurationHolder(CRepository config) {
        return new M2RepositoryConfiguration((Xpp3Dom) config.getExternalConfiguration());
      }
    };
  }

  @Override
  public ContentClass getRepositoryContentClass() {
    return contentClass;
  }

  @Override
  public GavCalculator getGavCalculator() {
    return gavCalculator;
  }

  @Override
  protected Configurator getConfigurator() {
    return m2RepositoryConfigurator;
  }

  @Override
  public boolean isMavenMetadataPath(String path) {
    return M2ArtifactRecognizer.isMetadata(path);
  }

  @Override
  public boolean isMavenArtifactChecksumPath(String path) {
    return M2ArtifactRecognizer.isChecksum(path);
  }

  /**
   * Should serve by policies.
   *
   * @param request the request
   * @return true, if successful
   */
  @Override
  public boolean shouldServeByPolicies(ResourceStoreRequest request) {
    if (M2ArtifactRecognizer.isMetadata(request.getRequestPath())) {
      if (M2ArtifactRecognizer.isSnapshot(request.getRequestPath())) {
        return RepositoryPolicy.SNAPSHOT.equals(getRepositoryPolicy());
      }
      else {
        // metadatas goes always
        return true;
      }
    }

    // we are using Gav to test the path
    final Gav gav = getGavCalculator().pathToGav(request.getRequestPath());

    if (gav == null) {
      return true;
    }
    else {
      if (gav.isSnapshot()) {
        // snapshots goes if enabled
        return RepositoryPolicy.SNAPSHOT.equals(getRepositoryPolicy());
      }
      else {
        return RepositoryPolicy.RELEASE.equals(getRepositoryPolicy());
      }
    }
  }

  @Override
  public AbstractStorageItem doCacheItem(AbstractStorageItem item)
      throws LocalStorageException
  {
    // if the item is file, is M2 repository metadata and this repo is release-only or snapshot-only
    if (isCleanseRepositoryMetadata() && item instanceof StorageFileItem
        && M2ArtifactRecognizer.isMetadata(item.getPath())) {
      StorageFileItem mdFile = (StorageFileItem) item;
      ByteArrayInputStream backup = null;
      ByteArrayOutputStream backup1 = new ByteArrayOutputStream();
      try {
        // remote item is not reusable, and we usually cache remote stuff locally
        try (final InputStream orig = mdFile.getInputStream()) {
          StreamSupport.copy(orig, backup1, StreamSupport.BUFFER_SIZE);
        }
        backup = new ByteArrayInputStream(backup1.toByteArray());

        // Metadata is small, let's do it in memory
        MetadataXpp3Reader metadataReader = new MetadataXpp3Reader();
        InputStreamReader isr = new InputStreamReader(backup);
        Metadata imd = metadataReader.read(isr);

        // and fix it
        imd = cleanseMetadataForRepository(RepositoryPolicy.SNAPSHOT.equals(getRepositoryPolicy()), imd);

        // serialize and swap the new metadata
        MetadataXpp3Writer metadataWriter = new MetadataXpp3Writer();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter(bos);
        metadataWriter.write(osw, imd);
        mdFile.setContentLocator(new ByteArrayContentLocator(bos.toByteArray(), mdFile.getMimeType()));
      }
      catch (Exception e) {
        log.error("Exception during repository metadata cleansing.", e);

        if (backup != null) {
          // get backup and continue operation
          backup.reset();
          mdFile.setContentLocator(new ByteArrayContentLocator(backup1.toByteArray(), mdFile.getMimeType()));
        }
      }
    }

    return super.doCacheItem(item);
  }

  @Override
  protected boolean isOld(StorageItem item) {
    if (M2ArtifactRecognizer.isMetadata(item.getPath())) {
      return isOld(getMetadataMaxAge(), item);
    }
    if (M2ArtifactRecognizer.isSnapshot(item.getPath())) {
      return isOld(getArtifactMaxAge(), item);
    }

    // we are using Gav to test the path
    final Gav gav = getGavCalculator().pathToGav(item.getPath());

    if (gav == null) {
      // this is not an artifact, it is just any "file"
      return super.isOld(item);
    }
    // it is a release
    return isOld(getArtifactMaxAge(), item);
  }

  protected Metadata cleanseMetadataForRepository(boolean snapshot, Metadata metadata) {
    // remove base versions not belonging here
    List<String> versions = metadata.getVersioning().getVersions();
    for (Iterator<String> iversion = versions.iterator(); iversion.hasNext(); ) {
      // if we need snapshots and the version is not snapshot, or
      // if we need releases and the version is snapshot
      if ((snapshot && !Gav.isSnapshot(iversion.next()))
          || (!snapshot && Gav.isSnapshot(iversion.next()))) {
        iversion.remove();
      }
    }

    metadata.getVersioning().setLatest(getLatestVersion(metadata.getVersioning().getVersions()));
    if (snapshot) {
      metadata.getVersioning().setRelease(null);
    }
    else {
      metadata.getVersioning().setRelease(metadata.getVersioning().getLatest());
    }
    return metadata;
  }

  public String getLatestVersion(List<String> versions) {
    Collections.sort(versions, new AlphanumComparator());

    return versions.get(versions.size() - 1);
  }

  @Override
  protected void enforceWritePolicy(ResourceStoreRequest request, Action action)
      throws IllegalRequestException
  {
    // allow updating of metadata
    // we also need to allow updating snapshots
    if (!M2ArtifactRecognizer.isMetadata(request.getRequestPath())
        && !M2ArtifactRecognizer.isSnapshot(request.getRequestPath())) {
      super.enforceWritePolicy(request, action);
    }
  }

  @Override
  protected StorageItem doRetrieveItem(ResourceStoreRequest request)
      throws IllegalOperationException, ItemNotFoundException, StorageException
  {
    // we do all this mockery (NEXUS-4218 and NEXUS-4243) ONLY when we are sure
    // that we deal with REMOTE REQUEST INITIATED BY OLD 2.x MAVEN and nothing else.
    // Before this fix, the code was executed whenever
    // "!ModelVersionUtility.LATEST_MODEL_VERSION.equals( userSupportedVersion ) )" was true
    // and it was true even for userSupportedVersion being null (ie. not user agent string supplied!)!!!
    final boolean remoteCall = request.getRequestContext().containsKey(AccessManager.REQUEST_REMOTE_ADDRESS);
    final String userAgent = (String) request.getRequestContext().get(AccessManager.REQUEST_AGENT);

    if (remoteCall && null != userAgent) {
      final Version userSupportedVersion = getClientSupportedVersion(userAgent);

      // we still can make up our mind here, we do this only if we know: this request is about metadata,
      // the client's metadata version is known and it is not the latest one
      if (M2ArtifactRecognizer.isMetadata(request.getRequestPath()) && userSupportedVersion != null
          && !ModelVersionUtility.LATEST_MODEL_VERSION.equals(userSupportedVersion)) {
        // metadata checksum files are calculated and cached as side-effect
        // of doRetrieveMetadata.
        final StorageFileItem mdItem;
        if (M2ArtifactRecognizer.isChecksum(request.getRequestPath())) {
          String path = request.getRequestPath();
          if (request.getRequestPath().endsWith(".md5")) {
            path = path.substring(0, path.length() - 4);
          }
          else if (request.getRequestPath().endsWith(".sha1")) {
            path = path.substring(0, path.length() - 5);
          }
          // we have to keep original reqest's flags: localOnly and remoteOnly are strange ones, so
          // we do a hack here
          // second, since we initiate a request for different path within a context of this request,
          // we need to be careful about it
          ResourceStoreRequest mdRequest =
              new ResourceStoreRequest(path, request.isRequestLocalOnly(), request.isRequestRemoteOnly());
          mdRequest.getRequestContext().setParentContext(request.getRequestContext());

          mdItem = (StorageFileItem) super.retrieveItem(false, mdRequest);
        }
        else {
          mdItem = (StorageFileItem) super.doRetrieveItem(request);
        }

        try {
          Metadata metadata;
          try (final InputStream inputStream = mdItem.getInputStream()) {
            metadata = MetadataBuilder.read(inputStream);
          }

          Version requiredVersion = getClientSupportedVersion(userAgent);
          Version metadataVersion = ModelVersionUtility.getModelVersion(metadata);

          if (requiredVersion == null || requiredVersion.equals(metadataVersion)) {
            return super.doRetrieveItem(request);
          }

          ModelVersionUtility.setModelVersion(metadata, requiredVersion);

          ByteArrayOutputStream mdOutput = new ByteArrayOutputStream();

          MetadataBuilder.write(metadata, mdOutput);

          final byte[] content;
          if (M2ArtifactRecognizer.isChecksum(request.getRequestPath())) {
            String digest;
            if (request.getRequestPath().endsWith(".md5")) {
              digest = DigesterUtils.getMd5Digest(mdOutput.toByteArray());
            }
            else {
              digest = DigesterUtils.getSha1Digest(mdOutput.toByteArray());
            }
            content = (digest + '\n').getBytes("UTF-8");
          }
          else {
            content = mdOutput.toByteArray();
          }

          String mimeType =
              getMimeSupport().guessMimeTypeFromPath(getMimeRulesSource(), request.getRequestPath());
          ContentLocator contentLocator = new ByteArrayContentLocator(content, mimeType);

          DefaultStorageFileItem result =
              new DefaultStorageFileItem(this, request, true, false, contentLocator);
          result.setCreated(mdItem.getCreated());
          result.setModified(System.currentTimeMillis());
          return result;
        }
        catch (IOException e) {
          if (log.isDebugEnabled()) {
            log.error("Error parsing metadata, serving as retrieved", e);
          }
          else {
            log.error("Error parsing metadata, serving as retrieved: " + e.getMessage());
          }

          return super.doRetrieveItem(request);
        }
      }
    }

    return super.doRetrieveItem(request);
  }

  protected Version getClientSupportedVersion(String userAgent) {
    if (userAgent == null) {
      return null;
    }

    if (userAgent.startsWith("Apache Ivy")) {
      return Version.V100;
    }

    if (userAgent.startsWith("Java")) {
      return Version.V100;
    }

    if (userAgent.startsWith("Apache-Maven/2")) {
      return Version.V100;
    }

    return Version.V110;
  }

}
TOP

Related Classes of org.sonatype.nexus.proxy.maven.maven2.M2Repository

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.