org.apache.maven.index.cli.NexusIndexerCli.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.maven.index.cli.NexusIndexerCli.java

Source

package org.apache.maven.index.cli;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.FSDirectory;
import org.apache.maven.index.ArtifactContext;
import org.apache.maven.index.ArtifactInfo;
import org.apache.maven.index.ArtifactScanningListener;
import org.apache.maven.index.NexusIndexer;
import org.apache.maven.index.ScanningResult;
import org.apache.maven.index.context.IndexCreator;
import org.apache.maven.index.context.IndexingContext;
import org.apache.maven.index.context.UnsupportedExistingLuceneIndexException;
import org.apache.maven.index.packer.IndexPacker;
import org.apache.maven.index.packer.IndexPackingRequest;
import org.apache.maven.index.packer.IndexPackingRequest.IndexFormat;
import org.apache.maven.index.updater.DefaultIndexUpdater;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.LoggerManager;
import org.codehaus.plexus.tools.cli.AbstractCli;
import org.codehaus.plexus.util.IOUtil;

/**
 * A command line tool that can be used to index local Maven repository.
 * <p/>
 * The following command line options are supported:
 * <ul>
 * <li>-repository <path> : required path to repository to be indexed</li>
 * <li>-index <path> : required index folder used to store created index or where previously created index is stored</li>
 * <li>-name <path> : required repository name/id</li>
 * <li>-target <path> : optional folder name where to save produced index files</li>
 * <li>-type <path> : optional indexer types</li>
 * <li>-format <path> : optional indexer formats</li>
 * </ul>
 * When index folder contains previously created index, the tool will use it as a base line and will generate chunks for
 * the incremental updates.
 * <p/>
 * The indexer types could be one of default, min or full. You can also specify list of comma-separated custom index
 * creators. An index creator should be a regular Plexus component, see
 * {@link org.apache.maven.index.creator.MinimalArtifactInfoIndexCreator} and
 * {@link org.apache.maven.index.creator.JarFileContentsIndexCreator}.
 */
public class NexusIndexerCli extends AbstractCli {
    // Command line options

    public static final char REPO = 'r';

    public static final char INDEX = 'i';

    public static final char NAME = 'n';

    public static final char TYPE = 't';

    public static final char TARGET_DIR = 'd';

    public static final char CREATE_INCREMENTAL_CHUNKS = 'c';

    public static final char CREATE_FILE_CHECKSUMS = 's';

    public static final char INCREMENTAL_CHUNK_KEEP_COUNT = 'k';

    public static final char UNPACK = 'u';

    private static final long MB = 1024 * 1024;

    private Options options;

    private int status = 0;

    public static void main(String[] args) throws Exception {
        NexusIndexerCli cli = new NexusIndexerCli();

        cli.execute(args);

        System.exit(cli.status);
    }

    @Override
    public int execute(String[] arg0, ClassWorld arg1) {
        int value = super.execute(arg0, arg1);

        if (status == 0) {
            status = value;
        }

        return status;
    }

    @Override
    public int execute(String[] args) {
        int value = super.execute(args);

        if (status == 0) {
            status = value;
        }

        return status;
    }

    @Override
    protected void showError(String message, Exception e, boolean show) {
        status = 1;
        super.showError(message, e, show);
    }

    @Override
    protected int showFatalError(String message, Exception e, boolean show) {
        status = 1;
        return super.showFatalError(message, e, show);
    }

    @Override
    public CommandLine parse(String[] args) throws ParseException {
        try {
            return super.parse(args);
        } catch (ParseException e) {
            status = 1;
            throw e;
        }
    }

    @Override
    public String getPomPropertiesPath() {
        return "META-INF/maven/org.sonatype.nexus/nexus-indexer/pom.properties";
    }

