Package org.sonatype.nexus.proxy.storage.remote.httpclient

Source Code of org.sonatype.nexus.proxy.storage.remote.httpclient.HttpClientRemoteStorageTest$RequesterRunnable

/*
* 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.storage.remote.httpclient;

import org.sonatype.nexus.SystemStatus;
import org.sonatype.nexus.internal.httpclient.HttpClientFactoryImpl;
import org.sonatype.nexus.internal.httpclient.PoolingClientConnectionManagerMBeanInstaller;
import org.sonatype.nexus.mime.MimeSupport;
import org.sonatype.nexus.proxy.RemoteStorageException;
import org.sonatype.nexus.proxy.RemoteStorageTransportOverloadedException;
import org.sonatype.nexus.proxy.ResourceStoreRequest;
import org.sonatype.nexus.proxy.repository.DefaultRemoteConnectionSettings;
import org.sonatype.nexus.proxy.repository.ProxyRepository;
import org.sonatype.nexus.proxy.repository.RemoteProxySettings;
import org.sonatype.nexus.proxy.storage.remote.DefaultRemoteStorageContext;
import org.sonatype.nexus.proxy.storage.remote.RemoteItemNotFoundException;
import org.sonatype.nexus.proxy.storage.remote.RemoteStorageContext;
import org.sonatype.nexus.proxy.storage.remote.http.QueryStringBuilder;
import org.sonatype.sisu.goodies.common.Time;
import org.sonatype.sisu.goodies.eventbus.EventBus;
import org.sonatype.sisu.litmus.testsupport.TestSupport;
import org.sonatype.tests.http.server.fluent.Behaviours;
import org.sonatype.tests.http.server.fluent.Server;

import static org.hamcrest.MatcherAssert.assertThat;

import static org.hamcrest.Matchers.is;
import org.apache.http.message.BasicHeader;
import com.google.inject.util.Providers;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
* {@link HttpClientRemoteStorage} UTs.
*
* @since 2.2
*/
public class HttpClientRemoteStorageTest
    extends TestSupport
{

  @Rule
  public ExpectedException thrown = ExpectedException.none();

  /**
   * When retrieving an item with a path that ends with a "/" and without a query, an RemoteItemNotFoundException
   * with
   * a message that a collection could not be downloaded over HTTP should be thrown.
   */
  @Test
  public void retrieveCollectionWhenPathEndsWithSlashAndNoQuery()
      throws Exception
  {
    final HttpClientRemoteStorage underTest =
        new HttpClientRemoteStorage(Providers.of(mock(SystemStatus.class)),
            mock(MimeSupport.class), mock(QueryStringBuilder.class), mock(HttpClientManager.class));
    final ProxyRepository proxyMock = mock(ProxyRepository.class);
    when(proxyMock.getId()).thenReturn("id");
    when(proxyMock.getRemoteStorageContext()).thenReturn(new DefaultRemoteStorageContext(null));

    thrown.expect(RemoteItemNotFoundException.class);
    thrown.expectMessage("not found in remote storage of repository");

    underTest.retrieveItem(proxyMock, new ResourceStoreRequest("bar/"), "http://foo.com");
  }

  /**
   * When retrieving an item with a path that ends with a "/" and a query string that ends with a "/", an
   * RemoteItemNotFoundException with a message that a collection could not be downloaded over HTTP should be thrown.
   */
  @Test
  public void retrieveCollectionWhenPathEndsWithSlashAndQueryEndsWithSlash()
      throws Exception
  {
    final HttpClientRemoteStorage underTest =
        new HttpClientRemoteStorage(Providers.of(mock(SystemStatus.class)),
            mock(MimeSupport.class), mock(QueryStringBuilder.class), mock(HttpClientManager.class));
    final ProxyRepository proxyMock = mock(ProxyRepository.class);
    when(proxyMock.getId()).thenReturn("id");
    when(proxyMock.getRemoteStorageContext()).thenReturn(new DefaultRemoteStorageContext(null));

    thrown.expect(RemoteItemNotFoundException.class);
    thrown.expectMessage("not found in remote storage of repository");

    underTest.retrieveItem(proxyMock, new ResourceStoreRequest("bar/?param=x/"),
        "http://foo.com");
  }

  /**
   * When retrieving an item with a path that ends with a "/" and a query string that does not end with a "/", an
   * RemoteItemNotFoundException with a message that a collection could not be downloaded over HTTP should be thrown.
   */
  @Test
  public void retrieveCollectionWhenPathEndsWithSlashAndQueryDoesNotEndWithSlash()
      throws Exception
  {
    final HttpClientRemoteStorage underTest =
        new HttpClientRemoteStorage(Providers.of(mock(SystemStatus.class)),
            mock(MimeSupport.class), mock(QueryStringBuilder.class), mock(HttpClientManager.class));
    final ProxyRepository proxyMock = mock(ProxyRepository.class);
    when(proxyMock.getId()).thenReturn("id");
    when(proxyMock.getRemoteStorageContext()).thenReturn(new DefaultRemoteStorageContext(null));

    thrown.expect(RemoteItemNotFoundException.class);
    thrown.expectMessage("not found in remote storage of repository");

    underTest.retrieveItem(proxyMock, new ResourceStoreRequest("bar/?param=x"),
        "http://foo.com");
  }

  /**
   * When retrieving an item with a path that does not end with a "/" and a query string that does not end with a
   * "/",
   * no exception should be thrown.
   */
  @Test
  public void retrieveCollectionWhenPathDoesNotEndWithSlashAndQueryDoesNotEndWithSlash()
      throws Exception
  {
    final HttpClientRemoteStorage underTest =
        new HttpClientRemoteStorage(Providers.of(mock(SystemStatus.class)),
            mock(MimeSupport.class), mock(QueryStringBuilder.class), mock(HttpClientManager.class))
        {
          @Override
          HttpResponse executeRequest(final ProxyRepository repository, final ResourceStoreRequest request,
                                      final HttpUriRequest httpRequest, final String baseUrl, final boolean contentRelated)
              throws RemoteStorageException
          {
            final HttpResponse httpResponse = mock(HttpResponse.class);
            final StatusLine statusLine = mock(StatusLine.class);
            when(httpResponse.getStatusLine()).thenReturn(statusLine);
            when(statusLine.getStatusCode()).thenReturn(200);
            when(httpResponse.getEntity()).thenReturn(mock(HttpEntity.class));
            return httpResponse;
          }
        };

    final ProxyRepository proxyMock = mock(ProxyRepository.class);
    when(proxyMock.getId()).thenReturn("foo");
    when(proxyMock.getRemoteStorageContext()).thenReturn(new DefaultRemoteStorageContext(null));

    underTest.retrieveItem(proxyMock, new ResourceStoreRequest("bar?param=x"), "http://foo.com");
  }

  /**
   * When pool is depleted, and underlying HttpClient4x cannot fulfil request due to
   * {@link ConnectionPoolTimeoutException}, the {@link HttpClientRemoteStorage} should throw a new exception,
   * instance of {@link RemoteStorageTransportOverloadedException} to mark to core that transport is overloaded.
   */
  @Test
  public void emitProperExceptionOnPoolDepletion()
      throws Exception
  {
    setParameters();
    HttpClientFactoryImpl httpClientFactory = null;
    try {
      // the foreplay: setting up
      final RemoteStorageContext globalRemoteStorageContext = new DefaultRemoteStorageContext(null);
      final DefaultRemoteConnectionSettings rcs = new DefaultRemoteConnectionSettings();
      rcs.setConnectionTimeout(86400000);
      globalRemoteStorageContext.setRemoteConnectionSettings(new DefaultRemoteConnectionSettings());
      globalRemoteStorageContext.setRemoteProxySettings(mock(RemoteProxySettings.class));

      // real provider and initializing it with NexusStarted event
      httpClientFactory = new HttpClientFactoryImpl(
          Providers.of(mock(SystemStatus.class)),
          Providers.of(globalRemoteStorageContext),
          mock(EventBus.class),
          mock(PoolingClientConnectionManagerMBeanInstaller.class),
          null
      );

      // the RRS instance we test
      final HttpClientRemoteStorage underTest =
          new HttpClientRemoteStorage(Providers.of(mock(SystemStatus.class)),
              mock(MimeSupport.class), mock(QueryStringBuilder.class), new HttpClientManagerImpl(httpClientFactory));

      // a mock proxy repository with some mocks to make RRS work
      final RemoteStorageContext proxyContext = new DefaultRemoteStorageContext(globalRemoteStorageContext);
      final ProxyRepository repository = mock(ProxyRepository.class);
      when(repository.getId()).thenReturn("foo");
      when(repository.getName()).thenReturn("foo");
      when(repository.getRemoteStorageContext()).thenReturn(proxyContext);

      // a mock remote server that will simply "hang" to occupy the request socket
      final Server server =
          Server.withPort(0).serve("/").withBehaviours(Behaviours.pause(Time.days(1))).start();
      // the URL we will try to connect to
      final String url = "http://foo.com:" + server.getPort() + "/foo/bar.jar";
      // the requesting logic packed as Runnable
      final Runnable request = new RequesterRunnable(underTest, repository, url);
      try {
        // we fire 1st request as a Thread, this thread will be blocked as Server will simply "pause"
        // this also means, that connection stays leased from pool, and since pool size is 1, we
        // intentionally depleted the connection pool (reached max connection count)
        final Thread blockedThread = new Thread(request);
        blockedThread.start();

        // give some time to thread above
        Thread.sleep(200);

        try {
          // in current thread we try to establish 2nd connection
          // this here will need to fail, as connection pool is depleted
          // ConnectionPoolTimeoutException should be thrown by HC4
          // that RRS "repackages" into RemoteStorageTransportOverloadedException
          request.run();

          // fail if no exception
          Assert.fail("RemoteStorageTransportOverloadedException expected!");
        }
        catch (IllegalStateException e) {
          Assert.assertNotNull("We except the cause be RemoteStorageTransportOverloadedException!",
              e.getCause());
          Assert.assertEquals(RemoteStorageTransportOverloadedException.class, e.getCause().getClass());
        }
      }
      finally {
        server.stop();
      }
    }
    finally {
      if (httpClientFactory != null) {
        httpClientFactory.shutdown();
      }
      unsetParameters();
    }
  }

  /**
   * When checking for repository remote availability (newerThen is 0), we should neglect response carried
   * last-modified time as we are not interested in it.
   *
   * @see https://issues.sonatype.org/browse/NEXUS-6701
   */
  @Test
  public void checkRepositoryRemoteAvailabilityNeglectLastModified()
      throws Exception
  {
    final HttpClientRemoteStorage underTest =
        new HttpClientRemoteStorage(Providers.of(mock(SystemStatus.class)),
            mock(MimeSupport.class), mock(QueryStringBuilder.class), mock(HttpClientManager.class))
        {
          @Override
          HttpResponse executeRequest(final ProxyRepository repository, final ResourceStoreRequest request,
                                      final HttpUriRequest httpRequest, final String baseUrl, final boolean contentRelated)
              throws RemoteStorageException
          {
            final HttpResponse httpResponse = mock(HttpResponse.class);
            when(httpResponse.getFirstHeader("last-modified")).thenReturn(new BasicHeader("last-modified", "Thu, 01 Jan 1970 00:00:00 GMT"));
            final StatusLine statusLine = mock(StatusLine.class);
            when(httpResponse.getStatusLine()).thenReturn(statusLine);
            when(statusLine.getStatusCode()).thenReturn(200);
            when(httpResponse.getEntity()).thenReturn(mock(HttpEntity.class));
            return httpResponse;
          }
        };

    final ProxyRepository proxyMock = mock(ProxyRepository.class);
    when(proxyMock.getId()).thenReturn("foo");
    when(proxyMock.getRemoteUrl()).thenReturn("http://repo1.maven.org/maven2/");
    when(proxyMock.getRemoteStorageContext()).thenReturn(new DefaultRemoteStorageContext(null));
   
    assertThat(underTest.checkRemoteAvailability(0, proxyMock, new ResourceStoreRequest("/"), false), is(true));
    assertThat(underTest.checkRemoteAvailability(System.currentTimeMillis(), proxyMock, new ResourceStoreRequest("/"), false), is(false));
  }

  protected void setParameters() {
    System.setProperty("nexus.apacheHttpClient4x.connectionPoolMaxSize", "1");
    System.setProperty("nexus.apacheHttpClient4x.connectionPoolSize", "1");
    System.setProperty("nexus.apacheHttpClient4x.connectionPoolKeepalive", "86400000");
    System.setProperty("nexus.apacheHttpClient4x.connectionPoolTimeout", "100");
  }

  protected void unsetParameters() {
    System.clearProperty("nexus.apacheHttpClient4x.connectionPoolMaxSize");
    System.clearProperty("nexus.apacheHttpClient4x.connectionPoolSize");
    System.clearProperty("nexus.apacheHttpClient4x.connectionPoolKeepalive");
    System.clearProperty("nexus.apacheHttpClient4x.connectionPoolTimeout");
  }

  private static class RequesterRunnable
      implements Runnable
  {
    private final HttpClientRemoteStorage underTest;

    private final ProxyRepository repository;

    private final String url;

    private RequesterRunnable(HttpClientRemoteStorage underTest, ProxyRepository repository, String url) {
      this.underTest = underTest;
      this.repository = repository;
      this.url = url;
    }

    @Override
    public void run() {
      try {
        underTest.retrieveItem(repository, new ResourceStoreRequest("foo/bar.jar"), url);
      }
      catch (Exception e) {
        throw new IllegalStateException("Failed!", e);
      }
    }
  }
}
TOP

Related Classes of org.sonatype.nexus.proxy.storage.remote.httpclient.HttpClientRemoteStorageTest$RequesterRunnable

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.