protected ArtifactRepository repository;
- protected boolean indexExists;
-
private Analyzer analyzer;
/**
{
try
{
- if ( indexExists )
+ if ( indexExists() )
{
indexWriter = new IndexWriter( indexPath, getAnalyzer(), false );
}
/**
* Check if the index already exists.
*
+ * @return true if the index already exists
* @throws IOException
* @throws RepositoryIndexException
*/
- protected void checkIfIndexExists()
+ protected boolean indexExists()
throws IOException, RepositoryIndexException
{
File indexDir = new File( indexPath );
if ( IndexReader.indexExists( indexDir ) )
{
- indexExists = true;
+ return true;
}
else if ( !indexDir.exists() )
{
- indexExists = false;
+ return false;
}
else if ( indexDir.isDirectory() )
{
}
/**
- * Checks if the object has already been indexed.
+ * Checks if the object has already been indexed and deletes it if it is.
*
* @param object the object to be indexed.
* @throws RepositoryIndexException
* @throws IOException
*/
- abstract void isIndexed( Object object )
+ abstract void deleteIfIndexed( Object object )
throws RepositoryIndexException, IOException;
/**
}
/**
- * @see AbstractRepositoryIndex#isIndexed(Object)
+ * @see AbstractRepositoryIndex#deleteIfIndexed(Object)
*/
- public void isIndexed( Object object )
+ public void deleteIfIndexed( Object object )
throws RepositoryIndexException, IOException
{
if ( object instanceof Artifact )
{
Artifact artifact = (Artifact) object;
- checkIfIndexExists();
- if ( indexExists )
+ if ( indexExists() )
{
validateIndex( FIELDS );
deleteDocument( FLD_ID, ARTIFACT + ":" + artifact.getId() );
try
{
- isIndexed( artifact );
+ deleteIfIndexed( artifact );
if ( !isOpen() )
{
open();
{
idx = 0;
}
- String classname = name.substring( idx, name.length() - 6 );
+ String classname = name.substring( idx + 1, name.length() - 6 );
classes.append( classname ).append( "\n" );
isAdded = true;
}
--- /dev/null
+package org.apache.maven.repository.indexing;
+
+/*
+ * Copyright 2005-2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.CharTokenizer;
+import org.apache.lucene.analysis.SimpleAnalyzer;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.document.DateField;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.repository.digest.Digester;
+import org.codehaus.plexus.util.FileUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @author Edwin Punzalan
+ */
+public class EclipseRepositoryIndex
+ extends AbstractRepositoryIndex
+{
+ private static final String JAR_NAME = "j";
+
+ private static final String JAR_SIZE = "s";
+
+ private static final String JAR_DATE = "d";
+
+ private static final String NAMES = "c";
+
+ private static final String MD5 = "m";
+
+ private Digester digester;
+
+
+ /**
+ * Class constructor
+ *
+ * @param indexPath the path where the lucene index will be created/updated.
+ * @param repository the repository where the indexed artifacts are located
+ * @param digester the digester object to generate the checksum strings
+ * @throws RepositoryIndexException
+ */
+ public EclipseRepositoryIndex( String indexPath, ArtifactRepository repository, Digester digester )
+ throws RepositoryIndexException
+ {
+ super( indexPath, repository );
+
+ this.digester = digester;
+ }
+
+ /**
+ * @see AbstractRepositoryIndex#getAnalyzer()
+ */
+ public Analyzer getAnalyzer()
+ {
+ return new EclipseIndexAnalyzer( new SimpleAnalyzer() );
+ }
+
+ /**
+ * Method to index a given artifact for use with the eclipse plugin
+ *
+ * @param artifact the Artifact object to be indexed
+ * @throws RepositoryIndexException
+ */
+ public void indexArtifact( Artifact artifact )
+ throws RepositoryIndexException
+ {
+ File artifactFile = artifact.getFile();
+ if ( artifactFile != null && artifactFile.getName().endsWith( ".jar" ) && artifactFile.exists() )
+ {
+ String md5;
+ try
+ {
+ md5 = digester.createChecksum( artifactFile, "MD5" );
+ }
+ catch ( FileNotFoundException e )
+ {
+ throw new RepositoryIndexException( "Unable to compute checksum.", e );
+ }
+ catch ( NoSuchAlgorithmException e )
+ {
+ throw new RepositoryIndexException( "Unable to compute checksum.", e );
+ }
+ catch ( IOException e )
+ {
+ throw new RepositoryIndexException( "Unable to compute checksum.", e );
+ }
+
+ StringBuffer classes;
+ try
+ {
+ // TODO: improve
+ classes = new StringBuffer();
+ if ( "jar".equals( artifact.getType() ) )
+ {
+ ZipFile jar = new ZipFile( artifact.getFile() );
+
+ for ( Enumeration entries = jar.entries(); entries.hasMoreElements(); )
+ {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ addIfClassEntry( entry, classes );
+ }
+ }
+ }
+ catch ( ZipException e )
+ {
+ throw new RepositoryIndexException( "Error reading from artifact file: " + artifact.getFile(), e );
+ }
+ catch ( IOException e )
+ {
+ throw new RepositoryIndexException( "Error reading from artifact file", e );
+ }
+
+ Document doc = new Document();
+ doc.add( Field.Text( MD5, md5 ) );
+ doc.add( Field.Text( JAR_NAME, artifactFile.getName() ) );
+ doc.add( Field.Text( JAR_DATE, DateField.timeToString( artifactFile.lastModified() ) ) );
+ doc.add( Field.Text( JAR_SIZE, Long.toString( artifactFile.length() ) ) );
+ doc.add( Field.Text( NAMES, classes.toString() ) );
+
+ try
+ {
+ if ( !isOpen() )
+ {
+ open();
+ }
+ getIndexWriter().addDocument( doc );
+ }
+ catch ( IOException e )
+ {
+ throw new RepositoryIndexException( "Error opening index.", e );
+ }
+ }
+ }
+
+ /**
+ * method to create an archived copy of the index contents
+ *
+ * @return File object to the archive
+ * @throws IOException
+ */
+ public File getCompressedCopy()
+ throws IOException
+ {
+ File indexPath = new File( getIndexPath() );
+ String name = indexPath.getName();
+
+ File outputFile = new File( indexPath.getParent(), name + ".zip" );
+ FileUtils.fileDelete( outputFile.getAbsolutePath() );
+
+ ZipOutputStream zos = new ZipOutputStream( new FileOutputStream( outputFile ) );
+ zos.setLevel( 9 );
+
+ File[] files = indexPath.listFiles();
+ try
+ {
+ for ( int i = 0; i < files.length; i++ )
+ {
+ ZipEntry e = new ZipEntry( files[i].getName() );
+ zos.putNextEntry( e );
+
+ FileInputStream is = new FileInputStream( files[i] );
+ byte[] buf = new byte[4096];
+ int n;
+ try
+ {
+ while ( ( n = is.read( buf ) ) > 0 )
+ {
+ zos.write( buf, 0, n );
+ }
+ }
+ finally
+ {
+ is.close();
+ }
+ zos.flush();
+
+ zos.closeEntry();
+ }
+ }
+ finally
+ {
+ zos.close();
+ }
+
+ return outputFile;
+ }
+
+ /**
+ * Method to test a zip entry if it is a java class, and adds it to the classes buffer
+ *
+ * @param entry the zip entry to test for java class
+ * @param classes the String buffer to add the java class if the test result as true
+ * @return true if the zip entry is a java class and was successfully added to the buffer
+ */
+ protected boolean addIfClassEntry( ZipEntry entry, StringBuffer classes )
+ {
+ boolean isAdded = false;
+
+ String name = entry.getName();
+ if ( name.endsWith( ".class" ) )
+ {
+ // TODO verify if class is public or protected
+ if ( name.lastIndexOf( "$" ) == -1 )
+ {
+ int idx = name.lastIndexOf( '/' );
+ if ( idx < 0 )
+ {
+ idx = 0;
+ }
+ String classname = name.substring( idx + 1, name.length() - 6 );
+ classes.append( classname ).append( "\n" );
+ isAdded = true;
+ }
+ }
+
+ return isAdded;
+ }
+
+ /**
+ * @see AbstractRepositoryIndex#deleteIfIndexed(Object)
+ */
+ public void deleteIfIndexed( Object object )
+ throws RepositoryIndexException, IOException
+ {
+ if ( object instanceof Artifact )
+ {
+ Artifact artifact = (Artifact) object;
+ if ( indexExists() )
+ {
+ validateIndex( FIELDS );
+
+ String md5;
+ try
+ {
+ md5 = digester.createChecksum( artifact.getFile(), "MD5" );
+ }
+ catch ( FileNotFoundException e )
+ {
+ throw new RepositoryIndexException( "Unable to create index.", e );
+ }
+ catch ( NoSuchAlgorithmException e )
+ {
+ throw new RepositoryIndexException( "Unable to create index.", e );
+ }
+ catch ( IOException e )
+ {
+ throw new RepositoryIndexException( "Unable to create index.", e );
+ }
+
+ deleteDocument( MD5, md5 );
+ }
+ }
+ else
+ {
+ throw new RepositoryIndexException( "Object is not of type artifact." );
+ }
+ }
+
+ /**
+ * Class used to analyze the lucene index
+ */
+ private class EclipseIndexAnalyzer
+ extends Analyzer
+ {
+ private Analyzer defaultAnalyzer;
+
+ /**
+ * constructor to for this analyzer
+ *
+ * @param defaultAnalyzer the analyzer to use as default for the general fields of the artifact indeces
+ */
+ public EclipseIndexAnalyzer( Analyzer defaultAnalyzer )
+ {
+ this.defaultAnalyzer = defaultAnalyzer;
+ }
+
+ /**
+ * Method called by lucence during indexing operations
+ *
+ * @param fieldName the field name that the lucene object is currently processing
+ * @param reader a Reader object to the index stream
+ * @return an analyzer to specific to the field name or the default analyzer if none is present
+ */
+ public TokenStream tokenStream( String fieldName, Reader reader )
+ {
+ TokenStream tokenStream;
+
+ if ( "s".equals( fieldName ) )
+ {
+ tokenStream = new EclipseIndexTokenizer( reader );
+ }
+ else
+ {
+ tokenStream = defaultAnalyzer.tokenStream( fieldName, reader );
+ }
+
+ return tokenStream;
+ }
+
+ /**
+ * Class used to tokenize the eclipse index
+ */
+ private class EclipseIndexTokenizer
+ extends CharTokenizer
+ {
+ /**
+ * Constructor with the required reader to the index stream
+ *
+ * @param reader the Reader object of the index stream
+ */
+ EclipseIndexTokenizer( Reader reader )
+ {
+ super( reader );
+ }
+
+ /**
+ * method that lucene calls to check tokenization of a stream character
+ *
+ * @param character char currently being processed
+ * @return true if the char is a token, false if the char is a stop char
+ */
+ protected boolean isTokenChar( char character )
+ {
+ return true;
+ }
+ }
+ }
+}
\r
try\r
{\r
- isIndexed( repoMetadata );\r
+ deleteIfIndexed( repoMetadata );\r
if ( !isOpen() )\r
{\r
open();\r
}\r
\r
/**\r
- * @see org.apache.maven.repository.indexing.AbstractRepositoryIndex#isIndexed(Object)\r
+ * @see org.apache.maven.repository.indexing.AbstractRepositoryIndex#deleteIfIndexed(Object)\r
*/\r
- public void isIndexed( Object object )\r
+ public void deleteIfIndexed( Object object )\r
throws RepositoryIndexException, IOException\r
{\r
if ( object instanceof RepositoryMetadata )\r
{\r
RepositoryMetadata repoMetadata = (RepositoryMetadata) object;\r
- checkIfIndexExists();\r
- if ( indexExists )\r
+ if ( indexExists() )\r
{\r
validateIndex( FIELDS );\r
deleteDocument( FLD_ID, (String) repoMetadata.getKey() );\r
}
/**
- * @see org.apache.maven.repository.indexing.AbstractRepositoryIndex#isIndexed(Object)
+ * @see org.apache.maven.repository.indexing.AbstractRepositoryIndex#deleteIfIndexed(Object)
*/
- public void isIndexed( Object object )
+ public void deleteIfIndexed( Object object )
throws RepositoryIndexException, IOException
{
if ( object instanceof Model )
{
Model pom = (Model) object;
- checkIfIndexExists();
- if ( indexExists )
+ if ( indexExists() )
{
validateIndex( FIELDS );
deleteDocument( FLD_ID, POM + ":" + pom.getId() );
try
{
- isIndexed( pom );
+ deleteIfIndexed( pom );
if ( !isOpen() )
{
open();
ArtifactRepositoryIndex indexer = factory.createArtifactRepositoryIndex( indexPath, repository );
try
{
- indexer.isIndexed( new Object() );
+ indexer.deleteIfIndexed( new Object() );
fail( "Must throw exception on object not of type artifact." );
}
catch ( RepositoryIndexException e )
--- /dev/null
+package org.apache.maven.repository.indexing;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Hits;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
+import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
+import org.apache.maven.repository.digest.DefaultDigester;
+import org.apache.maven.repository.digest.Digester;
+import org.codehaus.plexus.PlexusTestCase;
+import org.codehaus.plexus.util.FileUtils;
+
+import java.io.File;
+
+/*
+ * Copyright 2005-2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Edwin Punzalan
+ */
+public class EclipseRepositoryIndexTest
+ extends PlexusTestCase
+{
+ private ArtifactFactory artifactFactory;
+
+ private ArtifactRepository repository;
+
+ private String indexPath;
+
+ private Digester digester;
+
+ protected void setUp()
+ throws Exception
+ {
+ super.setUp();
+
+ File repositoryDirectory = getTestFile( "src/test/repository" );
+ String repoDir = repositoryDirectory.toURL().toString();
+ ArtifactRepositoryLayout layout = (ArtifactRepositoryLayout) lookup( ArtifactRepositoryLayout.ROLE, "default" );
+ ArtifactRepositoryFactory repoFactory = (ArtifactRepositoryFactory) lookup( ArtifactRepositoryFactory.ROLE );
+ repository = repoFactory.createArtifactRepository( "test", repoDir, layout, null, null );
+ digester = new DefaultDigester();
+
+ indexPath = "target/index";
+ FileUtils.deleteDirectory( indexPath );
+ }
+
+ /**
+ * Create an index that will be used for testing.
+ * Indexing process: check if the object was already indexed [ checkIfIndexed(Object) ], open the index [ open() ],
+ * index the object [ index(Object) ], optimize the index [ optimize() ] and close the index [ close() ].
+ *
+ * @throws Exception
+ */
+ private EclipseRepositoryIndex createTestIndex()
+ throws Exception
+ {
+ EclipseRepositoryIndex indexer = new EclipseRepositoryIndex( indexPath, repository, new DefaultDigester() );
+
+ Artifact artifact = getArtifact( "org.apache.maven", "maven-artifact", "2.0.1" );
+ artifact.setFile( new File( repository.getBasedir(), repository.pathOf( artifact ) ) );
+ indexer.indexArtifact( artifact );
+ indexer.optimize();
+ indexer.close();
+
+ artifact = getArtifact( "org.apache.maven", "maven-model", "2.0" );
+ artifact.setFile( new File( repository.getBasedir(), repository.pathOf( artifact ) ) );
+ indexer.indexArtifact( artifact );
+ indexer.optimize();
+ indexer.close();
+
+ artifact = getArtifact( "test", "test-artifactId", "1.0" );
+ artifact.setFile( new File( repository.getBasedir(), repository.pathOf( artifact ) ) );
+ indexer.indexArtifact( artifact );
+ indexer.optimize();
+ indexer.close();
+
+ artifact = getArtifact( "test", "test-artifactId", "1.0" );
+ artifact.setFile( new File( repository.getBasedir(), repository.pathOf( artifact ) ) );
+ indexer.indexArtifact( artifact );
+ indexer.optimize();
+ indexer.close();
+
+ return indexer;
+ }
+
+ /**
+ * Method for testing the exceptions thrown by ArtifactRepositoryIndex
+ *
+ * @throws Exception
+ */
+ public void testIndexerExceptions()
+ throws Exception
+ {
+ Artifact artifact = getArtifact( "test", "test-artifactId", "1.0" );
+ artifact.setFile( new File( repository.getBasedir(), repository.pathOf( artifact ) ) );
+
+ try
+ {
+ String notIndexDir = new File( "pom.xml" ).getAbsolutePath();
+ EclipseRepositoryIndex indexer = new EclipseRepositoryIndex( notIndexDir, repository, digester );
+ indexer.indexArtifact( artifact );
+ fail( "Must throw exception on non-directory index directory" );
+ }
+ catch ( RepositoryIndexException e )
+ {
+ assertTrue( true );
+ }
+
+ try
+ {
+ String notIndexDir = new File( "" ).getAbsolutePath();
+ EclipseRepositoryIndex indexer = new EclipseRepositoryIndex( notIndexDir, repository, digester );
+ indexer.indexArtifact( artifact );
+ fail( "Must throw an exception on a non-index directory" );
+ }
+ catch ( RepositoryIndexException e )
+ {
+ assertTrue( true );
+ }
+
+ EclipseRepositoryIndex indexer = new EclipseRepositoryIndex( indexPath, repository, digester );
+ try
+ {
+ indexer.deleteIfIndexed( new Object() );
+ fail( "Must throw exception on object not of type artifact." );
+ }
+ catch ( RepositoryIndexException e )
+ {
+ assertTrue( true );
+ }
+ }
+
+ /**
+ * Test the ArtifactRepositoryIndex using a single-phrase search.
+ *
+ * @throws Exception
+ */
+ public void testSearch()
+ throws Exception
+ {
+ EclipseRepositoryIndex index = createTestIndex();
+
+ IndexSearcher searcher = new IndexSearcher( index.getIndexPath() );
+ try
+ {
+ QueryParser parser = new QueryParser( "j", index.getAnalyzer() );
+ Hits hits = searcher.search( parser.parse( "maven-artifact-2.0.1.jar" ) );
+
+ assertEquals( "Total hits", 1, hits.length() );
+ Document doc = hits.doc( 0 );
+ assertEquals( "Check jar name", "maven-artifact-2.0.1.jar", doc.get( "j" ) );
+
+ parser = new QueryParser( "s", index.getAnalyzer() );
+ hits = searcher.search( parser.parse( "78377" ) );
+
+ assertEquals( "Total hits", 1, hits.length() );
+ doc = hits.doc( 0 );
+ assertEquals( "Check jar name", "maven-artifact-2.0.1.jar", doc.get( "j" ) );
+
+ parser = new QueryParser( "d", index.getAnalyzer() );
+ hits = searcher.search( parser.parse( "0eii7ckc0" ) );
+
+ assertEquals( "Total hits", 1, hits.length() );
+ doc = hits.doc( 0 );
+ assertEquals( "Check jar name", "maven-artifact-2.0.1.jar", doc.get( "j" ) );
+
+ parser = new QueryParser( "m", index.getAnalyzer() );
+ hits = searcher.search( parser.parse( "AE55D9B5720E11B6CF19FE1E31A42E51" ) );
+
+ assertEquals( "Total hits", 1, hits.length() );
+ doc = hits.doc( 0 );
+ assertEquals( "Check jar name", "maven-artifact-2.0.1.jar", doc.get( "j" ) );
+
+ parser = new QueryParser( "c", index.getAnalyzer() );
+ hits = searcher.search( parser.parse( "MavenXpp3Reader" ) );
+
+ assertEquals( "Total hits", 1, hits.length() );
+ doc = hits.doc( 0 );
+ assertEquals( "Check jar name", "maven-model-2.0.jar", doc.get( "j" ) );
+
+ parser = new QueryParser( "j", index.getAnalyzer() );
+ hits = searcher.search( parser.parse( "maven" ) );
+
+ assertEquals( "Total hits", 2, hits.length() );
+ }
+ finally
+ {
+ searcher.close();
+ }
+ }
+
+ /**
+ * Method for creating artifact object
+ *
+ * @param groupId the groupId of the artifact to be created
+ * @param artifactId the artifactId of the artifact to be created
+ * @param version the version of the artifact to be created
+ * @return Artifact object
+ * @throws Exception
+ */
+ private Artifact getArtifact( String groupId, String artifactId, String version )
+ throws Exception
+ {
+ if ( artifactFactory == null )
+ {
+ artifactFactory = (ArtifactFactory) lookup( ArtifactFactory.ROLE );
+ }
+
+ return artifactFactory.createBuildArtifact( groupId, artifactId, version, "jar" );
+ }
+
+ protected void tearDown()
+ throws Exception
+ {
+ repository = null;
+
+ super.tearDown();
+ }
+}
\r
try\r
{\r
- indexer.isIndexed( new Object() );\r
+ indexer.deleteIfIndexed( new Object() );\r
fail( "Must throw exception when the passed object is not of type metadata." );\r
}\r
catch ( RepositoryIndexException e )\r
PomRepositoryIndex indexer = factory.createPomRepositoryIndex( indexPath, repository );
try
{
- indexer.isIndexed( new Object() );
+ indexer.deleteIfIndexed( new Object() );
fail( "Must throw exception when the passed object is not of type model." );
}
catch ( RepositoryIndexException e )