    @Override
    @SuppressWarnings("static-access")
    public Options buildCliOptions(Options options) {
        this.options = options;

        options.addOption(OptionBuilder.withLongOpt("index").hasArg() //
                .withDescription("Path to the index folder.").create(INDEX));

        options.addOption(OptionBuilder.withLongOpt("destination").hasArg() //
                .withDescription("Target folder.").create(TARGET_DIR));

        options.addOption(OptionBuilder.withLongOpt("repository").hasArg() //
                .withDescription("Path to the Maven repository.").create(REPO));

        options.addOption(OptionBuilder.withLongOpt("name").hasArg() //
                .withDescription("Repository name.").create(NAME));

        options.addOption(OptionBuilder.withLongOpt("chunks") //
                .withDescription("Create incremental chunks.").create(CREATE_INCREMENTAL_CHUNKS));

        options.addOption(OptionBuilder.withLongOpt("keep").hasArg()
                .withDescription("Number of incremental chunks to keep.").create(INCREMENTAL_CHUNK_KEEP_COUNT));

        options.addOption(OptionBuilder.withLongOpt("checksums") //
                .withDescription("Create checksums for all files (sha1, md5).").create(CREATE_FILE_CHECKSUMS));

        options.addOption(OptionBuilder.withLongOpt("type").hasArg() //
                .withDescription("Indexer type (default, min, full or comma separated list of custom types).")
                .create(TYPE));

        options.addOption(OptionBuilder.withLongOpt("unpack") //
                .withDescription("Unpack an index file").create(UNPACK));

        return options;
    }

    @Override
    public void displayHelp() {
        System.out.println();

        HelpFormatter formatter = new HelpFormatter();

        formatter.printHelp("nexus-indexer [options]", "\nOptions:", options, "\n");
    }

    public void displayHelp(String message) {
        System.out.println();

        System.out.println(message);

        System.out.println();

        displayHelp();
    }

    @Override
    public void invokePlexusComponent(final CommandLine cli, PlexusContainer plexus) throws Exception {
        final DefaultContainerConfiguration configuration = new DefaultContainerConfiguration();
        configuration.setClassWorld(((DefaultPlexusContainer) plexus).getClassWorld());
        configuration.setClassPathScanning(PlexusConstants.SCANNING_INDEX);

        // replace plexus, as PlexusCli is blunt, does not allow to modify configuration
        // TODO: get rid of PlexusCli use!
        plexus = new DefaultPlexusContainer(configuration);

        if (cli.hasOption(QUIET)) {
            setLogLevel(plexus, Logger.LEVEL_DISABLED);
        } else if (cli.hasOption(DEBUG)) {
            setLogLevel(plexus, Logger.LEVEL_DEBUG);
        } else if (cli.hasOption(ERRORS)) {
            setLogLevel(plexus, Logger.LEVEL_ERROR);
        }

        if (cli.hasOption(UNPACK)) {
            unpack(cli, plexus);
        } else if (cli.hasOption(INDEX) && cli.hasOption(REPO)) {
            index(cli, plexus);
        } else {
            status = 1;

            displayHelp("Use either unpack (\"" + UNPACK + "\") or index (\"" + INDEX + "\" and \"" + REPO
                    + "\") options, but none has been found!");
        }
    }

    private void setLogLevel(PlexusContainer plexus, int logLevel) throws ComponentLookupException {
        plexus.lookup(LoggerManager.class).setThresholds(logLevel);
    }

