You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by br...@apache.org on 2010/11/03 23:51:35 UTC

svn commit: r1030748 [6/21] - in /maven/indexer: branches/ tags/ trunk/ trunk/indexer-artifact/ trunk/indexer-artifact/src/ trunk/indexer-artifact/src/main/ trunk/indexer-artifact/src/main/java/ trunk/indexer-artifact/src/main/java/org/ trunk/indexer-a...

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/NexusIndexerCli.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/NexusIndexerCli.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/NexusIndexerCli.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/NexusIndexerCli.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,615 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.cli;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.lang.reflect.Proxy;
+
+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.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.creator.JarFileContentsIndexCreator;
+import org.apache.maven.index.creator.MinimalArtifactInfoIndexCreator;
+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.PlexusContainer;
+import org.codehaus.plexus.util.IOUtil;
+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;
+
+/**
+ * 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 coma-separated custom index
+ * creators. An index creator should be a regular Plexus component, see {@link MinimalArtifactInfoIndexCreator} and
+ * {@link 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 LEGACY = 'l';
+
+    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 coma separated list of custom types)." ).create( TYPE ) );
+
+        options.addOption( OptionBuilder.withLongOpt( "legacy" ) //
+        .withDescription( "Build legacy .zip index file" ).create( LEGACY ) );
+
+        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
+    {
+        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 createLegacyIndex = cli.hasOption( LEGACY );
+
+        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" );
+            }
+
+            if ( createLegacyIndex )
+            {
+                System.err.printf( "Will also create legacy .zip index file.\n" );
+            }
+        }
+
+        NexusIndexer indexer = plexus.lookup( NexusIndexer.class );
+        IndexingContext context = null;
+
+        try
+        {
+            long tstart = System.currentTimeMillis();
+
+            context = indexer.addIndexingContext( //
+                repositoryName, // context id
+                repositoryName, // repository id
+                repositoryFolder, // repository folder
+                indexFolder, // index folder
+                null, // repositoryUrl
+                null, // index update url
+                indexers );
+
+            IndexPacker packer = plexus.lookup( IndexPacker.class );
+
+            ArtifactScanningListener listener = new IndexerListener( context, debug, quiet );
+
+            indexer.scan( context, listener, true );
+
+            IndexPackingRequest request = new IndexPackingRequest( context, outputFolder );
+
+            request.setCreateChecksumFiles( createChecksums );
+
+            request.setCreateIncrementalChunks( createIncrementalChunks );
+
+            if ( createLegacyIndex )
+            {
+                request.setFormats( Arrays.asList( IndexFormat.FORMAT_LEGACY, IndexFormat.FORMAT_V1 ) );
+            }
+            else
+            {
+                request.setFormats( Arrays.asList( IndexFormat.FORMAT_V1 ) );
+            }
+
+            if ( chunkCount != null )
+            {
+                request.setMaxIndexChunks( chunkCount.intValue() );
+            }
+
+            packIndex( packer, request, debug, quiet );
+
+            if ( !quiet )
+            {
+                printStats( tstart );
+            }
+        }
+        finally
+        {
+            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 + ".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.getDirectory( 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.packaging ) )
+            {
+                System.err.printf( "Plugin: %s:%s:%s - %s %s\n", //
+                    ai.groupId, ai.artifactId, ai.version, ai.prefix, "" + ai.goals );
+            }
+
+            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() );
+            }
+        }
+    }
+
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/NexusIndexerCli.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/NexusIndexerCli.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/PartialImplementation.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/PartialImplementation.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/PartialImplementation.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/PartialImplementation.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,59 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.cli;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * An {@link java.lang.reflect.InvocationHandler} that can be extended with methods from the proxied interface.
+ * While invocation it will look for a method within itself that matches the signature of the invoked peoxy method.
+ * If found the method will be invoked and result returned, otherwise an {@link UnsupportedOperationException} will be
+ * thrown.
+ *
+ * @author Alin Dreghiciu
+ */
+public class PartialImplementation
+    implements InvocationHandler
+{
+
+    public Object invoke( final Object proxy, final Method method, final Object[] args )
+        throws Throwable
+    {
+        try
+        {
+            final Method localMethod = getClass().getMethod( method.getName(), method.getParameterTypes() );
+            return localMethod.invoke( this, args );
+        }
+        catch( NoSuchMethodException e )
+        {
+            throw new UnsupportedOperationException( "Method " + method.getName() + "() is not supported" );
+        }
+        catch( IllegalAccessException e )
+        {
+            throw new UnsupportedOperationException( "Method " + method.getName() + "() is not supported" );
+        }
+        catch( InvocationTargetException e )
+        {
+            throw e.getCause();
+        }
+    }
+
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/PartialImplementation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/cli/PartialImplementation.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DefaultIndexingContext.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DefaultIndexingContext.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DefaultIndexingContext.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DefaultIndexingContext.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,800 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.context;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.SerialMergeScheduler;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Hits;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.maven.index.ArtifactInfo;
+import org.apache.maven.index.artifact.GavCalculator;
+import org.apache.maven.index.artifact.M2GavCalculator;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * The default {@link IndexingContext} implementation.
+ * 
+ * @author Jason van Zyl
+ * @author Tamas Cservenak
+ */
+public class DefaultIndexingContext
+    implements IndexingContext
+{
+    /**
+     * A standard location for indices served up by a webserver.
+     */
+    private static final String INDEX_DIRECTORY = ".index";
+
+    private static final String FLD_DESCRIPTOR = "DESCRIPTOR";
+
+    private static final String FLD_DESCRIPTOR_CONTENTS = "NexusIndex";
+
+    private static final String FLD_IDXINFO = "IDXINFO";
+
+    private static final String VERSION = "1.0";
+
+    private static final Term DESCRIPTOR_TERM = new Term( FLD_DESCRIPTOR, FLD_DESCRIPTOR_CONTENTS );
+
+    private Object indexLock = new Object();
+
+    private Directory indexDirectory;
+
+    private File indexDirectoryFile;
+
+    private String id;
+
+    private boolean searchable;
+
+    private String repositoryId;
+
+    private File repository;
+
+    private String repositoryUrl;
+
+    private String indexUpdateUrl;
+
+    private IndexReader indexReader;
+
+    private NexusIndexSearcher indexSearcher;
+
+    // disabled for now, see getReadOnlyIndexSearcher() method for explanation
+    // private NexusIndexSearcher readOnlyIndexSearcher;
+
+    private NexusIndexWriter indexWriter;
+
+    private Date timestamp;
+
+    private List<? extends IndexCreator> indexCreators;
+
+    /**
+     * Currently nexus-indexer knows only M2 reposes
+     * <p>
+     * XXX move this into a concrete Scanner implementation
+     */
+    private GavCalculator gavCalculator;
+
+    private DefaultIndexingContext( String id, String repositoryId,
+                                    File repository, //
+                                    String repositoryUrl, String indexUpdateUrl,
+                                    List<? extends IndexCreator> indexCreators )
+    {
+        this.id = id;
+
+        this.searchable = true;
+
+        this.repositoryId = repositoryId;
+
+        this.repository = repository;
+
+        this.repositoryUrl = repositoryUrl;
+
+        this.indexUpdateUrl = indexUpdateUrl;
+
+        this.indexReader = null;
+
+        this.indexWriter = null;
+
+        this.indexCreators = indexCreators;
+
+        // eh?
+        // Guice does NOT initialize these, and we have to do manually?
+        // While in Plexus, all is well, but when in guice-shim,
+        // these objects are still LazyHintedBeans or what not and IndexerFields are NOT registered!
+        for ( IndexCreator indexCreator : indexCreators )
+        {
+            indexCreator.getIndexerFields();
+        }
+
+        this.gavCalculator = new M2GavCalculator();
+    }
+
+    public DefaultIndexingContext( String id, String repositoryId, File repository, File indexDirectoryFile,
+                                   String repositoryUrl, String indexUpdateUrl,
+                                   List<? extends IndexCreator> indexCreators, boolean reclaimIndex )
+        throws IOException, UnsupportedExistingLuceneIndexException
+    {
+        this( id, repositoryId, repository, repositoryUrl, indexUpdateUrl, indexCreators );
+
+        this.indexDirectoryFile = indexDirectoryFile;
+
+        this.indexDirectory = FSDirectory.getDirectory( indexDirectoryFile );
+
+        prepareIndex( reclaimIndex );
+    }
+
+    public DefaultIndexingContext( String id, String repositoryId, File repository, Directory indexDirectory,
+                                   String repositoryUrl, String indexUpdateUrl,
+                                   List<? extends IndexCreator> indexCreators, boolean reclaimIndex )
+        throws IOException, UnsupportedExistingLuceneIndexException
+    {
+        this( id, repositoryId, repository, repositoryUrl, indexUpdateUrl, indexCreators );
+
+        this.indexDirectory = indexDirectory;
+
+        if ( indexDirectory instanceof FSDirectory )
+        {
+            this.indexDirectoryFile = ( (FSDirectory) indexDirectory ).getFile();
+        }
+
+        prepareIndex( reclaimIndex );
+    }
+
+    public Directory getIndexDirectory()
+    {
+        return indexDirectory;
+    }
+
+    public File getIndexDirectoryFile()
+    {
+        return indexDirectoryFile;
+    }
+
+    private void prepareIndex( boolean reclaimIndex )
+        throws IOException, UnsupportedExistingLuceneIndexException
+    {
+        if ( IndexReader.indexExists( indexDirectory ) )
+        {
+            try
+            {
+                // unlock the dir forcibly
+                if ( IndexReader.isLocked( indexDirectory ) )
+                {
+                    IndexReader.unlock( indexDirectory );
+                }
+
+                checkAndUpdateIndexDescriptor( reclaimIndex );
+            }
+            catch ( IOException e )
+            {
+                if ( reclaimIndex )
+                {
+                    prepareCleanIndex( true );
+                }
+                else
+                {
+                    throw e;
+                }
+            }
+        }
+        else
+        {
+            prepareCleanIndex( false );
+        }
+
+        timestamp = IndexUtils.getTimestamp( indexDirectory );
+    }
+
+    private void prepareCleanIndex( boolean deleteExisting )
+        throws IOException
+    {
+        if ( deleteExisting )
+        {
+            if ( indexReader != null )
+            {
+                indexReader.close();
+            }
+
+            indexReader = null;
+
+            if ( indexWriter != null && !indexWriter.isClosed() )
+            {
+                indexWriter.close();
+            }
+
+            indexWriter = null;
+
+            // unlock the dir forcibly
+            if ( IndexReader.isLocked( indexDirectory ) )
+            {
+                IndexReader.unlock( indexDirectory );
+            }
+
+            indexDirectory.close();
+            FileUtils.deleteDirectory( indexDirectoryFile );
+            indexDirectoryFile.mkdirs();
+
+            indexDirectory = FSDirectory.getDirectory( indexDirectoryFile );
+        }
+
+        if ( StringUtils.isEmpty( getRepositoryId() ) )
+        {
+            throw new IllegalArgumentException( "The repositoryId cannot be null when creating new repository!" );
+        }
+
+        // create empty idx and store descriptor
+        new NexusIndexWriter( getIndexDirectory(), new NexusAnalyzer(), true ).close();
+
+        storeDescriptor();
+    }
+
+    private void checkAndUpdateIndexDescriptor( boolean reclaimIndex )
+        throws IOException, UnsupportedExistingLuceneIndexException
+    {
+        if ( reclaimIndex )
+        {
+            // forcefully "reclaiming" the ownership of the index as ours
+            storeDescriptor();
+            return;
+        }
+
+        Hits hits = getIndexSearcher().search( new TermQuery( DESCRIPTOR_TERM ) );
+
+        if ( hits == null || hits.length() == 0 )
+        {
+            throw new UnsupportedExistingLuceneIndexException( "The existing index has no NexusIndexer descriptor" );
+        }
+
+        Document descriptor = hits.doc( 0 );
+
+        if ( hits.length() != 1 )
+        {
+            storeDescriptor();
+            return;
+        }
+
+        String[] h = StringUtils.split( descriptor.get( FLD_IDXINFO ), ArtifactInfo.FS );
+        // String version = h[0];
+        String repoId = h[1];
+
+        // // compare version
+        // if ( !VERSION.equals( version ) )
+        // {
+        // throw new UnsupportedExistingLuceneIndexException(
+        // "The existing index has version [" + version + "] and not [" + VERSION + "] version!" );
+        // }
+
+        if ( getRepositoryId() == null )
+        {
+            repositoryId = repoId;
+        }
+        else if ( !getRepositoryId().equals( repoId ) )
+        {
+            throw new UnsupportedExistingLuceneIndexException( "The existing index is for repository " //
+                + "[" + repoId + "] and not for repository [" + getRepositoryId() + "]" );
+        }
+    }
+
+    private void storeDescriptor()
+        throws IOException
+    {
+        Document hdr = new Document();
+
+        hdr.add( new Field( FLD_DESCRIPTOR, FLD_DESCRIPTOR_CONTENTS, Field.Store.YES, Field.Index.UN_TOKENIZED ) );
+
+        hdr.add( new Field( FLD_IDXINFO, VERSION + ArtifactInfo.FS + getRepositoryId(), Field.Store.YES, Field.Index.NO ) );
+
+        IndexWriter w = getIndexWriter();
+
+        w.updateDocument( DESCRIPTOR_TERM, hdr );
+
+        w.commit();
+    }
+
+    private void deleteIndexFiles()
+        throws IOException
+    {
+        String[] names = indexDirectory.list();
+
+        if ( names != null )
+        {
+            for ( int i = 0; i < names.length; i++ )
+            {
+                indexDirectory.deleteFile( names[i] );
+            }
+        }
+
+        IndexUtils.deleteTimestamp( indexDirectory );
+    }
+
+    public boolean isSearchable()
+    {
+        return searchable;
+    }
+
+    public void setSearchable( boolean searchable )
+    {
+        this.searchable = searchable;
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public void updateTimestamp()
+        throws IOException
+    {
+        updateTimestamp( false );
+    }
+
+    public void updateTimestamp( boolean save )
+        throws IOException
+    {
+        updateTimestamp( save, new Date() );
+    }
+
+    public void updateTimestamp( boolean save, Date timestamp )
+        throws IOException
+    {
+        this.timestamp = timestamp;
+
+        if ( save )
+        {
+            IndexUtils.updateTimestamp( indexDirectory, getTimestamp() );
+        }
+    }
+
+    public Date getTimestamp()
+    {
+        return timestamp;
+    }
+
+    public String getRepositoryId()
+    {
+        return repositoryId;
+    }
+
+    public File getRepository()
+    {
+        return repository;
+    }
+
+    public String getRepositoryUrl()
+    {
+        return repositoryUrl;
+    }
+
+    public String getIndexUpdateUrl()
+    {
+        if ( repositoryUrl != null )
+        {
+            if ( indexUpdateUrl == null || indexUpdateUrl.trim().length() == 0 )
+            {
+                return repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + INDEX_DIRECTORY;
+            }
+        }
+        return indexUpdateUrl;
+    }
+
+    public Analyzer getAnalyzer()
+    {
+        return new NexusAnalyzer();
+    }
+
+    public IndexWriter getIndexWriter()
+        throws IOException
+    {
+        synchronized ( indexLock )
+        {
+            if ( indexWriter == null || indexWriter.isClosed() )
+            {
+                indexWriter = new NexusIndexWriter( getIndexDirectory(), new NexusAnalyzer(), false );
+
+                indexWriter.setRAMBufferSizeMB( 2 );
+
+                indexWriter.setMergeScheduler( new SerialMergeScheduler() );
+            }
+
+            return indexWriter;
+        }
+    }
+
+    protected boolean isIndexWriterDirty()
+    {
+        synchronized ( indexLock )
+        {
+            if ( indexWriter == null || indexWriter.isClosed() )
+            {
+                return false;
+            }
+            else
+            {
+                return indexWriter.hasUncommittedChanges();
+            }
+        }
+    }
+
+    public IndexReader getIndexReader()
+        throws IOException
+    {
+        synchronized ( indexLock )
+        {
+            if ( indexReader == null || ( !indexReader.isCurrent() && !isIndexWriterDirty() ) )
+            {
+                if ( indexReader != null )
+                {
+                    indexReader.close();
+                }
+
+                indexReader = IndexReader.open( indexDirectory );
+            }
+
+            return indexReader;
+        }
+    }
+
+    public IndexSearcher getIndexSearcher()
+        throws IOException
+    {
+        synchronized ( indexLock )
+        {
+            if ( indexSearcher == null || getIndexReader() != indexSearcher.getIndexReader() )
+            {
+                if ( indexSearcher != null )
+                {
+                    indexSearcher.close();
+
+                    // the reader was supplied explicitly
+                    indexSearcher.getIndexReader().close();
+                }
+
+                indexSearcher = new NexusIndexSearcher( this );
+            }
+
+            return indexSearcher;
+        }
+    }
+
+    @Deprecated
+    public IndexSearcher getReadOnlyIndexSearcher()
+        throws IOException
+    {
+        return getIndexSearcher();
+
+        // using RO IndexReader in this way just causes to _double_ the file handles Nexus
+        // uses to read/maintain the Lucene Indexes (once for UPDATE, once for Searches).
+        // The performance gain (no lock contention) is currently too small for the price of
+        // doubled file handles. We will have to resolve this later.
+
+        // disabled for now, see getReadOnlyIndexSearcher() method for explanation
+        // synchronized ( indexLock )
+        // {
+        // if ( readOnlyIndexSearcher == null || !readOnlyIndexSearcher.getIndexReader().isCurrent() )
+        // {
+        // if ( readOnlyIndexSearcher != null )
+        // {
+        // readOnlyIndexSearcher.close();
+        //
+        // // the reader was supplied explicitly
+        // readOnlyIndexSearcher.getIndexReader().close();
+        // }
+        //
+        // readOnlyIndexSearcher = new NexusIndexSearcher( this, IndexReader.open( indexDirectory, true ) );
+        // }
+        //
+        // return readOnlyIndexSearcher;
+        // }
+    }
+
+    public void optimize()
+        throws CorruptIndexException, IOException
+    {
+        IndexWriter w = getIndexWriter();
+
+        try
+        {
+            w.optimize();
+
+            w.commit();
+        }
+        catch ( CorruptIndexException e )
+        {
+            w.close();
+
+            throw e;
+        }
+        catch ( IOException e )
+        {
+            w.close();
+
+            throw e;
+        }
+    }
+
+    public void close( boolean deleteFiles )
+        throws IOException
+    {
+        if ( indexDirectory != null )
+        {
+            synchronized ( indexLock )
+            {
+                IndexUtils.updateTimestamp( indexDirectory, getTimestamp() );
+
+                closeReaders();
+
+                if ( deleteFiles )
+                {
+                    deleteIndexFiles();
+                }
+
+                indexDirectory.close();
+            }
+        }
+
+        // TODO: this will prevent from reopening them, but needs better solution
+        indexDirectory = null;
+    }
+
+    public void purge()
+        throws IOException
+    {
+        synchronized ( indexLock )
+        {
+            closeReaders();
+
+            deleteIndexFiles();
+
+            try
+            {
+                prepareIndex( true );
+            }
+            catch ( UnsupportedExistingLuceneIndexException e )
+            {
+                // just deleted it
+            }
+
+            rebuildGroups();
+
+            updateTimestamp( true, null );
+        }
+    }
+
+    // XXX need some locking for reader/writer
+    public void replace( Directory directory )
+        throws IOException
+    {
+        synchronized ( indexLock )
+        {
+            closeReaders();
+
+            deleteIndexFiles();
+
+            Directory.copy( directory, indexDirectory, false );
+
+            // reclaim the index as mine
+            storeDescriptor();
+
+            timestamp = IndexUtils.getTimestamp( directory );
+
+            IndexUtils.updateTimestamp( indexDirectory, getTimestamp() );
+
+            optimize();
+        }
+    }
+
+    public void merge( Directory directory )
+        throws IOException
+    {
+        merge( directory, null );
+    }
+
+    public void merge( Directory directory, DocumentFilter filter )
+        throws IOException
+    {
+        synchronized ( indexLock )
+        {
+            IndexWriter w = getIndexWriter();
+
+            IndexSearcher s = getIndexSearcher();
+
+            IndexReader r = IndexReader.open( directory );
+
+            try
+            {
+                int numDocs = r.maxDoc();
+
+                for ( int i = 0; i < numDocs; i++ )
+                {
+                    if ( r.isDeleted( i ) )
+                    {
+                        continue;
+                    }
+
+                    Document d = r.document( i );
+
+                    if ( filter != null && !filter.accept( d ) )
+                    {
+                        continue;
+                    }
+
+                    String uinfo = d.get( ArtifactInfo.UINFO );
+
+                    if ( uinfo != null )
+                    {
+                        Hits hits = s.search( new TermQuery( new Term( ArtifactInfo.UINFO, uinfo ) ) );
+
+                        if ( hits.length() == 0 )
+                        {
+                            w.addDocument( IndexUtils.updateDocument( d, this, false ) );
+                        }
+                    }
+                    else
+                    {
+                        String deleted = d.get( ArtifactInfo.DELETED );
+
+                        if ( deleted != null )
+                        {
+                            // Deleting the document loses history that it was delete,
+                            // so incrementals wont work. Therefore, put the delete
+                            // document in as well
+                            w.deleteDocuments( new Term( ArtifactInfo.UINFO, deleted ) );
+                            w.addDocument( d );
+                        }
+                    }
+                }
+
+            }
+            finally
+            {
+                r.close();
+
+                w.commit();
+            }
+
+            rebuildGroups();
+
+            Date mergedTimestamp = IndexUtils.getTimestamp( directory );
+
+            if ( getTimestamp() != null && mergedTimestamp != null && mergedTimestamp.after( getTimestamp() ) )
+            {
+                // we have both, keep the newest
+                updateTimestamp( true, mergedTimestamp );
+            }
+            else
+            {
+                updateTimestamp( true );
+            }
+
+            optimize();
+        }
+    }
+
+    private void closeReaders()
+        throws CorruptIndexException, IOException
+    {
+        if ( indexWriter != null )
+        {
+            if ( !indexWriter.isClosed() )
+            {
+                indexWriter.close();
+            }
+
+            indexWriter = null;
+        }
+        if ( indexSearcher != null )
+        {
+            indexSearcher.close();
+
+            // the reader was supplied explicitly
+            indexSearcher.getIndexReader().close();
+
+            indexSearcher = null;
+        }
+        if ( indexReader != null )
+        {
+            indexReader.close();
+
+            indexReader = null;
+        }
+
+        // if ( readOnlyIndexSearcher != null )
+        // {
+        // readOnlyIndexSearcher.close();
+        //
+        // // the reader was supplied explicitly
+        // readOnlyIndexSearcher.getIndexReader().close();
+        //
+        // readOnlyIndexSearcher = null;
+        // }
+    }
+
+    public GavCalculator getGavCalculator()
+    {
+        return gavCalculator;
+    }
+
+    public List<IndexCreator> getIndexCreators()
+    {
+        return Collections.unmodifiableList( indexCreators );
+    }
+
+    // groups
+
+    public void rebuildGroups()
+        throws IOException
+    {
+        synchronized ( indexLock )
+        {
+            IndexUtils.rebuildGroups( this );
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.maven.index.context.IndexingContext#getAllGroups()
+     */
+    public Set<String> getAllGroups()
+        throws IOException
+    {
+        return IndexUtils.getAllGroups( this );
+    }
+
+    public void setAllGroups( Collection<String> groups )
+        throws IOException
+    {
+        IndexUtils.setAllGroups( this, groups );
+    }
+
+    public Set<String> getRootGroups()
+        throws IOException
+    {
+        return IndexUtils.getRootGroups( this );
+    }
+
+    public void setRootGroups( Collection<String> groups )
+        throws IOException
+    {
+        IndexUtils.setRootGroups( this, groups );
+    }
+
+    @Override
+    public String toString()
+    {
+        return id + " : " + timestamp;
+    }
+
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DefaultIndexingContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DefaultIndexingContext.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DocumentFilter.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DocumentFilter.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DocumentFilter.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DocumentFilter.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,26 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.context;
+
+import org.apache.lucene.document.Document;
+
+public interface DocumentFilter
+{
+    boolean accept( Document doc );
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DocumentFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/DocumentFilter.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexCreator.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexCreator.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexCreator.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexCreator.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,65 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.context;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.apache.lucene.document.Document;
+import org.apache.maven.index.ArtifactContext;
+import org.apache.maven.index.ArtifactInfo;
+import org.apache.maven.index.IndexerField;
+import org.apache.maven.index.creator.JarFileContentsIndexCreator;
+import org.apache.maven.index.creator.MinimalArtifactInfoIndexCreator;
+
+/**
+ * An index creator is responsible for storing and reading data to and from Lucene index.
+ * 
+ * @author Jason van Zyl
+ * @see MinimalArtifactInfoIndexCreator
+ * @see JarFileContentsIndexCreator
+ */
+public interface IndexCreator
+{
+    /**
+     * Returns the indexer fields that this IndexCreator introduces to index.
+     * 
+     * @return
+     */
+    public Collection<IndexerField> getIndexerFields();
+
+    /**
+     * Populate an <code>ArtifactContext</code> with information about corresponding artifact.
+     */
+    void populateArtifactInfo( ArtifactContext artifactContext )
+        throws IOException;
+
+    /**
+     * Update Lucene <code>Document</code> from a given <code>ArtifactInfo</code>.
+     */
+    void updateDocument( ArtifactInfo artifactInfo, Document document );
+
+    /**
+     * Update an <code>ArtifactInfo</code> from given Lucene <code>Document</code>.
+     * 
+     * @return true is artifact info has been updated
+     */
+    boolean updateArtifactInfo( Document document, ArtifactInfo artifactInfo );
+
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexCreator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexCreator.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexUtils.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexUtils.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexUtils.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexUtils.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,418 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.context;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Hits;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.IndexOutput;
+import org.apache.maven.index.ArtifactInfo;
+import org.codehaus.plexus.util.FileUtils;
+
+public class IndexUtils
+{
+    public static final String TIMESTAMP_FILE = "timestamp";
+
+    // timestamp
+
+    public static ArtifactInfo constructArtifactInfo( Document doc, IndexingContext context )
+    {
+        // if no UINFO can't create, must be a different type of record
+        if ( doc.get( ArtifactInfo.UINFO ) == null )
+        {
+            return null;
+        }
+        
+        boolean res = false;
+    
+        ArtifactInfo artifactInfo = new ArtifactInfo();
+    
+        for ( IndexCreator ic : context.getIndexCreators() )
+        {
+            res |= ic.updateArtifactInfo( doc, artifactInfo );
+        }
+    
+        return res ? artifactInfo : null;
+    }
+    
+    public static Document updateDocument( Document doc, IndexingContext context )
+    {
+        return updateDocument( doc, context, true );
+    }
+
+    public static Document updateDocument( Document doc, IndexingContext context, boolean updateLastModified )
+    {
+        ArtifactInfo ai = constructArtifactInfo( doc, context );
+        if ( ai == null )
+        {
+            return doc;
+        }
+    
+        Document document = new Document();
+        
+        // unique key
+        document.add( new Field( ArtifactInfo.UINFO, ai.getUinfo(), Field.Store.YES, Field.Index.UN_TOKENIZED ) );
+        
+        if ( updateLastModified 
+            || doc.getField( ArtifactInfo.LAST_MODIFIED ) == null )
+        {
+            document.add( new Field( ArtifactInfo.LAST_MODIFIED, //
+                Long.toString( System.currentTimeMillis() ), Field.Store.YES, Field.Index.NO ) );
+        }
+        else
+        {
+            document.add( doc.getField( ArtifactInfo.LAST_MODIFIED ) );
+        }
+        
+        for ( IndexCreator ic : context.getIndexCreators() )
+        {
+            ic.updateDocument( ai, document );
+        }
+    
+        return document;
+    }
+
+    public static void deleteTimestamp( Directory directory )
+        throws IOException
+    {
+        if ( directory.fileExists( TIMESTAMP_FILE ) )
+        {
+            directory.deleteFile( TIMESTAMP_FILE );
+        }
+    }
+
+    public static void updateTimestamp( Directory directory, Date timestamp )
+        throws IOException
+    {
+        synchronized ( directory )
+        {
+            Date currentTimestamp = getTimestamp( directory );
+
+            if ( timestamp != null && ( currentTimestamp == null || !currentTimestamp.equals( timestamp ) ) )
+            {
+                deleteTimestamp( directory );
+
+                IndexOutput io = directory.createOutput( TIMESTAMP_FILE );
+
+                try
+                {
+                    io.writeLong( timestamp.getTime() );
+
+                    io.flush();
+                }
+                finally
+                {
+                    close( io );
+                }
+            }
+        }
+    }
+
+    public static Date getTimestamp( Directory directory )
+    {
+        synchronized ( directory )
+        {
+            Date result = null;
+            try
+            {
+                if ( directory.fileExists( TIMESTAMP_FILE ) )
+                {
+                    IndexInput ii = null;
+
+                    try
+                    {
+                        ii = directory.openInput( TIMESTAMP_FILE );
+
+                        result = new Date( ii.readLong() );
+                    }
+                    finally
+                    {
+                        close( ii );
+                    }
+                }
+            }
+            catch ( IOException ex )
+            {
+            }
+
+            return result;
+        }
+    }
+
+    /**
+     * Used to rebuild group information, for example on context which were merged, since merge() of contexts 
+     * only merges the Documents with UINFO record (Artifacts).
+     */
+    public static void rebuildGroups( IndexingContext context ) throws IOException 
+    {
+        IndexReader r = context.getIndexReader();
+
+        Set<String> rootGroups = new LinkedHashSet<String>();
+        Set<String> allGroups = new LinkedHashSet<String>();
+
+        int numDocs = r.maxDoc();
+
+        for ( int i = 0; i < numDocs; i++ )
+        {
+            if ( r.isDeleted( i ) )
+            {
+                continue;
+            }
+
+            Document d = r.document( i );
+
+            String uinfo = d.get( ArtifactInfo.UINFO );
+
+            if ( uinfo != null )
+            {
+                ArtifactInfo info = IndexUtils.constructArtifactInfo( d, context );
+                rootGroups.add( info.getRootGroup() );
+                allGroups.add( info.groupId );
+            }
+        }
+
+        setRootGroups( context, rootGroups );
+        setAllGroups( context, allGroups );
+
+        context.optimize();
+    }
+
+    // ----------------------------------------------------------------------------
+    // Root groups
+    // ----------------------------------------------------------------------------
+
+    public static Set<String> getRootGroups( IndexingContext context )
+        throws IOException
+    {
+        return getGroups( context, ArtifactInfo.ROOT_GROUPS, ArtifactInfo.ROOT_GROUPS_VALUE, ArtifactInfo.ROOT_GROUPS_LIST );
+    }
+
+    public static void setRootGroups( IndexingContext context, Collection<String> groups )
+        throws IOException
+    {
+        setGroups( context, groups, ArtifactInfo.ROOT_GROUPS, ArtifactInfo.ROOT_GROUPS_VALUE, ArtifactInfo.ROOT_GROUPS_LIST );
+    }
+
+    // ----------------------------------------------------------------------------
+    // All groups
+    // ----------------------------------------------------------------------------
+
+    public static Set<String> getAllGroups( IndexingContext context )
+        throws IOException
+    {
+        return getGroups( context, ArtifactInfo.ALL_GROUPS, ArtifactInfo.ALL_GROUPS_VALUE, ArtifactInfo.ALL_GROUPS_LIST );
+    }
+
+    public static void setAllGroups( IndexingContext context, Collection<String> groups )
+        throws IOException
+    {
+        setGroups( context, groups, ArtifactInfo.ALL_GROUPS, ArtifactInfo.ALL_GROUPS_VALUE, ArtifactInfo.ALL_GROUPS_LIST );
+    }
+    
+    private static Set<String> getGroups( IndexingContext context, String field, String filedValue, String listField )
+        throws IOException,
+            CorruptIndexException
+    {
+        Hits hits = context.getIndexSearcher().search( new TermQuery( new Term( field, filedValue ) ) );
+        Set<String> groups = new LinkedHashSet<String>( Math.max( 10, hits.length() ) );
+        if ( hits.length() > 0 )
+        {
+            Document doc = hits.doc( 0 );
+    
+            String groupList = doc.get( listField );
+    
+            if ( groupList != null )
+            {
+                groups.addAll( Arrays.asList( groupList.split( "\\|" ) ) );
+            }
+        }
+    
+        return groups;
+    }
+
+    static void setGroups( IndexingContext context, Collection<String> groups, String groupField, String groupFieldValue, String groupListField )
+        throws IOException,
+            CorruptIndexException
+    {
+        IndexWriter w = context.getIndexWriter();
+    
+        w.updateDocument( new Term( groupField, groupFieldValue ), createGroupsDocument(
+            groups,
+            groupField,
+            groupFieldValue,
+            groupListField ) );
+    
+        w.commit();
+    }
+    
+    static Document createGroupsDocument( Collection<String> groups, String field, String fieldValue, String listField )
+    {
+        Document groupDoc = new Document();
+    
+        groupDoc.add( new Field( field, //
+            fieldValue,
+            Field.Store.YES,
+            Field.Index.UN_TOKENIZED ) );
+    
+        groupDoc.add( new Field( listField, //
+            ArtifactInfo.lst2str( groups ),
+            Field.Store.YES,
+            Field.Index.NO ) );
+    
+        return groupDoc;
+    }
+    
+
+    // close helpers
+
+    public static void close( OutputStream os )
+    {
+        if ( os != null )
+        {
+            try
+            {
+                os.close();
+            }
+            catch ( IOException e )
+            {
+                // ignore
+            }
+        }
+    }
+
+    public static void close( InputStream is )
+    {
+        if ( is != null )
+        {
+            try
+            {
+                is.close();
+            }
+            catch ( IOException e )
+            {
+                // ignore
+            }
+        }
+    }
+
+    public static void close( IndexOutput io )
+    {
+        if ( io != null )
+        {
+            try
+            {
+                io.close();
+            }
+            catch ( IOException e )
+            {
+                // ignore
+            }
+        }
+    }
+
+    public static void close( IndexInput in )
+    {
+        if ( in != null )
+        {
+            try
+            {
+                in.close();
+            }
+            catch ( IOException e )
+            {
+                // ignore
+            }
+        }
+    }
+
+    public static void close( IndexReader r )
+    {
+        if ( r != null )
+        {
+            try
+            {
+                r.close();
+            }
+            catch ( IOException e )
+            {
+                // ignore
+            }
+        }
+    }
+
+    public static void close( IndexWriter w )
+    {
+        if ( w != null )
+        {
+            try
+            {
+                w.close();
+            }
+            catch ( IOException e )
+            {
+                // ignore
+            }
+        }
+    }
+
+    public static void close( Directory d )
+    {
+        if ( d != null )
+        {
+            try
+            {
+                d.close();
+            }
+            catch ( IOException e )
+            {
+                // ignore
+            }
+        }
+    }
+
+    public static void delete( File indexDir )
+    {
+        try
+        {
+            FileUtils.deleteDirectory( indexDir );
+        }
+        catch ( IOException ex )
+        {
+            // ignore
+        }
+    }
+
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexUtils.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexingContext.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexingContext.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexingContext.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexingContext.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,289 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.context;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.store.Directory;
+import org.apache.maven.index.artifact.GavCalculator;
+
+/**
+ * An indexing context is representing artifact repository for indexing and searching. Indexing context is a statefull
+ * component, it keeps state of index readers and writers.
+ * 
+ * @author Jason van Zyl
+ * @author Tamas Cservenak
+ * @author Eugene Kuleshov
+ */
+public interface IndexingContext
+{
+    /**
+     * Standard name of the full repository index that is used when clients requesting index information have nothing to
+     * start with.
+     */
+    public static final String INDEX_FILE = "nexus-maven-repository-index";
+
+    /**
+     * A prefix used for all index property names
+     */
+    public static final String INDEX_PROPERTY_PREFIX = "nexus.index.";
+
+    /**
+     * A property name used to specify index id
+     */
+    public static final String INDEX_ID = INDEX_PROPERTY_PREFIX + "id";
+
+    /**
+     * A property name used to specify legacy index timestam (the last update time)
+     */
+    public static final String INDEX_LEGACY_TIMESTAMP = INDEX_PROPERTY_PREFIX + "time";
+
+    /**
+     * A property name used to specify index timtestamp
+     */
+    public static final String INDEX_TIMESTAMP = INDEX_PROPERTY_PREFIX + "timestamp";
+
+    /**
+     * A prefix used to specify an incremental update chunk name
+     */
+    public static final String INDEX_CHUNK_PREFIX = INDEX_PROPERTY_PREFIX + "incremental-";
+
+    /**
+     * A date format used for index timestamp
+     */
+    public static final String INDEX_TIME_FORMAT = "yyyyMMddHHmmss.SSS Z";
+
+    /**
+     * A date format used for incremental update chunk names
+     */
+    public static final String INDEX_TIME_DAY_FORMAT = "yyyyMMdd";
+
+    /**
+     * A counter used to id the chunks
+     */
+    public static final String INDEX_CHUNK_COUNTER = INDEX_PROPERTY_PREFIX + "last-incremental";
+
+    /**
+     * An id that defines the current incremental chain. If when checking remote repo, the index chain doesnt match
+     * you'll know that you need to download the full index
+     */
+    public static final String INDEX_CHAIN_ID = INDEX_PROPERTY_PREFIX + "chain-id";
+
+    /**
+     * Returns this indexing context id.
+     */
+    String getId();
+
+    /**
+     * Returns repository id.
+     */
+    String getRepositoryId();
+
+    /**
+     * Returns location for the local repository.
+     */
+    File getRepository();
+
+    /**
+     * Returns public repository url.
+     */
+    String getRepositoryUrl();
+
+    /**
+     * Returns url for the index update
+     */
+    String getIndexUpdateUrl();
+
+    /**
+     * Is the context searchable when doing "non-targeted" searches? Ie. Should it take a part when searching without
+     * specifying context?
+     * 
+     * @return
+     */
+    boolean isSearchable();
+
+    /**
+     * Sets is the context searchable when doing "non-targeted" searches.
+     * 
+     * @param searchable
+     */
+    void setSearchable( boolean searchable );
+
+    /**
+     * Returns index update time
+     */
+    Date getTimestamp();
+
+    void updateTimestamp()
+        throws IOException;
+
+    void updateTimestamp( boolean save )
+        throws IOException;
+
+    void updateTimestamp( boolean save, Date date )
+        throws IOException;
+
+    /**
+     * Returns the Lucene IndexReader of this context.
+     * 
+     * @return reader
+     * @throws IOException
+     */
+    IndexReader getIndexReader()
+        throws IOException;
+
+    /**
+     * Returns the Lucene IndexSearcher of this context.
+     * 
+     * @return searcher
+     * @throws IOException
+     */
+    IndexSearcher getIndexSearcher()
+        throws IOException;
+
+    /**
+     * Returns the ReadOnly IndexSearcher of this context. IndexingContext will manage this searcher in similar way as
+     * the RW readers. -- THIS IS NOT YET FULLY DONE, DO NOT USE IT!
+     * 
+     * @return
+     * @throws IOException
+     * @deprecated Do not use it yet! Not done!
+     */
+    IndexSearcher getReadOnlyIndexSearcher()
+        throws IOException;
+
+    /**
+     * Returns the Lucene IndexWriter of this context.
+     * 
+     * @return indexWriter
+     * @throws IOException
+     */
+    IndexWriter getIndexWriter()
+        throws IOException;
+
+    /**
+     * List of IndexCreators used in this context.
+     * 
+     * @return list of index creators.
+     */
+    List<IndexCreator> getIndexCreators();
+
+    /**
+     * Returns the Lucene Analyzer of this context used for by IndexWriter and IndexSearcher. Note: this method always
+     * creates a new instance of analyzer!
+     * 
+     * @return
+     */
+    Analyzer getAnalyzer();
+
+    /**
+     * Optimizes index
+     */
+    void optimize()
+        throws CorruptIndexException, IOException;
+
+    /**
+     * Shuts down this context.
+     */
+    void close( boolean deleteFiles )
+        throws IOException;
+
+    /**
+     * Purge (cleans) the context, deletes/empties the index and restores the context to new/empty state.
+     * 
+     * @throws IOException
+     */
+    void purge()
+        throws IOException;
+
+    /**
+     * Merges content of given Lucene directory with this context.
+     * 
+     * @param directory - the directory to merge
+     */
+    void merge( Directory directory )
+        throws IOException;
+
+    /**
+     * Merges content of given Lucene directory with this context, but filters out the unwanted ones.
+     * 
+     * @param directory - the directory to merge
+     */
+    void merge( Directory directory, DocumentFilter filter )
+        throws IOException;
+
+    /**
+     * Replaces the Lucene index with the one from supplied directory.
+     * 
+     * @param directory
+     * @throws IOException
+     */
+    void replace( Directory directory )
+        throws IOException;
+
+    Directory getIndexDirectory();
+
+    File getIndexDirectoryFile();
+
+    /**
+     * Returns the GavCalculator for this Context. Implies repository layout.
+     */
+    GavCalculator getGavCalculator();
+
+    /**
+     * Sets all group names stored in the current indexing context
+     */
+    void setAllGroups( Collection<String> groups )
+        throws IOException;
+
+    /**
+     * Gets all group names stored in the current indexing context
+     */
+    Set<String> getAllGroups()
+        throws IOException;
+
+    /**
+     * Sets root group names stored in the current indexing context
+     */
+    void setRootGroups( Collection<String> groups )
+        throws IOException;
+
+    /**
+     * Gets root group names stored in the current indexing context
+     */
+    Set<String> getRootGroups()
+        throws IOException;
+
+    /**
+     * Rebuilds stored group names from the index
+     */
+    void rebuildGroups()
+        throws IOException;
+
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexingContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/IndexingContext.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusAnalyzer.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusAnalyzer.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusAnalyzer.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusAnalyzer.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,118 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.context;
+
+import java.io.Reader;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.CharTokenizer;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.Tokenizer;
+import org.apache.maven.index.creator.JarFileContentsIndexCreator;
+
+/**
+ * A Nexus specific analyzer. Only difference from Lucene's SimpleAnalyzer is that we use LetterOrDigitTokenizer instead
+ * of LowerCaseTokenizer. LetterOrDigitTokenizer does pretty much the same as LowerCaseTokenizer, it normalizes to lower
+ * case letter, but it takes letters and numbers too (as opposed to LowerCaseTokenizer) as token chars.
+ * 
+ * @author Eugene Kuleshov
+ * @author cstamas
+ */
+public class NexusAnalyzer
+    extends Analyzer
+{
+    public TokenStream tokenStream( String fieldName, Reader reader )
+    {
+        return getTokenizer( fieldName, reader );
+    }
+
+    protected Tokenizer getTokenizer( String fieldName, Reader reader )
+    {
+        if ( JarFileContentsIndexCreator.FLD_CLASSNAMES_KW.getKey().equals( fieldName ) )
+        {
+            // To keep "backward" compatibility, we have to use old flawed tokenizer.
+            return new DeprecatedClassnamesTokenizer( reader );
+        }
+        else
+        {
+            return new LetterOrDigitTokenizer( reader );
+        }
+    }
+
+    // ==
+
+    public static class NoopTokenizer
+        extends CharTokenizer
+    {
+        public NoopTokenizer( Reader in )
+        {
+            super( in );
+        }
+
+        @Override
+        protected boolean isTokenChar( char c )
+        {
+            return true;
+        }
+    }
+
+    @Deprecated
+    public static class DeprecatedClassnamesTokenizer
+        extends CharTokenizer
+    {
+        public DeprecatedClassnamesTokenizer( Reader in )
+        {
+            super( in );
+        }
+
+        @Override
+        protected boolean isTokenChar( char c )
+        {
+            return c != '\n';
+        }
+
+        @Override
+        protected char normalize( char c )
+        {
+            return Character.toLowerCase( c );
+        }
+    }
+
+    public static class LetterOrDigitTokenizer
+        extends CharTokenizer
+    {
+        public LetterOrDigitTokenizer( Reader in )
+        {
+            super( in );
+        }
+
+        @Override
+        protected boolean isTokenChar( char c )
+        {
+            return Character.isLetterOrDigit( c );
+        }
+
+        @Override
+        protected char normalize( char c )
+        {
+            return Character.toLowerCase( c );
+        }
+    }
+
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusAnalyzer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusAnalyzer.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexSearcher.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexSearcher.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexSearcher.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexSearcher.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,55 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.context;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.IndexSearcher;
+
+/**
+ * An extended Searcher, that holds reference to the IndexingContext that is a searcher for. Needed to provide "extra"
+ * data for search hits, that are not on index, and suppot ArtifactInfoPostprocessor's.
+ * 
+ * @author cstamas
+ */
+public class NexusIndexSearcher
+    extends IndexSearcher
+{
+    private final IndexingContext indexingContext;
+
+    public NexusIndexSearcher( final IndexingContext indexingContext )
+        throws IOException
+    {
+        this( indexingContext, indexingContext.getIndexReader() );
+    }
+
+    public NexusIndexSearcher( final IndexingContext indexingContext, final IndexReader reader )
+        throws IOException
+    {
+        super( reader );
+        
+        this.indexingContext = indexingContext;
+    }
+
+    public IndexingContext getIndexingContext()
+    {
+        return indexingContext;
+    }
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexSearcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexSearcher.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexWriter.java
URL: http://svn.apache.org/viewvc/maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexWriter.java?rev=1030748&view=auto
==============================================================================
--- maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexWriter.java (added)
+++ maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexWriter.java Wed Nov  3 22:51:12 2010
@@ -0,0 +1,90 @@
+/**
+ * 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.
+ */
+package org.apache.maven.index.context;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.LockObtainFailedException;
+
+/**
+ * An extension of <a
+ * href="http://lucene.apache.org/java/2_4_0/api/core/org/apache/lucene/index/IndexWriter.html">Lucene IndexWriter</a>
+ * to allow to track if writer is closed
+ */
+public class NexusIndexWriter
+    extends IndexWriter
+{
+    private boolean closed;
+
+    public NexusIndexWriter( final Directory directory, final Analyzer analyzer, boolean create )
+        throws CorruptIndexException, LockObtainFailedException, IOException
+    {
+        this( directory, analyzer, create, false /* autoCommit */);
+    }
+
+    public NexusIndexWriter( final Directory directory, final Analyzer analyzer, boolean create, boolean autoCommit )
+        throws CorruptIndexException, LockObtainFailedException, IOException
+    {
+        super( directory, autoCommit, analyzer, create );
+
+        this.closed = false;
+    }
+
+    @Override
+    public void close()
+        throws CorruptIndexException, IOException
+    {
+        super.close();
+
+        this.closed = true;
+    }
+
+    public boolean isClosed()
+    {
+        return closed;
+    }
+
+    public boolean hasUncommittedChanges()
+    {
+        try
+        {
+            Field pendingCommit = IndexWriter.class.getDeclaredField( "pendingCommit" );
+
+            pendingCommit.setAccessible( true );
+
+            return pendingCommit.get( this ) != null;
+        }
+        catch ( Exception x )
+        {
+            if ( x instanceof RuntimeException )
+            {
+                throw (RuntimeException) x;
+            }
+            else
+            {
+                throw new RuntimeException( "Could not access the \"IndexWriter.pendingCommit\" field!", x );
+            }
+        }
+    }
+}

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/indexer/trunk/indexer-core/src/main/java/org/apache/maven/index/context/NexusIndexWriter.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision