Author: olamy
Date: Thu Nov  7 05:21:39 2013
New Revision: 1539520

URL: http://svn.apache.org/r1539520
Log:
use zero copy transfer for huge maven index

Modified:
    
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/pom.xml
    
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/main/java/org/apache/archiva/scheduler/indexing/DownloadRemoteIndexTask.java
    
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/test/resources/log4j2-test.xml
    archiva/trunk/pom.xml

Modified: 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/pom.xml
URL: 
http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/pom.xml?rev=1539520&r1=1539519&r2=1539520&view=diff
==============================================================================
--- 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/pom.xml
 (original)
+++ 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/pom.xml
 Thu Nov  7 05:21:39 2013
@@ -72,6 +72,10 @@
       </exclusions>
     </dependency>
     <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpasyncclient</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
     </dependency>
@@ -79,6 +83,7 @@
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
+
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-server</artifactId>

Modified: 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/main/java/org/apache/archiva/scheduler/indexing/DownloadRemoteIndexTask.java
URL: 
http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/main/java/org/apache/archiva/scheduler/indexing/DownloadRemoteIndexTask.java?rev=1539520&r1=1539519&r2=1539520&view=diff
==============================================================================
--- 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/main/java/org/apache/archiva/scheduler/indexing/DownloadRemoteIndexTask.java
 (original)
+++ 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/main/java/org/apache/archiva/scheduler/indexing/DownloadRemoteIndexTask.java
 Thu Nov  7 05:21:39 2013
@@ -26,7 +26,29 @@ import org.apache.archiva.proxy.common.W
 import org.apache.archiva.proxy.common.WagonFactoryException;
 import org.apache.archiva.proxy.common.WagonFactoryRequest;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.time.StopWatch;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.client.methods.ZeroCopyConsumer;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.HttpContext;
 import org.apache.maven.index.context.IndexingContext;
 import org.apache.maven.index.updater.IndexUpdateRequest;
 import org.apache.maven.index.updater.IndexUpdater;
@@ -56,8 +78,13 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.security.Principal;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * @author Olivier Lamy
@@ -170,6 +197,23 @@ public class DownloadRemoteIndexTask
             wagon.connect( new Repository( this.remoteRepository.getId(), 
baseIndexUrl ), authenticationInfo,
                            proxyInfo );
 
+            //---------------------------------------------
+
+            HttpAsyncClientBuilder builder = HttpAsyncClientBuilder.create();
+
+            if ( this.networkProxy != null )
+            {
+                HttpHost httpHost = new HttpHost( this.networkProxy.getHost(), 
this.networkProxy.getPort() );
+                builder = builder.setProxy( httpHost );
+            }
+
+            if ( this.remoteRepository.getUserName() != null )
+            {
+                BasicCredentialsProvider basicCredentialsProvider = new 
BasicCredentialsProvider();
+                basicCredentialsProvider.setCredentials( AuthScope.ANY, new 
UsernamePasswordCredentials(
+                    this.remoteRepository.getUserName(), 
this.remoteRepository.getPassword() ) );
+            }
+
             File indexDirectory = indexingContext.getIndexDirectoryFile();
             if ( !indexDirectory.exists() )
             {
@@ -178,6 +222,12 @@ public class DownloadRemoteIndexTask
 
             ResourceFetcher resourceFetcher =
                 new WagonResourceFetcher( log, tempIndexDirectory, wagon, 
remoteRepository );
+            CloseableHttpAsyncClient closeableHttpAsyncClient = 
builder.build();
+            closeableHttpAsyncClient.start();
+            resourceFetcher =
+                new ZeroCopyResourceFetcher( log, tempIndexDirectory, 
remoteRepository, closeableHttpAsyncClient,
+                                             baseIndexUrl );
+
             IndexUpdateRequest request = new IndexUpdateRequest( 
indexingContext, resourceFetcher );
             request.setForceFullUpdate( this.fullDownload );
             request.setLocalIndexCacheDir( indexCacheDirectory );
@@ -360,29 +410,183 @@ public class DownloadRemoteIndexTask
             }
         }
 