    private void index(final CommandLine cli, PlexusContainer plexus)
            throws ComponentLookupException, IOException, UnsupportedExistingLuceneIndexException {
        String indexDirectoryName = cli.getOptionValue(INDEX);

        File indexFolder = new File(indexDirectoryName);

        String outputDirectoryName = cli.getOptionValue(TARGET_DIR, ".");

        File outputFolder = new File(outputDirectoryName);

        File repositoryFolder = new File(cli.getOptionValue(REPO));

        String repositoryName = cli.getOptionValue(NAME, indexFolder.getName());

        List<IndexCreator> indexers = getIndexers(cli, plexus);

        boolean createChecksums = cli.hasOption(CREATE_FILE_CHECKSUMS);

        boolean createIncrementalChunks = cli.hasOption(CREATE_INCREMENTAL_CHUNKS);

        boolean debug = cli.hasOption(DEBUG);

        boolean quiet = cli.hasOption(QUIET);

        Integer chunkCount = cli.hasOption(INCREMENTAL_CHUNK_KEEP_COUNT)
                ? Integer.parseInt(cli.getOptionValue(INCREMENTAL_CHUNK_KEEP_COUNT))
                : null;

        if (!quiet) {
            System.err.printf("Repository Folder: %s\n", repositoryFolder.getAbsolutePath());
            System.err.printf("Index Folder:      %s\n", indexFolder.getAbsolutePath());
            System.err.printf("Output Folder:     %s\n", outputFolder.getAbsolutePath());
            System.err.printf("Repository name:   %s\n", repositoryName);
            System.err.printf("Indexers: %s\n", indexers.toString());

            if (createChecksums) {
                System.err.printf("Will create checksum files for all published files (sha1, md5).\n");
            } else {
                System.err.printf("Will not create checksum files.\n");
            }

            if (createIncrementalChunks) {
                System.err.printf("Will create incremental chunks for changes, along with baseline file.\n");
            } else {
                System.err.printf("Will create baseline file.\n");
            }
        }

        NexusIndexer indexer = plexus.lookup(NexusIndexer.class);

        long tstart = System.currentTimeMillis();

        IndexingContext context = indexer.addIndexingContext( //
                repositoryName, // context id
                repositoryName, // repository id
                repositoryFolder, // repository folder
                indexFolder, // index folder
                null, // repositoryUrl
                null, // index update url
                indexers);

        final IndexSearcher indexSearcher = context.acquireIndexSearcher();
        try {
            IndexPacker packer = plexus.lookup(IndexPacker.class);

            ArtifactScanningListener listener = new IndexerListener(context, debug, quiet);

            indexer.scan(context, listener, true);

            IndexPackingRequest request = new IndexPackingRequest(context, indexSearcher.getIndexReader(),
                    outputFolder);

            request.setCreateChecksumFiles(createChecksums);

            request.setCreateIncrementalChunks(createIncrementalChunks);

            request.setFormats(Arrays.asList(IndexFormat.FORMAT_V1));

            if (chunkCount != null) {
                request.setMaxIndexChunks(chunkCount.intValue());
            }

            packIndex(packer, request, debug, quiet);

            if (!quiet) {
                printStats(tstart);
            }
        } finally {
            context.releaseIndexSearcher(indexSearcher);
            indexer.removeIndexingContext(context, false);
        }
    }

    private void unpack(CommandLine cli, PlexusContainer plexus) throws ComponentLookupException, IOException {
        final String indexDirectoryName = cli.getOptionValue(INDEX, ".");
        final File indexFolder = new File(indexDirectoryName).getCanonicalFile();
        final File indexArchive = new File(indexFolder, IndexingContext.INDEX_FILE_PREFIX + ".gz");

        final String outputDirectoryName = cli.getOptionValue(TARGET_DIR, ".");
        final File outputFolder = new File(outputDirectoryName).getCanonicalFile();

        final boolean quiet = cli.hasOption(QUIET);
        if (!quiet) {
            System.err.printf("Index Folder:      %s\n", indexFolder.getAbsolutePath());
            System.err.printf("Output Folder:     %s\n", outputFolder.getAbsolutePath());
        }

        long tstart = System.currentTimeMillis();

        final FSDirectory directory = FSDirectory.open(outputFolder);

        final List<IndexCreator> indexers = getIndexers(cli, plexus);

        BufferedInputStream is = null;
        try {
            is = new BufferedInputStream(new FileInputStream(indexArchive));
            DefaultIndexUpdater.unpackIndexData(is, directory,
                    (IndexingContext) Proxy.newProxyInstance(getClass().getClassLoader(),
                            new Class[] { IndexingContext.class }, new PartialImplementation() {
                                public List<IndexCreator> getIndexCreators() {
                                    return indexers;
                                }
                            })

            );
        } finally {
            IOUtil.close(is);
            if (directory != null) {
                directory.close();
            }
        }

        if (!quiet) {
            printStats(tstart);
        }
    }

