/*-
 * Copyright (c) 2014 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fedoraproject.xmvn.resolver.impl;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;

import org.fedoraproject.xmvn.metadata.PackageMetadata;
import org.fedoraproject.xmvn.metadata.io.stax.MetadataStaxReader;

/**
 * @author Mikolaj Izdebski
 */
class MetadataReader
{
    private final ThreadPoolExecutor executor;

    static class DaemonFactory
        implements ThreadFactory
    {
        @Override
        public Thread newThread( Runnable runnable )
        {
            Thread thread = new Thread( runnable );
            thread.setName( MetadataReader.class.getCanonicalName() + ".worker" );
            thread.setDaemon( true );
            return thread;
        }
    }

    public MetadataReader()
    {
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
        int nThread = 2 * Math.min( Math.max( Runtime.getRuntime().availableProcessors(), 1 ), 8 );
        executor = new ThreadPoolExecutor( nThread, nThread, 1, TimeUnit.MINUTES, queue, new DaemonFactory() );
    }

    public List<PackageMetadata> readMetadata( List<String> depmapLocations )
    {
        List<Future<PackageMetadata>> futures = new ArrayList<>();

        for ( String pathString : depmapLocations )
        {
            Path path = Paths.get( pathString );

            if ( Files.isDirectory( path ) )
            {
                String flist[] = path.toFile().list();
                if ( flist != null )
                {
                    Arrays.sort( flist );
                    for ( String fragFilename : flist )
                        futures.add( executor.submit( new Task( path.resolve( fragFilename ) ) ) );
                }
            }
            else
            {
                futures.add( executor.submit( new Task( path ) ) );
            }
        }

        try
        {
            List<PackageMetadata> result = new ArrayList<>();

            for ( Future<PackageMetadata> future : futures )
            {
                try
                {
                    result.add( future.get() );
                }
                catch ( ExecutionException e )
                {
                    // Ignore. Failure to read PackageMetadata of a single package should not break the whole system
                }
            }

            return result;
        }
        catch ( InterruptedException e )
        {
            throw new RuntimeException( e );
        }
    }

    class Task
        implements Callable<PackageMetadata>
    {
        private final Path path;

        public Task( Path path )
        {
            this.path = path;
        }

        @Override
        public PackageMetadata call()
            throws Exception
        {
            try (InputStream fis = Files.newInputStream( path ))
            {
                try (BufferedInputStream bis = new BufferedInputStream( fis, 128 ))
                {
                    try (InputStream is = isCompressed( bis ) ? new GZIPInputStream( bis ) : bis)
                    {
                        MetadataStaxReader reader = new MetadataStaxReader();
                        return reader.read( is );
                    }
                }
            }
        }

        private boolean isCompressed( BufferedInputStream bis )
            throws IOException
        {
            try
            {
                bis.mark( 2 );
                DataInputStream ois = new DataInputStream( bis );
                int magic = Short.reverseBytes( ois.readShort() ) & 0xFFFF;
                return magic == GZIPInputStream.GZIP_MAGIC;
            }
            catch ( EOFException e )
            {
                return false;
            }
            finally
            {
                bis.reset();
            }
        }
    }
}

@@ -950,18 +950,100 @@
 .util.Li
-st
+nkedHashMap;%0Aimport java.util.List;%0Aimport java.util.Map;%0Aimport java.util.Map.Entry
 ;%0Aimport
@@ -1418,16 +1418,74 @@
 tream;%0A%0A
+import org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;%0A%0A
 import o
@@ -1659,16 +1659,100 @@
 eader%0A%7B%0A
+    private final Logger logger = LoggerFactory.getLogger( MetadataReader.class );%0A%0A
     priv
@@ -2573,21 +2573,26 @@
         
-List%3C
+Map%3CPath, 
 Future%3CP
@@ -2618,33 +2618,37 @@
 tures = new 
-ArrayList
+LinkedHashMap
 %3C%3E();%0A%0A     
@@ -3045,87 +3045,188 @@
     
-    futures.add( executor.submit( new Task( path.resolve( fragFilename ) ) ) );
+%7B%0A                        Path xmlPath = path.resolve( fragFilename );%0A                        futures.put( xmlPath, executor.submit( new Task( xmlPath ) ) );%0A                    %7D
 %0A   
@@ -3309,20 +3309,26 @@
 futures.
-add(
+put( path,
  executo
@@ -3486,16 +3486,28 @@
    for (
+ Entry%3CPath,
  Future%3C
@@ -3522,23 +3522,23 @@
 etadata%3E
- future
+%3E entry
  : futur
@@ -3539,16 +3539,27 @@
  futures
+.entrySet()
  )%0A     
@@ -3587,74 +3587,364 @@
     
-try%0A                %7B%0A                    result.add( future.get()
+Path path = entry.getKey();%0A                Future%3CPackageMetadata%3E future = entry.getValue();%0A%0A                try%0A                %7B%0A                    PackageMetadata metadata = future.get();%0A                    result.add( metadata );%0A                    logger.debug( %22Read metadata for %7B%7D artifacts from file %7B%7D%22, metadata.getArtifacts().size(), path
  );%0A
@@ -4143,16 +4143,90 @@
  system%0A
+                    logger.debug( %22Skipping metadata file %7B%7D%22, path, e );%0A
         
@@ -4330,32 +4330,102 @@
 n e )%0A        %7B%0A
+            logger.debug( %22Metadata reader thread was interrupted%22 );%0A
             thro

Improve metadata reader logging