-        // FIXME remove crappy copy/paste
-        protected String addParameters( String path, RemoteRepository 
remoteRepository )
+    }
+
+    private static class ZeroCopyResourceFetcher
+        implements ResourceFetcher
+    {
+
+        Logger log;
+
+        File tempIndexDirectory;
+
+        final RemoteRepository remoteRepository;
+
+        CloseableHttpAsyncClient httpclient;
+
+        String baseIndexUrl;
+
+        private ZeroCopyResourceFetcher( Logger log, File tempIndexDirectory, 
RemoteRepository remoteRepository,
+                                         CloseableHttpAsyncClient httpclient, 
String baseIndexUrl )
+        {
+            this.log = log;
+            this.tempIndexDirectory = tempIndexDirectory;
+            this.remoteRepository = remoteRepository;
+            this.httpclient = httpclient;
+            this.baseIndexUrl = baseIndexUrl;
+        }
+
+        public void connect( String id, String url )
+            throws IOException
         {
-            if ( remoteRepository.getExtraParameters().isEmpty() )
+            //no op
+        }
+
+        public void disconnect()
+            throws IOException
+        {
+            // no op
+        }
+
+        public InputStream retrieve( final String name )
+            throws IOException, FileNotFoundException
+        {
+
+            log.info( "index update retrieve file, name:{}", name );
+            File file = new File( tempIndexDirectory, name );
+            if ( file.exists() )
             {
-                return path;
+                file.delete();
             }
+            file.deleteOnExit();
+
+            ZeroCopyConsumer<File> consumer = new ZeroCopyConsumer<File>( file 
)
+            {
 
-            boolean question = false;
+                @Override
+                protected File process( final HttpResponse response, final 
File file, final ContentType contentType )
+                    throws Exception
+                {
+                    if ( response.getStatusLine().getStatusCode() != 
HttpStatus.SC_OK )
+                    {
+                        throw new ClientProtocolException( "Upload failed: " + 
response.getStatusLine() );
+                    }
+                    return file;
+                }
 
-            StringBuilder res = new StringBuilder( path == null ? "" : path );
+                @Override
+                protected void onContentReceived( ContentDecoder decoder, 
IOControl ioctrl )
+                    throws IOException
+                {
+                    log.debug( "onContentReceived" );
+                    super.onContentReceived( decoder, ioctrl );
+                }
+            };
+            URL targetUrl = new URL( this.remoteRepository.getUrl() );
+            final HttpHost targetHost = new HttpHost( targetUrl.getHost(), 
targetUrl.getPort() );
 
-            for ( Map.Entry<String, String> entry : 
remoteRepository.getExtraParameters().entrySet() )
+            Future<File> httpResponseFuture = httpclient.execute( new 
HttpAsyncRequestProducer()
             {
-                if ( !question )
+                @Override
+                public HttpHost getTarget()
+                {
+                    return targetHost;
+                }
+
+                @Override
+                public HttpRequest generateRequest()
+                    throws IOException, HttpException
+                {
+                    StringBuilder url = new StringBuilder( baseIndexUrl );
+                    if ( !StringUtils.endsWith( baseIndexUrl, "/" ) )
+                    {
+                        url.append( '/' );
+                    }
+                    HttpGet httpGet = new HttpGet( url.append( addParameters( 
name, remoteRepository ) ).toString() );
+                    return httpGet;
+                }
+
+                @Override
+                public void produceContent( ContentEncoder encoder, IOControl 
ioctrl )
+                    throws IOException
+                {
+                    // no op
+                }
+
+                @Override
+                public void requestCompleted( HttpContext context )
+                {
+                    // no op
+                }
+
+                @Override
+                public void failed( Exception ex )
+                {
+                    log.error( "http request failed", ex );
+                }
+
+                @Override
+                public boolean isRepeatable()
+                {
+                    return true;
+                }
+
+                @Override
+                public void resetRequest()
+                    throws IOException
+                {
+                    // no op
+                }
+
+                @Override
+                public void close()
+                    throws IOException
                 {
-                    res.append( '?' ).append( entry.getKey() ).append( '=' 
).append( entry.getValue() );
+                    // no op
                 }
+            }, consumer, null );
+            try
+            {
+                file = httpResponseFuture.get( 
this.remoteRepository.getTimeout(), TimeUnit.SECONDS );
+            }
+            catch ( InterruptedException e )
+            {
+                throw new IOException( e.getMessage(), e );
+            }
+            catch ( ExecutionException e )
+            {
+                throw new IOException( e.getMessage(), e );
+            }
+            catch ( TimeoutException e )
+            {
+                throw new IOException( e.getMessage(), e );
             }
+            return new FileInputStream( file );
+        }
 
-            return res.toString();
+    }
+
+    // FIXME remove crappy copy/paste
+    protected static String addParameters( String path, RemoteRepository 
remoteRepository )
+    {
+        if ( remoteRepository.getExtraParameters().isEmpty() )
+        {
+            return path;
+        }
+
+        boolean question = false;
+
+        StringBuilder res = new StringBuilder( path == null ? "" : path );
+
+        for ( Map.Entry<String, String> entry : 
remoteRepository.getExtraParameters().entrySet() )
+        {
+            if ( !question )
+            {
+                res.append( '?' ).append( entry.getKey() ).append( '=' 
).append( entry.getValue() );
+            }
         }
 
+        return res.toString();
     }
 
 

Modified: 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/test/resources/log4j2-test.xml
URL: 
http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/test/resources/log4j2-test.xml?rev=1539520&r1=1539519&r2=1539520&view=diff
==============================================================================
--- 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/test/resources/log4j2-test.xml
 (original)
+++ 
archiva/trunk/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/src/test/resources/log4j2-test.xml
 Thu Nov  7 05:21:39 2013
@@ -30,7 +30,9 @@
     <logger name="org.springframework" level="error"/>
 
     <logger name="org.apache.archiva.scheduler.indexing" level="debug"/>
-
+    <!--
+    <logger name="org.apache.http" level="debug"/>
+    -->
     <root level="info">
       <appender-ref ref="console"/>
     </root>

Modified: archiva/trunk/pom.xml
URL: 
http://svn.apache.org/viewvc/archiva/trunk/pom.xml?rev=1539520&r1=1539519&r2=1539520&view=diff
==============================================================================
--- archiva/trunk/pom.xml (original)
+++ archiva/trunk/pom.xml Thu Nov  7 05:21:39 2013
@@ -1288,6 +1288,13 @@
           </exclusion>
         </exclusions>
       </dependency>
+
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpasyncclient</artifactId>
+        <version>4.0</version>
+      </dependency>
+
       <dependency>
         <groupId>org.apache.maven.wagon</groupId>
         <artifactId>wagon-provider-api</artifactId>


Reply via email to