    private List<IndexCreator> getIndexers(final CommandLine cli, PlexusContainer plexus)
            throws ComponentLookupException {
        String type = "default";

        if (cli.hasOption(TYPE)) {
            type = cli.getOptionValue(TYPE);
        }

        List<IndexCreator> indexers = new ArrayList<IndexCreator>(); // NexusIndexer.DEFAULT_INDEX;

        if ("default".equals(type)) {
            indexers.add(plexus.lookup(IndexCreator.class, "min"));
            indexers.add(plexus.lookup(IndexCreator.class, "jarContent"));
        } else if ("full".equals(type)) {
            for (Object component : plexus.lookupList(IndexCreator.class)) {
                indexers.add((IndexCreator) component);
            }
        } else {
            for (String hint : type.split(",")) {
                indexers.add(plexus.lookup(IndexCreator.class, hint));
            }
        }
        return indexers;
    }

    private void packIndex(IndexPacker packer, IndexPackingRequest request, boolean debug, boolean quiet) {
        try {
            packer.packIndex(request);
        } catch (IOException e) {
            if (!quiet) {
                System.err.printf("Cannot zip index; \n", e.getMessage());

                if (debug) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void printStats(final long startTimeInMillis) {
        long t = System.currentTimeMillis() - startTimeInMillis;

        long s = t / 1000L;
        if (t > 60 * 1000) {
            long m = t / 1000L / 60L;

            System.err.printf("Total time:   %d min %d sec\n", m, s - (m * 60));
        } else {
            System.err.printf("Total time:   %d sec\n", s);
        }

        Runtime r = Runtime.getRuntime();

        System.err.printf("Final memory: %dM/%dM\n", //
                (r.totalMemory() - r.freeMemory()) / MB, r.totalMemory() / MB);
    }

    /**
     * Scanner listener
     */
    private static final class IndexerListener implements ArtifactScanningListener {
        private final IndexingContext context;

        private final boolean debug;

        private boolean quiet;

        private long ts = System.currentTimeMillis();

        private int count;

        IndexerListener(IndexingContext context, boolean debug, boolean quiet) {
            this.context = context;
            this.debug = debug;
            this.quiet = quiet;
        }

        public void scanningStarted(IndexingContext context) {
            if (!quiet) {
                System.err.println("Scanning started");
            }
        }

        public void artifactDiscovered(ArtifactContext ac) {
            count++;

            long t = System.currentTimeMillis();

            ArtifactInfo ai = ac.getArtifactInfo();

            if (!quiet && debug && "maven-plugin".equals(ai.getPackaging())) {
                System.err.printf("Plugin: %s:%s:%s - %s %s\n", //
                        ai.getGroupId(), ai.getArtifactId(), ai.getVersion(), ai.getPrefix(), "" + ai.getGoals());
            }

            if (!quiet && (debug || (t - ts) > 2000L)) {
                System.err.printf("  %6d %s\n", count, formatFile(ac.getPom()));
                ts = t;
            }
        }

        public void artifactError(ArtifactContext ac, Exception e) {
            if (!quiet) {
                System.err.printf("! %6d %s - %s\n", count, formatFile(ac.getPom()), e.getMessage());

                System.err.printf("         %s\n", formatFile(ac.getArtifact()));

                if (debug) {
                    e.printStackTrace();
                }
            }

            ts = System.currentTimeMillis();
        }

        private String formatFile(File file) {
            return file.getAbsolutePath().substring(context.getRepository().getAbsolutePath().length() + 1);
        }

        public void scanningFinished(IndexingContext context, ScanningResult result) {
            if (!quiet) {
                if (result.hasExceptions()) {
                    System.err.printf("Scanning errors:   %s\n", result.getExceptions().size());
                }

                System.err.printf("Artifacts added:   %s\n", result.getTotalFiles());
                System.err.printf("Artifacts deleted: %s\n", result.getDeletedFiles());
            }
        }
    }

}