diff options
35 files changed, 1389 insertions, 194 deletions
diff --git a/archiva-docs/src/site/apt/release-notes.apt b/archiva-docs/src/site/apt/release-notes.apt index 55f9bef7e..aae94d0d7 100644 --- a/archiva-docs/src/site/apt/release-notes.apt +++ b/archiva-docs/src/site/apt/release-notes.apt @@ -65,6 +65,8 @@ Release Notes for Archiva 1.1 * [MRM-801] - Create documentation for virtual repositories + * [MRM-861] - Paginate Search Results page + ** Bug * [MRM-234] - Moving files between repos through webdav doesn't work @@ -75,6 +77,8 @@ Release Notes for Archiva 1.1 * [MRM-538] - Browse > Info not synchronised with updated POM contents + * [MRM-550] - proxy connectors - move up/down should not show when it's not possible + * [MRM-584] - Newly created user cannot login * [MRM-597] - Cannot run Archiva as another user because su is run with option "-" (--login) @@ -93,6 +97,8 @@ Release Notes for Archiva 1.1 * [MRM-638] - some developer reports are appearing in the archiva-docs module and need to be disabled + * [MRM-650] - Deleted metadata files still appear in search results + * [MRM-652] - Invalid html links in "webdav" directory browsing * [MRM-653] - WebDAV deploy fails with error @@ -175,6 +181,14 @@ Release Notes for Archiva 1.1 * [MRM-867] - 500 on HEAD method + * [MRM-869] - many empty folders created in managed repos with proxy connectors when used in a group + + * [MRM-870] - addRepositoryToGroup.action => AbstractMethodError on DeferredDocumentImpl.getXmlStandalone() + + * [MRM-871] - jetty.host and jetty.port properties are set by default in jetty.xml preventing user from using other hostname if user is not aware of these properties + + * [MRM-872] - empty metadatas frmo a repository group + ** Improvement * [MRM-591] - Reports should show from which repository the defective artifact was found. @@ -187,6 +201,8 @@ Release Notes for Archiva 1.1 * [MRM-678] - 404 errors on proxies should be cached + * [MRM-737] - Don't list metadata files in the search results + * [MRM-752] - Trim URL in Remote Repositories * [MRM-760] - set JSW to auto-restart if it crashes or hangs @@ -217,10 +233,14 @@ Release Notes for Archiva 1.1 * [MRM-694] - Virtual repositories or repository grouping + * [MRM-730] - Index/Search by class, method or package name + * [MRM-751] - Support database connection pooling * [MRM-774] - Support inclusion of pom file when deploying an artifact via the web upload form + * [MRM-864] - Searching within search results + ** Task * [MRM-677] - Upgrade archiva to the latest redback (1.0.1) @@ -230,7 +250,7 @@ Release Notes for Archiva 1.1 * [MRM-708] - Migrate from Plexus Logging to Slf4J * [MRM-754] - Update the screenshots in the docs - + * Changes in Archiva 1.0.2 ** Bug diff --git a/archiva-jetty/src/main/conf/jetty.xml b/archiva-jetty/src/main/conf/jetty.xml index 5a106f08e..53a600fd8 100644 --- a/archiva-jetty/src/main/conf/jetty.xml +++ b/archiva-jetty/src/main/conf/jetty.xml @@ -50,17 +50,6 @@ --> </Set> - - <Call class="java.lang.System" name="setProperty"> - <Arg>jetty.port</Arg> - <Arg>8080</Arg> - </Call> - - <Call class="java.lang.System" name="setProperty"> - <Arg>jetty.host</Arg> - <Arg>localhost</Arg> - </Call> - <!-- =========================================================== --> <!-- Set connectors --> <!-- =========================================================== --> diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/pom.xml b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/pom.xml index 100605093..14de945ce 100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/pom.xml +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/pom.xml @@ -49,6 +49,11 @@ <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-spring</artifactId> <scope>test</scope> - </dependency> + </dependency> + <dependency> + <groupId>org.apache.bcel</groupId> + <artifactId>bcel</artifactId> + <version>5.2</version> + </dependency> </dependencies> </project> diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/main/java/org/apache/maven/archiva/consumers/lucene/IndexContentConsumer.java b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/main/java/org/apache/maven/archiva/consumers/lucene/IndexContentConsumer.java index 26d0cca6c..77fab6ea4 100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/main/java/org/apache/maven/archiva/consumers/lucene/IndexContentConsumer.java +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/main/java/org/apache/maven/archiva/consumers/lucene/IndexContentConsumer.java @@ -36,10 +36,13 @@ import org.apache.maven.archiva.repository.ManagedRepositoryContent; import org.apache.maven.archiva.repository.RepositoryContentFactory; import org.apache.maven.archiva.repository.RepositoryException; import org.apache.maven.archiva.repository.layout.LayoutException; +import org.apache.maven.archiva.repository.metadata.MetadataTools; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; import org.codehaus.plexus.registry.Registry; import org.codehaus.plexus.registry.RegistryListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; @@ -60,6 +63,8 @@ public class IndexContentConsumer extends AbstractMonitoredConsumer implements KnownRepositoryContentConsumer, RegistryListener, Initializable { + private Logger log = LoggerFactory.getLogger( IndexContentConsumer.class ); + private static final String READ_CONTENT = "read_content"; private static final String INDEX_ERROR = "indexing_error"; @@ -147,6 +152,12 @@ public class IndexContentConsumer public void processFile( String path ) throws ConsumerException { + if ( path.endsWith( "/" + MetadataTools.MAVEN_METADATA ) ) + { + log.debug( "File is a metadata file. Not indexing." ); + return; + } + FileContentRecord record = new FileContentRecord(); try { diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/main/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumer.java b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/main/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumer.java index 9f04c61d7..755755225 100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/main/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumer.java +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/main/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumer.java @@ -22,14 +22,34 @@ package org.apache.maven.archiva.consumers.lucene; import org.apache.maven.archiva.consumers.AbstractMonitoredConsumer; import org.apache.maven.archiva.consumers.ConsumerException; import org.apache.maven.archiva.consumers.DatabaseUnprocessedArtifactConsumer; +import org.apache.maven.archiva.indexer.RepositoryContentIndex; +import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory; +import org.apache.maven.archiva.indexer.RepositoryIndexException; +import org.apache.maven.archiva.indexer.bytecode.BytecodeRecord; import org.apache.maven.archiva.model.ArchivaArtifact; +import org.apache.maven.archiva.repository.ManagedRepositoryContent; +import org.apache.maven.archiva.repository.RepositoryContentFactory; +import org.apache.maven.archiva.repository.RepositoryException; +import com.sun.org.apache.bcel.internal.classfile.ClassParser; +import com.sun.org.apache.bcel.internal.classfile.JavaClass; +import com.sun.org.apache.bcel.internal.classfile.Method; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /** * IndexJavaPublicMethodsConsumer * * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a> + * <a href="mailto:oching@apache.org">Maria Odea Ching</a> * @version $Id$ * * @plexus.component role="org.apache.maven.archiva.consumers.DatabaseUnprocessedArtifactConsumer" @@ -49,11 +69,40 @@ public class IndexJavaPublicMethodsConsumer * @plexus.configuration default-value="Index the java public methods for Full Text Search." */ private String description; + + /** + * @plexus.requirement role-hint="lucene" + */ + private RepositoryContentIndexFactory repoIndexFactory; + + /** + * @plexus.requirement + */ + private RepositoryContentFactory repoFactory; + + private static final String CLASSES = "classes"; + + private static final String METHODS = "methods"; + + private List<String> includes = new ArrayList<String>(); + public IndexJavaPublicMethodsConsumer() + { + includes.add( "jar" ); + includes.add( "war" ); + includes.add( "ear" ); + includes.add( "zip" ); + includes.add( "tar.gz" ); + includes.add( "tar.bz2" ); + includes.add( "car" ); + includes.add( "sar" ); + includes.add( "mar" ); + includes.add( "rar" ); + } + public void beginScan() { - // TODO Auto-generated method stub - + // TODO Auto-generated method stubx } public void completeScan() @@ -63,16 +112,58 @@ public class IndexJavaPublicMethodsConsumer } public List<String> getIncludedTypes() - { - // TODO Auto-generated method stub - return null; + { + return includes; } public void processArchivaArtifact( ArchivaArtifact artifact ) throws ConsumerException - { - // TODO Auto-generated method stub - + { + try + { + ManagedRepositoryContent repoContent = + repoFactory.getManagedRepositoryContent( artifact.getModel().getRepositoryId() ); + File file = new File( repoContent.getRepoRoot(), repoContent.toPath( artifact ) ); + + if( file.getAbsolutePath().endsWith( ".jar" ) || file.getAbsolutePath().endsWith( ".war" ) || + file.getAbsolutePath().endsWith( ".ear" ) || file.getAbsolutePath().endsWith( ".zip" ) || + file.getAbsolutePath().endsWith( ".tar.gz" ) || file.getAbsolutePath().endsWith( ".tar.bz2" ) || + file.getAbsolutePath().endsWith( ".car" ) || file.getAbsolutePath().endsWith( ".sar" ) || + file.getAbsolutePath().endsWith( ".mar" ) || file.getAbsolutePath().endsWith( ".rar" ) ) + { + + if( file.exists() ) + { + List<String> files = readFilesInArchive( file ); + Map<String, List<String>> mapOfClassesAndMethods = + getPublicClassesAndMethodsFromFiles( file.getAbsolutePath(), files ); + + // NOTE: what about public variables? should these be indexed too? + RepositoryContentIndex bytecodeIndex = repoIndexFactory.createBytecodeIndex( repoContent.getRepository() ); + + BytecodeRecord bytecodeRecord = new BytecodeRecord(); + bytecodeRecord.setFilename( file.getName() ); + bytecodeRecord.setClasses( mapOfClassesAndMethods.get( CLASSES ) ); + bytecodeRecord.setFiles( files ); + bytecodeRecord.setMethods( mapOfClassesAndMethods.get( METHODS ) ); + bytecodeRecord.setArtifact( artifact ); + bytecodeRecord.setRepositoryId( artifact.getModel().getRepositoryId() ); + bytecodeIndex.modifyRecord( bytecodeRecord ); + } + } + } + catch ( RepositoryException e ) + { + throw new ConsumerException( "Can't run index cleanup consumer: " + e.getMessage() ); + } + catch ( RepositoryIndexException e ) + { + throw new ConsumerException( "Error encountered while adding artifact to index: " + e.getMessage() ); + } + catch ( IOException e ) + { + throw new ConsumerException( "Error encountered while getting file contents: " + e.getMessage() ); + } } public String getDescription() @@ -89,5 +180,89 @@ public class IndexJavaPublicMethodsConsumer { return false; } + + private List<String> readFilesInArchive( File file ) + throws IOException + { + ZipFile zipFile = new ZipFile( file ); + List<String> files; + + try + { + files = new ArrayList<String>( zipFile.size() ); + for ( Enumeration entries = zipFile.entries(); entries.hasMoreElements(); ) + { + ZipEntry entry = (ZipEntry) entries.nextElement(); + files.add( entry.getName() ); + } + } + finally + { + closeQuietly( zipFile ); + } + return files; + } + + private void closeQuietly( ZipFile zipFile ) + { + try + { + if ( zipFile != null ) + { + zipFile.close(); + } + } + catch ( IOException e ) + { + // ignored + } + } + + private static boolean isClass( String name ) + { + return name.endsWith( ".class" ) && name.lastIndexOf( "$" ) < 0; + } + + private Map<String, List<String>> getPublicClassesAndMethodsFromFiles( String zipFile, List<String> files ) + { + Map<String, List<String>> map = new HashMap<String, List<String>>(); + List<String> methods = new ArrayList<String>(); + List<String> classes = new ArrayList<String>(); + + for( String file : files ) + { + if( isClass( file ) ) + { + try + { + ClassParser parser = new ClassParser( zipFile, file ); + JavaClass javaClass = parser.parse(); + + if( javaClass.isPublic() ) + { + classes.add( javaClass.getClassName() ); + } + + Method[] methodsArr = javaClass.getMethods(); + for( Method method : methodsArr ) + { + if( method.isPublic() ) + { + methods.add( method.getName() ); + } + } + } + catch ( IOException e ) + { + // ignore + } + } + } + + map.put( CLASSES, classes ); + map.put( METHODS, methods ); + + return map; + } } diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumerTest.java b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumerTest.java new file mode 100644 index 000000000..83ae9588b --- /dev/null +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumerTest.java @@ -0,0 +1,124 @@ +package org.apache.maven.archiva.consumers.lucene; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.archiva.configuration.ArchivaConfiguration; +import org.apache.maven.archiva.configuration.Configuration; +import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; +import org.apache.maven.archiva.consumers.DatabaseUnprocessedArtifactConsumer; +import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory; +import org.apache.maven.archiva.indexer.search.SearchResultLimits; +import org.apache.maven.archiva.indexer.search.SearchResults; +import org.apache.maven.archiva.model.ArchivaArtifact; +import org.apache.maven.archiva.model.ArchivaArtifactModel; +import org.codehaus.plexus.spring.PlexusInSpringTestCase; + +/** + * + * @author <a href="mailto:oching@apache.org">Maria Odea Ching</a> + * @version + * + */ +public class IndexJavaPublicMethodsConsumerTest + extends PlexusInSpringTestCase +{ + DatabaseUnprocessedArtifactConsumer indexMethodsConsumer; + + IndexJavaPublicMethodsCrossRepositorySearch searcher; + + private RepositoryContentIndexFactory indexFactory; + + public void setUp() + throws Exception + { + super.setUp(); + indexMethodsConsumer = + (DatabaseUnprocessedArtifactConsumer) lookup( DatabaseUnprocessedArtifactConsumer.class, + "index-public-methods" ); + + ManagedRepositoryConfiguration config = new ManagedRepositoryConfiguration(); + config.setId( "test-repo" ); + config.setLayout( "default" ); + config.setLocation( getBasedir() + "/target/test-classes/test-repo" ); + config.setName( "Test Repository" ); + + addRepoToConfiguration( "index-public-methods", config ); + + indexFactory = (RepositoryContentIndexFactory) lookup (RepositoryContentIndexFactory.class, "lucene" ); + searcher = new IndexJavaPublicMethodsCrossRepositorySearch( config, indexFactory ); + } + + private void addRepoToConfiguration( String configHint, ManagedRepositoryConfiguration repoConfiguration ) + throws Exception + { + ArchivaConfiguration archivaConfiguration = + (ArchivaConfiguration) lookup( ArchivaConfiguration.class, configHint ); + Configuration configuration = archivaConfiguration.getConfiguration(); + configuration.removeManagedRepository( configuration.findManagedRepositoryById( repoConfiguration.getId() ) ); + configuration.addManagedRepository( repoConfiguration ); + } + + public void testJarPublicMethods() + throws Exception + { + ArchivaArtifact artifact = + createArtifact( "org.apache.archiva", "archiva-index-methods-jar-test", "1.0", "jar" ); + indexMethodsConsumer.processArchivaArtifact( artifact ); + + List<String> selectedRepos = new ArrayList<String>(); + selectedRepos.add( "test-repo" ); + + // search for class names + SearchResults results = searcher.searchForBytecode( "", selectedRepos, "FirstPackageApp", new SearchResultLimits( 0 ) ); + assertEquals( 1, results.getTotalHits() ); + + results = searcher.searchForBytecode( "", selectedRepos, "SecondPackageApp", new SearchResultLimits( 0 ) ); + assertEquals( 1, results.getTotalHits() ); + + // search for public methods + results = searcher.searchForBytecode( "", selectedRepos, "appMethodOne", new SearchResultLimits( 0 ) ); + assertEquals( 1, results.getTotalHits() ); + + // should return only the overridding public method in SecondPackageApp + results = searcher.searchForBytecode( "", selectedRepos, "protectedMethod", new SearchResultLimits( 0 ) ); + assertEquals( 1, results.getTotalHits() ); + + // should not return any private methods + results = searcher.searchForBytecode( "", selectedRepos, "privMethod", new SearchResultLimits( 0 ) ); + assertEquals( 0, results.getTotalHits() ); + + // test for public variables? + } + + private ArchivaArtifact createArtifact( String groupId, String artifactId, String version, String type ) + { + ArchivaArtifactModel model = new ArchivaArtifactModel(); + model.setGroupId( groupId ); + model.setArtifactId( artifactId ); + model.setVersion( version ); + model.setType( type ); + model.setRepositoryId( "test-repo" ); + + return new ArchivaArtifact( model ); + } +} diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsCrossRepositorySearch.java b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsCrossRepositorySearch.java new file mode 100644 index 000000000..ea9f13311 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/java/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsCrossRepositorySearch.java @@ -0,0 +1,191 @@ +package org.apache.maven.archiva.consumers.lucene; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.lucene.document.Document; +import org.apache.lucene.queryParser.ParseException; +import org.apache.lucene.queryParser.QueryParser; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.MultiSearcher; +import org.apache.lucene.search.Searchable; +import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; +import org.apache.maven.archiva.indexer.RepositoryContentIndex; +import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory; +import org.apache.maven.archiva.indexer.RepositoryIndexSearchException; +import org.apache.maven.archiva.indexer.bytecode.BytecodeHandlers; +import org.apache.maven.archiva.indexer.lucene.LuceneEntryConverter; +import org.apache.maven.archiva.indexer.lucene.LuceneQuery; +import org.apache.maven.archiva.indexer.lucene.LuceneRepositoryContentRecord; +import org.apache.maven.archiva.indexer.search.SearchResultLimits; +import org.apache.maven.archiva.indexer.search.SearchResults; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Searcher used for testing purposes only. + * + * @author <a href="mailto:oching@apache.org">Maria Odea Ching</a> + * @version + */ +public class IndexJavaPublicMethodsCrossRepositorySearch +{ + private Logger log = LoggerFactory.getLogger( IndexJavaPublicMethodsCrossRepositorySearch.class ); + + private ManagedRepositoryConfiguration localIndexedRepo; + + private RepositoryContentIndexFactory indexFactory; + + public IndexJavaPublicMethodsCrossRepositorySearch( ManagedRepositoryConfiguration localIndexedRepo, RepositoryContentIndexFactory indexFactory ) + { + this.localIndexedRepo = localIndexedRepo; + this.indexFactory = indexFactory; + } + + public SearchResults searchForBytecode( String principal, List<String> selectedRepos, String term, + SearchResultLimits limits ) throws ParseException + { + List<RepositoryContentIndex> indexes = new ArrayList<RepositoryContentIndex>(); + indexes.add( indexFactory.createBytecodeIndex( localIndexedRepo ) ); + + QueryParser parser = new BytecodeHandlers().getQueryParser(); + LuceneQuery query = new LuceneQuery( parser.parse( term ) ); + SearchResults results = searchAll( query, limits, indexes ); + results.getRepositories().add( localIndexedRepo ); + + return results; + } + + private SearchResults searchAll( LuceneQuery luceneQuery, SearchResultLimits limits, List<RepositoryContentIndex> indexes ) + { + org.apache.lucene.search.Query specificQuery = luceneQuery.getLuceneQuery(); + + SearchResults results = new SearchResults(); + + if ( indexes.isEmpty() ) + { + // No point going any further. + return results; + } + + // Setup the converter + LuceneEntryConverter converter = null; + RepositoryContentIndex index = indexes.get( 0 ); + converter = index.getEntryConverter(); + + // Process indexes into an array of Searchables. + List<Searchable> searchableList = toSearchables( indexes ); + + Searchable searchables[] = new Searchable[searchableList.size()]; + searchableList.toArray( searchables ); + + MultiSearcher searcher = null; + + try + { + // Create a multi-searcher for looking up the information. + searcher = new MultiSearcher( searchables ); + + // Perform the search. + Hits hits = searcher.search( specificQuery ); + + int hitCount = hits.length(); + + // Now process the limits. + results.setLimits( limits ); + results.setTotalHits( hitCount ); + + int fetchCount = limits.getPageSize(); + int offset = ( limits.getSelectedPage() * limits.getPageSize() ); + + if ( limits.getSelectedPage() == SearchResultLimits.ALL_PAGES ) + { + fetchCount = hitCount; + offset = 0; + } + + // Goto offset. + if ( offset < hitCount ) + { + // only process if the offset is within the hit count. + for ( int i = 0; i <= fetchCount; i++ ) + { + // Stop fetching if we are past the total # of available hits. + if ( offset + i >= hitCount ) + { + break; + } + + try + { + Document doc = hits.doc( offset + i ); + LuceneRepositoryContentRecord record = converter.convert( doc ); + results.addHit( record ); + } + catch ( java.text.ParseException e ) + { + log.error( e.getMessage() ); + } + } + } + + } + catch ( IOException e ) + { + log.error( e.getMessage() ); + } + finally + { + try + { + if ( searcher != null ) + { + searcher.close(); + } + } + catch ( IOException ie ) + { + log.error( ie.getMessage() ); + } + } + + return results; + } + + private List<Searchable> toSearchables( List<RepositoryContentIndex> indexes ) + { + List<Searchable> searchableList = new ArrayList<Searchable>(); + for ( RepositoryContentIndex contentIndex : indexes ) + { + try + { + searchableList.add( contentIndex.getSearchable() ); + } + catch ( RepositoryIndexSearchException e ) + { + log.error( e.getMessage() ); + } + } + return searchableList; + } +} diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumerTest.xml b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumerTest.xml new file mode 100644 index 000000000..93f760b56 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/org/apache/maven/archiva/consumers/lucene/IndexJavaPublicMethodsConsumerTest.xml @@ -0,0 +1,64 @@ +<component-set> + <components> +<!-- for testing repo purge using retention count --> + <component> + <role>org.apache.maven.archiva.consumers.DatabaseUnprocessedArtifactConsumer</role> + <role-hint>index-public-methods</role-hint> + <implementation>org.apache.maven.archiva.consumers.lucene.IndexJavaPublicMethodsConsumer</implementation> + <requirements> + <requirement> + <role>org.apache.maven.archiva.repository.RepositoryContentFactory</role> + <role-hint>index-public-methods</role-hint> + </requirement> + <requirement> + <role>org.apache.maven.archiva.indexer.RepositoryContentIndexFactory</role> + <role-hint>lucene</role-hint> + <field-name>repoIndexFactory</field-name> + </requirement> + </requirements> + </component> + <component> + <role>org.apache.maven.archiva.repository.RepositoryContentFactory</role> + <role-hint>index-public-methods</role-hint> + <implementation>org.apache.maven.archiva.repository.RepositoryContentFactory</implementation> + <description>RepositoryContentRequest</description> + <requirements> + <requirement> + <role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role> + <role-hint>index-public-methods</role-hint> + <field-name>archivaConfiguration</field-name> + </requirement> + </requirements> + </component> + <component> + <role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role> + <role-hint>index-public-methods</role-hint> + <implementation>org.apache.maven.archiva.configuration.DefaultArchivaConfiguration</implementation> + <requirements> + <requirement> + <role>org.codehaus.plexus.registry.Registry</role> + <role-hint>index-public-methods</role-hint> + </requirement> + <requirement> + <role>org.apache.maven.archiva.policies.PreDownloadPolicy</role> + <field-name>prePolicies</field-name> + </requirement> + <requirement> + <role>org.apache.maven.archiva.policies.PostDownloadPolicy</role> + <field-name>postPolicies</field-name> + </requirement> + </requirements> + </component> + <component> + <role>org.codehaus.plexus.registry.Registry</role> + <role-hint>index-public-methods</role-hint> + <implementation>org.codehaus.plexus.registry.commons.CommonsConfigurationRegistry</implementation> + <configuration> + <properties> + <xml fileName="${basedir}/src/test/conf/repository-manager.xml" + config-name="org.apache.maven.archiva" config-at="org.apache.maven.archiva"/> + </properties> + </configuration> + </component> + </components> +</component-set>
\ No newline at end of file diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/test-repo/org/apache/archiva/archiva-index-methods-jar-test/1.0/archiva-index-methods-jar-test-1.0.jar b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/test-repo/org/apache/archiva/archiva-index-methods-jar-test/1.0/archiva-index-methods-jar-test-1.0.jar Binary files differnew file mode 100644 index 000000000..cc03dacc1 --- /dev/null +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/test-repo/org/apache/archiva/archiva-index-methods-jar-test/1.0/archiva-index-methods-jar-test-1.0.jar diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/test-repo/org/apache/archiva/archiva-index-methods-jar-test/1.0/pom.xml b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/test-repo/org/apache/archiva/archiva-index-methods-jar-test/1.0/pom.xml new file mode 100644 index 000000000..e7bf54bda --- /dev/null +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-lucene-consumers/src/test/resources/test-repo/org/apache/archiva/archiva-index-methods-jar-test/1.0/pom.xml @@ -0,0 +1,18 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.apache.archiva</groupId> + <artifactId>archiva-index-methods-jar-test</artifactId> + <packaging>jar</packaging> + <version>1.0</version> + <name>archiva-index-methods-jar-test</name> + <url>http://maven.apache.org</url> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/bytecode/BytecodeRecord.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/bytecode/BytecodeRecord.java index 1cb7422a9..888067da5 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/bytecode/BytecodeRecord.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/bytecode/BytecodeRecord.java @@ -39,28 +39,28 @@ public class BytecodeRecord private String filename; - private List classes; + private List<String> classes; - private List methods; + private List<String> methods; - private List files; + private List<String> files; public ArchivaArtifact getArtifact() { return artifact; } - public List getClasses() + public List<String> getClasses() { return classes; } - public List getFiles() + public List<String> getFiles() { return files; } - public List getMethods() + public List<String> getMethods() { return methods; } @@ -92,17 +92,17 @@ public class BytecodeRecord this.artifact = artifact; } - public void setClasses( List classes ) + public void setClasses( List<String> classes ) { this.classes = classes; } - public void setFiles( List files ) + public void setFiles( List<String> files ) { this.files = files; } - public void setMethods( List methods ) + public void setMethods( List<String> methods ) { this.methods = methods; } diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/filecontent/FileContentRecord.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/filecontent/FileContentRecord.java index 7b1365b23..af039c8c7 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/filecontent/FileContentRecord.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/filecontent/FileContentRecord.java @@ -64,7 +64,7 @@ public class FileContentRecord public String getPrimaryKey() { - return filename; + return repositoryId + ":" + filename; } public int hashCode() diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java index 73c8cf61a..63e21151c 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java @@ -40,6 +40,19 @@ public interface CrossRepositorySearch public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, SearchResultLimits limits ); /** + * Search for a specific term from the previous search results. + * + * @param principal the user doing the search. + * @param selectedRepos the repositories to search from. + * @param term the term to search for. + * @param limits the limits to apply to the search results. + * @param previousSearchTerms the list of the previous search terms. + * @return the results + */ + public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, + SearchResultLimits limits, List<String> previousSearchTerms ); + + /** * Search for the specific bytecode across all repositories. * * @param term the term to search for. diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java index 98490ebe4..8565acc46 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java @@ -27,8 +27,12 @@ import org.apache.lucene.document.Document; import org.apache.lucene.queryParser.MultiFieldQueryParser; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Filter; import org.apache.lucene.search.Hits; import org.apache.lucene.search.MultiSearcher; +import org.apache.lucene.search.QueryWrapperFilter; import org.apache.lucene.search.Searchable; import org.apache.maven.archiva.configuration.ArchivaConfiguration; import org.apache.maven.archiva.configuration.ConfigurationNames; @@ -84,7 +88,7 @@ public class DefaultCrossRepositorySearch QueryParser parser = new MultiFieldQueryParser( new String[]{HashcodesKeys.MD5, HashcodesKeys.SHA1}, new HashcodesHandlers().getAnalyzer() ); LuceneQuery query = new LuceneQuery( parser.parse( checksum ) ); - SearchResults results = searchAll( query, limits, indexes ); + SearchResults results = searchAll( query, limits, indexes, null ); results.getRepositories().addAll( this.localIndexedRepositories ); return results; @@ -100,13 +104,13 @@ public class DefaultCrossRepositorySearch public SearchResults searchForBytecode( String principal, List<String> selectedRepos, String term, SearchResultLimits limits ) { - List<RepositoryContentIndex> indexes = getHashcodeIndexes( principal, selectedRepos ); + List<RepositoryContentIndex> indexes = getBytecodeIndexes( principal, selectedRepos ); try { QueryParser parser = new BytecodeHandlers().getQueryParser(); LuceneQuery query = new LuceneQuery( parser.parse( term ) ); - SearchResults results = searchAll( query, limits, indexes ); + SearchResults results = searchAll( query, limits, indexes, null ); results.getRepositories().addAll( this.localIndexedRepositories ); return results; @@ -120,17 +124,44 @@ public class DefaultCrossRepositorySearch return new SearchResults(); } + public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, SearchResultLimits limits ) { + return searchForTerm( principal, selectedRepos, term, limits, null ); + } + + public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, + SearchResultLimits limits, List<String> previousSearchTerms ) + { List<RepositoryContentIndex> indexes = getFileContentIndexes( principal, selectedRepos ); + List<RepositoryContentIndex> bytecodeIndices = getBytecodeIndexes( principal, selectedRepos ); + indexes.addAll( bytecodeIndices ); try { QueryParser parser = new FileContentHandlers().getQueryParser(); - LuceneQuery query = new LuceneQuery( parser.parse( term ) ); - SearchResults results = searchAll( query, limits, indexes ); + LuceneQuery query = null; + SearchResults results = null; + if( previousSearchTerms == null || previousSearchTerms.isEmpty() ) + { + query = new LuceneQuery( parser.parse( term ) ); + results = searchAll( query, limits, indexes, null ); + } + else + { + // AND the previous search terms + BooleanQuery booleanQuery = new BooleanQuery(); + for( String previousSearchTerm : previousSearchTerms ) + { + booleanQuery.add( parser.parse( previousSearchTerm ), BooleanClause.Occur.MUST ); + } + + query = new LuceneQuery( booleanQuery ); + Filter filter = new QueryWrapperFilter( parser.parse( term ) ); + results = searchAll( query, limits, indexes, filter ); + } results.getRepositories().addAll( this.localIndexedRepositories ); - + return results; } catch ( ParseException e ) @@ -139,10 +170,10 @@ public class DefaultCrossRepositorySearch } // empty results. - return new SearchResults(); + return new SearchResults(); } - - private SearchResults searchAll( LuceneQuery luceneQuery, SearchResultLimits limits, List<RepositoryContentIndex> indexes ) + + private SearchResults searchAll( LuceneQuery luceneQuery, SearchResultLimits limits, List<RepositoryContentIndex> indexes, Filter filter ) { org.apache.lucene.search.Query specificQuery = luceneQuery.getLuceneQuery(); @@ -173,7 +204,15 @@ public class DefaultCrossRepositorySearch searcher = new MultiSearcher( searchables ); // Perform the search. - Hits hits = searcher.search( specificQuery ); + Hits hits = null; + if( filter != null ) + { + hits = searcher.search( specificQuery, filter ); + } + else + { + hits = searcher.search( specificQuery ); + } int hitCount = hits.length(); @@ -194,7 +233,7 @@ public class DefaultCrossRepositorySearch if ( offset < hitCount ) { // only process if the offset is within the hit count. - for ( int i = 0; i <= fetchCount; i++ ) + for ( int i = 0; i < fetchCount; i++ ) { // Stop fetching if we are past the total # of available hits. if ( offset + i >= hitCount ) diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResultHit.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResultHit.java index 1d2f74437..2933fae8e 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResultHit.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResultHit.java @@ -46,6 +46,8 @@ public class SearchResultHit private String artifactId; private String version = ""; + + private String repositoryId = ""; // Advanced hit, if artifact, all versions of artifact private List artifacts = new ArrayList(); @@ -138,4 +140,14 @@ public class SearchResultHit { return versions; } + + public String getRepositoryId() + { + return repositoryId; + } + + public void setRepositoryId( String repositoryId ) + { + this.repositoryId = repositoryId; + } } diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResults.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResults.java index 6e2f8fa80..6465814c3 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResults.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/SearchResults.java @@ -126,6 +126,7 @@ public class SearchResults // Only need to worry about this hit if it is truely new. hit = new SearchResultHit(); + hit.setRepositoryId( filecontent.getRepositoryId() ); hit.setUrl( filecontent.getRepositoryId() + "/" + filecontent.getFilename() ); hit.setContext( null ); // TODO: handle context + highlight later. diff --git a/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java b/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java index a503d6f8b..576b54481 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java @@ -140,7 +140,7 @@ public class DefaultCrossRepositorySearchTest "org","org2","org3","org4","org5","org6","org7" }; - assertSearchResults( expectedRepos, expectedResults, search, "org" ); + assertSearchResults( expectedRepos, expectedResults, search, "org", null ); } public void testSearchTerm_Junit() @@ -156,7 +156,7 @@ public class DefaultCrossRepositorySearchTest "junit","junit2","junit3" }; - assertSearchResults( expectedRepos, expectedResults, search, "junit" ); + assertSearchResults( expectedRepos, expectedResults, search, "junit", null ); } public void testSearchInvalidTerm() @@ -172,10 +172,49 @@ public class DefaultCrossRepositorySearchTest // Nothing. }; - assertSearchResults( expectedRepos, expectedResults, search, "monosodium" ); + assertSearchResults( expectedRepos, expectedResults, search, "monosodium", null ); } - private void assertSearchResults( String expectedRepos[], String expectedResults[], CrossRepositorySearch search, String term ) + public void testSearchWithinSearchResults() + throws Exception + { + CrossRepositorySearch search = lookupCrossRepositorySearch(); + + String expectedRepos[] = new String[] { + TEST_DEFAULT_REPO_ID + }; + + String expectedResults[] = new String[] { + "org","org2","org3","org4","org5","org6","org7" + }; + + // first search + assertSearchResults( expectedRepos, expectedResults, search, "org", null ); + + List<String> previousSearchTerms = new ArrayList<String>(); + previousSearchTerms.add( "org" ); + String secondSearchExpectedResults[] = new String[] { + "org.apache.maven.archiva.record", "org.apache.maven.archiva.record2", + "org.apache.maven.archiva.record3", "org.apache.maven.archiva.record4", + "org.apache.maven.archiva.record5", "org.apache.maven.archiva.record6", + "org.apache.maven.archiva.record7" + }; + + //second search + assertSearchResults( expectedRepos, secondSearchExpectedResults, search, "org.apache.maven.archiva.record", + previousSearchTerms ); + + previousSearchTerms.add( "org.apache.maven.archiva.record" ); + String thirdSearchExpectedResults[] = new String[] { + "junit", "junit2", "junit3" + }; + + //third search + assertSearchResults( expectedRepos, thirdSearchExpectedResults, search, "junit", previousSearchTerms ); + } + + private void assertSearchResults( String expectedRepos[], String expectedResults[], CrossRepositorySearch search, + String term, List<String> previousSearchTerms ) throws Exception { SearchResultLimits limits = new SearchResultLimits( 0 ); @@ -184,7 +223,15 @@ public class DefaultCrossRepositorySearchTest List<String> selectedRepos = new ArrayList<String>(); selectedRepos.addAll( Arrays.asList( expectedRepos ) ); - SearchResults results = search.searchForTerm( "guest", selectedRepos, term, limits ); + SearchResults results = null; + if( previousSearchTerms == null ) + { + results = search.searchForTerm( "guest", selectedRepos, term, limits ); + } + else + { + results = search.searchForTerm( "guest", selectedRepos, term, limits, previousSearchTerms ); + } assertNotNull( "Search Results should not be null.", results ); assertEquals( "Repository Hits", expectedRepos.length, results.getRepositories().size() ); diff --git a/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/maven/archiva/proxy/DefaultRepositoryProxyConnectors.java b/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/maven/archiva/proxy/DefaultRepositoryProxyConnectors.java index 398734973..c2937fe3e 100644 --- a/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/maven/archiva/proxy/DefaultRepositoryProxyConnectors.java +++ b/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/maven/archiva/proxy/DefaultRepositoryProxyConnectors.java @@ -88,7 +88,7 @@ public class DefaultRepositoryProxyConnectors implements RepositoryProxyConnectors, RegistryListener, Initializable { private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class ); - + /** * @plexus.requirement */ @@ -169,7 +169,7 @@ public class DefaultRepositoryProxyConnectors try { File downloadedFile = - transferFile( connector, targetRepository, targetPath, localFile, requestProperties ); + transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties ); if ( fileExists( downloadedFile ) ) { @@ -230,7 +230,7 @@ public class DefaultRepositoryProxyConnectors try { - transferFile( connector, targetRepository, targetPath, localRepoFile, requestProperties ); + transferFile( connector, targetRepository, targetPath, repository, localRepoFile, requestProperties ); if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) ) { @@ -348,7 +348,7 @@ public class DefaultRepositoryProxyConnectors long originalMetadataTimestamp = getLastModified( localRepoFile ); try { - transferFile( connector, targetRepository, targetPath, localRepoFile, requestProperties ); + transferFile( connector, targetRepository, targetPath, repository, localRepoFile, requestProperties ); if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) ) { @@ -487,6 +487,7 @@ public class DefaultRepositoryProxyConnectors * @param connector the connector configuration to use. * @param remoteRepository the remote repository get the resource from. * @param remotePath the path in the remote repository to the resource to get. + * @param repository the managed repository that will hold the file * @param localFile the local file to place the downloaded resource into * @param requestProperties the request properties to utilize for policy handling. * @return the local file that was downloaded, or null if not downloaded. @@ -496,7 +497,7 @@ public class DefaultRepositoryProxyConnectors * @throws ProxyException if transfer was unsuccessful. */ private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath, - File localFile, Properties requestProperties ) + ManagedRepositoryContent repository, File localFile, Properties requestProperties ) throws ProxyException, NotModifiedException { String url = remoteRepository.getURL().getUrl(); @@ -560,10 +561,10 @@ public class DefaultRepositoryProxyConnectors boolean connected = connectToRepository( connector, wagon, remoteRepository ); if ( connected ) { - localFile = transferSimpleFile( wagon, remoteRepository, remotePath, localFile ); + localFile = transferSimpleFile( wagon, remoteRepository, remotePath, repository, localFile ); - transferChecksum( wagon, remoteRepository, remotePath, localFile, ".sha1" ); - transferChecksum( wagon, remoteRepository, remotePath, localFile, ".md5" ); + transferChecksum( wagon, remoteRepository, remotePath, repository, localFile, ".sha1" ); + transferChecksum( wagon, remoteRepository, remotePath, repository, localFile, ".md5" ); } } catch ( NotFoundException e ) @@ -627,12 +628,13 @@ public class DefaultRepositoryProxyConnectors * @param wagon the wagon instance (should already be connected) to use. * @param remoteRepository the remote repository to transfer from. * @param remotePath the remote path to the resource to get. + * @param repository the managed repository that will hold the file * @param localFile the local file that should contain the downloaded contents * @param type the type of checksum to transfer (example: ".md5" or ".sha1") * @throws ProxyException if copying the downloaded file into place did not succeed. */ private void transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath, - File localFile, String type ) + ManagedRepositoryContent repository, File localFile, String type ) throws ProxyException { String url = remoteRepository.getURL().getUrl() + remotePath; @@ -646,7 +648,7 @@ public class DefaultRepositoryProxyConnectors try { File hashFile = new File( localFile.getAbsolutePath() + type ); - transferSimpleFile( wagon, remoteRepository, remotePath + type, hashFile ); + transferSimpleFile( wagon, remoteRepository, remotePath + type, repository, hashFile ); log.debug( "Checksum" + type + " Downloaded: " + hashFile ); } catch ( NotFoundException e ) @@ -675,13 +677,14 @@ public class DefaultRepositoryProxyConnectors * @param wagon the wagon instance to use. * @param remoteRepository the remote repository to use * @param remotePath the remote path to attempt to get + * @param repository the managed repository that will hold the file * @param localFile the local file to save to * @return The local file that was transfered. * @throws ProxyException if there was a problem moving the downloaded file into place. * @throws WagonException if there was a problem tranfering the file. */ private File transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath, - File localFile ) + ManagedRepositoryContent repository, File localFile ) throws ProxyException { assert ( remotePath != null ); @@ -691,9 +694,8 @@ public class DefaultRepositoryProxyConnectors try { - localFile.getParentFile().mkdirs(); - temp = File.createTempFile(localFile.getName() + ".", null, localFile.getParentFile()); - + temp = File.createTempFile(localFile.getName() + ".", null, new File( repository.getRepoRoot() )); + boolean success = false; if ( !localFile.exists() ) @@ -849,6 +851,7 @@ public class DefaultRepositoryProxyConnectors throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() ); } + target.getParentFile().mkdirs(); if ( !temp.renameTo( target ) ) { log.warn( "Unable to rename tmp file to its final name... resorting to copy command." ); @@ -1004,15 +1007,15 @@ public class DefaultRepositoryProxyConnectors { /* do nothing */ } - + private void logProcess( String managedRepoId, String resource, String event ) { - + } - + private void logRejection( String managedRepoId, String remoteRepoId, String resource, String reason ) { - + } private void initConnectorsAndNetworkProxies() diff --git a/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/AbstractProxyTestCase.java b/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/AbstractProxyTestCase.java index db4a91dc2..e71ea65b0 100644 --- a/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/AbstractProxyTestCase.java +++ b/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/AbstractProxyTestCase.java @@ -88,7 +88,7 @@ public abstract class AbstractProxyTestCase protected static final String REPOPATH_LEGACY_MANAGED = "src/test/repositories/legacy-managed"; protected static final String REPOPATH_LEGACY_MANAGED_TARGET = "target/test-repository/legacy-managed"; - + protected static final ArgumentsMatcher customWagonGetIfNewerMatcher = new ArgumentsMatcher() { public boolean matches(Object[] expected, Object[] actual) { @@ -103,10 +103,10 @@ public abstract class AbstractProxyTestCase return ArrayUtils.toString(arguments); } }; - + protected static final ArgumentsMatcher customWagonGetMatcher = new ArgumentsMatcher() { - public boolean matches(Object[] expected, Object[] actual) + public boolean matches(Object[] expected, Object[] actual) { if (expected.length == 2 && actual.length == 2) { @@ -114,23 +114,23 @@ public abstract class AbstractProxyTestCase { return true; } - + if (expected[0] == null) { return actual[0] == null; } - + if (actual[0] == null) { return expected[0] == null; } - + return expected[0].equals(actual[0]); } return false; } - public String toString(Object[] arguments) + public String toString(Object[] arguments) { return ArrayUtils.toString(arguments); } @@ -540,6 +540,9 @@ public abstract class AbstractProxyTestCase FileUtils.deleteDirectory( destDir ); } + // Make the destination dir. + destDir.mkdirs(); + // Test the source dir. if ( !sourceDir.exists() ) { @@ -555,9 +558,6 @@ public abstract class AbstractProxyTestCase fail( "Unable to setup testable managed repository, source is not a directory: " + sourceDir ); } - // Make the destination dir. - destDir.mkdirs(); - // Copy directory structure. copyDirectoryStructure( sourceDir, destDir ); } diff --git a/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/CacheFailuresTransferTest.java b/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/CacheFailuresTransferTest.java index 70aec32fc..119211d6c 100644 --- a/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/CacheFailuresTransferTest.java +++ b/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/CacheFailuresTransferTest.java @@ -63,11 +63,11 @@ public class CacheFailuresTransferTest SnapshotsPolicy.ALWAYS, CachedFailuresPolicy.YES ); saveConnector( ID_DEFAULT_MANAGED, "badproxied2", ChecksumPolicy.FIX, ReleasesPolicy.ALWAYS, SnapshotsPolicy.ALWAYS, CachedFailuresPolicy.YES ); - + wagonMock.get( path, new File( expectedFile.getParentFile(), expectedFile.getName() + ".tmp" ) ); - + wagonMockControl.setMatcher(customWagonGetMatcher); - + wagonMockControl.setThrowable( new ResourceDoesNotExistException( "resource does not exist." ), 2 ); wagonMockControl.replay(); @@ -75,11 +75,11 @@ public class CacheFailuresTransferTest File downloadedFile = proxyHandler.fetchFromProxies( managedDefaultRepository, artifact ); wagonMockControl.verify(); - - // Second attempt to download same artifact use cache + + // Second attempt to download same artifact use cache wagonMockControl.reset(); wagonMockControl.replay(); - downloadedFile = proxyHandler.fetchFromProxies( managedDefaultRepository, artifact ); + downloadedFile = proxyHandler.fetchFromProxies( managedDefaultRepository, artifact ); wagonMockControl.verify(); assertNotDownloaded( downloadedFile ); @@ -108,7 +108,7 @@ public class CacheFailuresTransferTest SnapshotsPolicy.ALWAYS, CachedFailuresPolicy.NO ); wagonMock.get( path, new File( expectedFile.getParentFile(), expectedFile.getName() + ".tmp" ) ); - + wagonMockControl.setMatcher(customWagonGetMatcher); wagonMockControl.setThrowable( new ResourceDoesNotExistException( "resource does not exist." ), 2 ); @@ -118,15 +118,15 @@ public class CacheFailuresTransferTest wagonMockControl.verify(); - // Second attempt to download same artifact DOES NOT use cache + // Second attempt to download same artifact DOES NOT use cache wagonMockControl.reset(); wagonMock.get( path, new File( expectedFile.getParentFile(), expectedFile.getName() + ".tmp" ) ); - + wagonMockControl.setMatcher(customWagonGetMatcher); wagonMockControl.setThrowable( new ResourceDoesNotExistException( "resource does not exist." ), 2 ); wagonMockControl.replay(); - downloadedFile = proxyHandler.fetchFromProxies( managedDefaultRepository, artifact ); + downloadedFile = proxyHandler.fetchFromProxies( managedDefaultRepository, artifact ); wagonMockControl.verify(); @@ -138,6 +138,7 @@ public class CacheFailuresTransferTest throws Exception { String path = "org/apache/maven/test/get-in-second-proxy/1.0/get-in-second-proxy-1.0.jar"; + setupTestableManagedRepository( path ); File expectedFile = new File( managedDefaultDir, path ); ArtifactReference artifact = managedDefaultRepository.toArtifactReference( path ); diff --git a/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/ErrorHandlingTest.java b/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/ErrorHandlingTest.java index c0c60a720..4b96766c7 100644 --- a/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/ErrorHandlingTest.java +++ b/archiva-modules/archiva-base/archiva-proxy/src/test/java/org/apache/maven/archiva/proxy/ErrorHandlingTest.java @@ -551,7 +551,7 @@ public class ErrorHandlingTest private File createExpectedTempFile( File expectedFile ) { - return new File( expectedFile.getParentFile(), expectedFile.getName() + ".tmp" ).getAbsoluteFile(); + return new File( managedDefaultDir, expectedFile.getName() + ".tmp" ).getAbsoluteFile(); } private void confirmSingleFailure( String path, String id ) diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java index 6e91a5799..92b203c84 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java @@ -20,6 +20,7 @@ package org.apache.maven.archiva.web.action; */ import java.net.MalformedURLException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -34,8 +35,11 @@ import org.apache.maven.archiva.indexer.RepositoryIndexSearchException; import org.apache.maven.archiva.indexer.search.CrossRepositorySearch; import org.apache.maven.archiva.indexer.search.SearchResultLimits; import org.apache.maven.archiva.indexer.search.SearchResults; -import org.apache.maven.archiva.security.*; +import org.apache.maven.archiva.security.AccessDeniedException; +import org.apache.maven.archiva.security.ArchivaSecurityException; import org.apache.maven.archiva.security.ArchivaXworkUser; +import org.apache.maven.archiva.security.PrincipalNotFoundException; +import org.apache.maven.archiva.security.UserRepositories; import org.codehaus.plexus.xwork.action.PlexusActionSupport; /** @@ -45,7 +49,7 @@ import org.codehaus.plexus.xwork.action.PlexusActionSupport; */ public class SearchAction extends PlexusActionSupport -{ +{ /** * Query string. */ @@ -76,6 +80,16 @@ public class SearchAction private static final String ARTIFACT = "artifact"; private List databaseResults; + + private int currentPage = 0; + + private int totalPages; + + private boolean searchResultsOnly; + + private String completeQueryString; + + private static final String COMPLETE_QUERY_STRING_SEPARATOR = ";"; public String quickSearch() throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException @@ -87,8 +101,8 @@ public class SearchAction */ assert q != null && q.length() != 0; - - SearchResultLimits limits = new SearchResultLimits( 0 ); + + SearchResultLimits limits = new SearchResultLimits( currentPage ); List<String> selectedRepos = getObservableRepos(); if ( CollectionUtils.isEmpty( selectedRepos ) ) @@ -96,14 +110,28 @@ public class SearchAction return GlobalResults.ACCESS_TO_NO_REPOS; } - results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits ); - + if( searchResultsOnly && !completeQueryString.equals( "" ) ) + { + results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() ); + } + else + { + completeQueryString = ""; + results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits ); + } + if ( results.isEmpty() ) { addActionError( "No results found" ); return INPUT; } - + + totalPages = results.getTotalHits() / limits.getPageSize(); + + if( (results.getTotalHits() % limits.getPageSize()) != 0 ) + { + totalPages = totalPages + 1; + } // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?) /* I don't think that we should, as I expect us to utilize the 'score' system in lucene in @@ -112,7 +140,12 @@ public class SearchAction * to result in a higher score. * - Joakim */ - + + if( !isEqualToPreviousSearchTerm( q ) ) + { + buildCompleteQueryString( q ); + } + return SUCCESS; } @@ -178,6 +211,46 @@ public class SearchAction return Collections.emptyList(); } + private void buildCompleteQueryString( String searchTerm ) + { + if( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 ) + { + searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR ); + } + + if( completeQueryString == null || "".equals( completeQueryString ) ) + { + completeQueryString = searchTerm; + } + else + { + completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm; + } + } + + private List<String> parseCompleteQueryString() + { + List<String> parsedCompleteQueryString = new ArrayList<String>(); + String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR ); + CollectionUtils.addAll( parsedCompleteQueryString, parsed ); + + return parsedCompleteQueryString; + } + + private boolean isEqualToPreviousSearchTerm( String searchTerm ) + { + if( !"".equals( completeQueryString ) ) + { + String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR ); + if( StringUtils.equalsIgnoreCase( searchTerm, parsed[ parsed.length - 1 ] ) ) + { + return true; + } + } + + return false; + } + public String getQ() { return q; @@ -197,4 +270,44 @@ public class SearchAction { return databaseResults; } + + public void setCurrentPage( int page ) + { + this.currentPage = page; + } + + public int getCurrentPage() + { + return currentPage; + } + + public int getTotalPages() + { + return totalPages; + } + + public void setTotalPages( int totalPages ) + { + this.totalPages = totalPages; + } + + public boolean isSearchResultsOnly() + { + return searchResultsOnly; + } + + public void setSearchResultsOnly( boolean searchResultsOnly ) + { + this.searchResultsOnly = searchResultsOnly; + } + + public String getCompleteQueryString() + { + return completeQueryString; + } + + public void setCompleteQueryString( String completeQueryString ) + { + this.completeQueryString = completeQueryString; + } } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/admin/proxyConnectors.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/admin/proxyConnectors.jsp index f334d4da1..f3362bc29 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/admin/proxyConnectors.jsp +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/admin/proxyConnectors.jsp @@ -73,6 +73,8 @@ <p class="name">${repoMap[repository.key].name}</p> </div> + <c:set var="numberOfRepos" value="${fn:length(repository.value)}" /> + <c:forEach items="${repository.value}" var="connector" varStatus="pc"> <c:choose> @@ -103,12 +105,16 @@ <ww:param name="source" value="%{'${connector.sourceRepoId}'}"/> <ww:param name="target" value="%{'${connector.targetRepoId}'}"/> </ww:url> - <ww:a href="%{sortUpProxyConnectorUrl}" cssClass="up" title="Move Proxy Connector Up"> - <img src="${iconUpUrl}"/> - </ww:a> - <ww:a href="%{sortDownProxyConnectorUrl}" cssClass="down" title="Move Proxy Connector Down"> - <img src="${iconDownUrl}"/> - </ww:a> + <c:if test="${pc.count > 1}"> + <ww:a href="%{sortUpProxyConnectorUrl}" cssClass="up" title="Move Proxy Connector Up"> + <img src="${iconUpUrl}"/> + </ww:a> + </c:if> + <c:if test="${pc.count < numberOfRepos}"> + <ww:a href="%{sortDownProxyConnectorUrl}" cssClass="down" title="Move Proxy Connector Down"> + <img src="${iconDownUrl}"/> + </ww:a> + </c:if> <ww:a href="%{editProxyConnectorUrl}" cssClass="edit" title="Edit Proxy Connector"> <img src="${iconEditUrl}"/> </ww:a> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/quickSearchForm.jspf b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/quickSearchForm.jspf index 7db37265b..71ef30ae4 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/quickSearchForm.jspf +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/include/quickSearchForm.jspf @@ -22,6 +22,8 @@ <div id="searchBox"> <ww:form method="get" action="quickSearch" validate="true"> <ww:textfield label="Search for" size="50" name="q"/> + <ww:checkbox label="Search Results Only" name="searchResultsOnly"/> + <ww:hidden name="completeQueryString" value="${completeQueryString}"/> <ww:submit label="Go!"/> </ww:form> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp index 3367c299f..685f24cfe 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp @@ -32,6 +32,11 @@ <h1>Search</h1> +<c:url var="imgNextPageUrl" value="/images/icon_next_page.gif"/> +<c:url var="imgPrevPageUrl" value="/images/icon_prev_page.gif"/> +<c:url var="imgPrevPageDisabledUrl" value="/images/icon_prev_page_disabled.gif"/> +<c:url var="imgNextPageDisabledUrl" value="/images/icon_next_page_disabled.gif"/> + <div id="contentArea"> <div id="searchBox"> <%@ include file="/WEB-INF/jsp/include/quickSearchForm.jspf" %> @@ -44,13 +49,104 @@ <%-- search was made from the indices --%> <c:when test="${databaseResults == null}"> - <p>Hits: ${fn:length(results.hits)} of ${results.totalHits}</p> - + <c:set var="hitsNum">${fn:length(results.hits) + (currentPage * results.limits.pageSize)}</c:set> + <c:choose> + <c:when test="${results.totalHits > results.limits.pageSize}"> + <p>Hits: ${(hitsNum - results.limits.pageSize) + 1} to ${hitsNum} of ${results.totalHits}</p> + </c:when> + <c:otherwise> + <p>Hits: 1 to ${hitsNum} of ${results.totalHits}</p> + </c:otherwise> + </c:choose> <c:choose> <c:when test="${empty results.hits}"> <p>No results</p> </c:when> <c:otherwise> + + <%-- Pagination start --%> + <p> + <%-- Prev & Next icons --%> + <c:set var="prevPageUrl"> + <ww:url action="quickSearch" namespace="/"> + <ww:param name="q" value="%{'${q}'}"/> + <ww:param name="currentPage" value="%{'${currentPage - 1}'}"/> + </ww:url> + </c:set> + <c:set var="nextPageUrl"> + <ww:url action="quickSearch" namespace="/"> + <ww:param name="q" value="%{'${q}'}"/> + <ww:param name="currentPage" value="%{'${currentPage + 1}'}"/> + </ww:url> + </c:set> + + <c:choose> + <c:when test="${currentPage == 0}"> + <img src="${imgPrevPageDisabledUrl}"/> + </c:when> + <c:otherwise> + <a href="${prevPageUrl}"> + <img src="${imgPrevPageUrl}"/> + </a> + </c:otherwise> + </c:choose> + + <%-- Google-style pagination --%> + <c:choose> + <c:when test="${totalPages > 11}"> + <c:choose> + <c:when test="${(currentPage - 5) < 0}"> + <c:set var="beginVal">0</c:set> + <c:set var="endVal">10</c:set> + </c:when> + <c:when test="${(currentPage + 5) > (totalPages - 1)}"> + <c:set var="beginVal">${(totalPages -1) - 10}</c:set> + <c:set var="endVal">${totalPages - 1}</c:set> + </c:when> + <c:otherwise> + <c:set var="beginVal">${currentPage - 5}</c:set> + <c:set var="endVal">${currentPage + 5}</c:set> + </c:otherwise> + </c:choose> + </c:when> + <c:otherwise> + <c:set var="beginVal">0</c:set> + <c:set var="endVal">${totalPages - 1}</c:set> + </c:otherwise> + </c:choose> + + <c:forEach var="i" begin="${beginVal}" end="${endVal}"> + <c:choose> + <c:when test="${i != currentPage}"> + <c:set var="specificPageUrl"> + <ww:url action="quickSearch" namespace="/"> + <ww:param name="q" value="%{'${q}'}"/> + <ww:param name="currentPage" value="%{'${i}'}"/> + <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/> + <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/> + </ww:url> + </c:set> + <a href="${specificPageUrl}">${i + 1}</a> + </c:when> + <c:otherwise> + <b>${i + 1}</b> + </c:otherwise> + </c:choose> + </c:forEach> + + <c:choose> + <c:when test="${currentPage == (totalPages - 1)}"> + <img src="${imgNextPageDisabledUrl}"/> + </c:when> + <c:otherwise> + <a href="${nextPageUrl}"> + <img src="${imgNextPageUrl}"/> + </a> + </c:otherwise> + </c:choose> + </p> + <%-- Pagination end --%> + <c:forEach items="${results.hits}" var="record" varStatus="i"> <c:choose> <c:when test="${not empty (record.groupId)}"> @@ -60,7 +156,7 @@ </h3> <p> <my:showArtifactLink groupId="${record.groupId}" artifactId="${record.artifactId}" - version="${record.version}" versions="${record.versions}"/> + version="${record.version}" versions="${record.versions}" repositoryId="${record.repositoryId}"/> </p> </c:when> <c:otherwise> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/tags/showArtifactLink.tag b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/tags/showArtifactLink.tag index 65dde0745..ba4558adc 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/tags/showArtifactLink.tag +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/tags/showArtifactLink.tag @@ -27,11 +27,14 @@ <%@ attribute name="classifier" %> <%@ attribute name="scope" %> <%@ attribute name="versions" type="java.util.List" %> +<%@ attribute name="repositoryId" %> <span class="artifact-link"> + <a href="${pageContext.request.contextPath}/repository/${repositoryId}">${repositoryId}</a> + <strong> : </strong> <archiva:groupIdLink var="${groupId}" includeTop="false" /> - <c:if test="${!empty(artifactId)}"> + <c:if test="${!empty(artifactId)}"> <c:set var="url"> <ww:url action="browseArtifact" namespace="/"> <ww:param name="groupId" value="%{'${groupId}'}"/> @@ -73,5 +76,5 @@ </c:if> <c:if test="${!empty(classifier)}"> | <strong>Classifier:</strong> ${classifier} - </c:if> + </c:if> </span> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_next_page.gif b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_next_page.gif Binary files differnew file mode 100644 index 000000000..7c5b30758 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_next_page.gif diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_next_page_disabled.gif b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_next_page_disabled.gif Binary files differnew file mode 100644 index 000000000..99c292f62 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_next_page_disabled.gif diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_prev_page.gif b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_prev_page.gif Binary files differnew file mode 100644 index 000000000..a051fa1f3 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_prev_page.gif diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_prev_page_disabled.gif b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_prev_page_disabled.gif Binary files differnew file mode 100644 index 000000000..79c241b37 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/icon_prev_page_disabled.gif diff --git a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactory.java b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactory.java index 5e926ceed..39e21c38f 100644 --- a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactory.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactory.java @@ -30,6 +30,7 @@ import java.util.Map; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.DavResource; @@ -43,6 +44,7 @@ import org.apache.jackrabbit.webdav.lock.SimpleLockManager; import org.apache.maven.archiva.common.utils.PathUtil; import org.apache.maven.archiva.configuration.ArchivaConfiguration; import org.apache.maven.archiva.configuration.RepositoryGroupConfiguration; +import org.apache.maven.archiva.model.ArchivaRepositoryMetadata; import org.apache.maven.archiva.model.ArtifactReference; import org.apache.maven.archiva.model.ProjectReference; import org.apache.maven.archiva.model.VersionedReference; @@ -59,6 +61,9 @@ import org.apache.maven.archiva.repository.content.RepositoryRequest; import org.apache.maven.archiva.repository.layout.LayoutException; import org.apache.maven.archiva.repository.metadata.MetadataTools; import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException; +import org.apache.maven.archiva.repository.metadata.RepositoryMetadataMerge; +import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader; +import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter; import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers; import org.apache.maven.archiva.security.ArchivaXworkUser; import org.apache.maven.archiva.security.ServletAuthenticator; @@ -69,6 +74,9 @@ import org.apache.maven.model.DistributionManagement; import org.apache.maven.model.Model; import org.apache.maven.model.Relocation; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.codehaus.plexus.digest.ChecksumFile; +import org.codehaus.plexus.digest.Digester; +import org.codehaus.plexus.digest.DigesterException; import org.codehaus.plexus.redback.authentication.AuthenticationException; import org.codehaus.plexus.redback.authentication.AuthenticationResult; import org.codehaus.plexus.redback.authorization.AuthorizationException; @@ -90,11 +98,11 @@ import com.opensymphony.xwork.ActionContext; */ public class ArchivaDavResourceFactory implements DavResourceFactory, Auditable -{ +{ private static final String PROXIED_SUFFIX = " (proxied)"; private static final String HTTP_PUT_METHOD = "PUT"; - + private Logger log = LoggerFactory.getLogger( ArchivaDavResourceFactory.class ); /** @@ -131,7 +139,7 @@ public class ArchivaDavResourceFactory * @plexus.requirement */ private ArchivaConfiguration archivaConfiguration; - + /** * @plexus.requirement */ @@ -141,59 +149,76 @@ public class ArchivaDavResourceFactory * @plexus.requirement role-hint="basic" */ private HttpAuthenticator httpAuth; - - + /** * Lock Manager - use simple implementation from JackRabbit */ private final LockManager lockManager = new SimpleLockManager(); - /** @plexus.requirement */ + /** + * @plexus.requirement + */ private RepositoryContentConsumers consumers; + /** + * @plexus.requirement + */ + private ChecksumFile checksum; + + /** + * @plexus.requirement role-hint="sha1" + */ + private Digester digestSha1; + + /** + * @plexus.requirement role-hint="md5"; + */ + private Digester digestMd5; + public DavResource createResource( final DavResourceLocator locator, final DavServletRequest request, final DavServletResponse response ) throws DavException - { + { checkLocatorIsInstanceOfRepositoryLocator( locator ); ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator; - + RepositoryGroupConfiguration repoGroupConfig = archivaConfiguration.getConfiguration().getRepositoryGroupsAsMap().get( archivaLocator.getRepositoryId() ); List<String> repositories = new ArrayList<String>(); boolean isGet = WebdavMethodUtil.isReadMethod( request.getMethod() ); boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() ); - + if ( repoGroupConfig != null ) - { + { if( WebdavMethodUtil.isWriteMethod( request.getMethod() ) ) { throw new DavException( HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Write method not allowed for repository groups." ); } repositories.addAll( repoGroupConfig.getRepositories() ); - + // handle browse requests for virtual repos - if ( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ).endsWith( "/" ) ) - { - return getResource( request, repositories, archivaLocator ); - } + if ( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ).endsWith( "/" ) ) + { + return getResource( request, repositories, archivaLocator ); + } } else { repositories.add( archivaLocator.getRepositoryId() ); } - + //MRM-419 - Windows Webdav support. Should not 404 if there is no content. if (StringUtils.isEmpty(archivaLocator.getRepositoryId())) { throw new DavException(HttpServletResponse.SC_NO_CONTENT); } - DavResource resource = null; + List<DavResource> availableResources = new ArrayList<DavResource>(); + List<String> resourcesInAbsolutePath = new ArrayList<String>(); DavException e = null; - + for ( String repositoryId : repositories ) { ManagedRepositoryContent managedRepository = null; @@ -207,7 +232,9 @@ public class ArchivaDavResourceFactory throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid managed repository <" + repositoryId + ">" ); } - + + DavResource resource = null; + if ( !locator.getResourcePath().startsWith( ArchivaDavResource.HIDDEN_PATH_PREFIX ) ) { if ( managedRepository != null ) @@ -215,42 +242,37 @@ public class ArchivaDavResourceFactory try { if( isAuthorized( request, repositoryId ) ) - { + { LogicalResource logicalResource = new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) ); - + if ( isGet ) { resource = doGet( managedRepository, request, archivaLocator, logicalResource ); } - + if ( isPut ) { resource = doPut( managedRepository, request, archivaLocator, logicalResource ); } } } - catch ( DavException de ) - { + catch ( DavException de ) + { e = de; continue; } - + if( resource == null ) { e = new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" ); } else - { - setHeaders(response, locator, resource ); + { + availableResources.add( resource ); - // compatibility with MRM-440 to ensure browsing the repository works ok - if ( resource.isCollection() && !request.getRequestURI().endsWith("/" ) ) - { - throw new BrowserRedirectException( resource.getHref() ); - } - resource.addLockManager(lockManager); - return resource; + String logicalResource = RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ); + resourcesInAbsolutePath.add( managedRepository.getRepoRoot() + logicalResource ); } } else @@ -258,14 +280,112 @@ public class ArchivaDavResourceFactory e = new DavException( HttpServletResponse.SC_NOT_FOUND, "Repository does not exist" ); } } + } + + if ( availableResources.isEmpty() ) + { + throw e; + } + + String requestedResource = request.getRequestURI(); + + // MRM-872 : merge all available metadata + // merge metadata only when requested via the repo group + if ( ( repositoryRequest.isMetadata( requestedResource ) || ( requestedResource.endsWith( "metadata.xml.sha1" ) || requestedResource.endsWith( "metadata.xml.md5" ) ) ) && + repoGroupConfig != null ) + { + // this should only be at the project level not version level! + if( isProjectReference( requestedResource ) ) + { + String artifactId = StringUtils.substringBeforeLast( requestedResource.replace( '\\', '/' ), "/" ); + artifactId = StringUtils.substringAfterLast( artifactId, "/" ); + + ArchivaDavResource res = ( ArchivaDavResource ) availableResources.get( 0 ); + String filePath = StringUtils.substringBeforeLast( res.getLocalResource().getAbsolutePath().replace( '\\', '/' ), "/" ); + filePath = filePath + "/maven-metadata-" + repoGroupConfig.getId() + ".xml"; + + // for MRM-872 handle checksums of the merged metadata files + if( repositoryRequest.isSupportFile( requestedResource ) ) + { + File metadataChecksum = new File( filePath + "." + + StringUtils.substringAfterLast( requestedResource, "." ) ); + if( metadataChecksum.exists() ) + { + LogicalResource logicalResource = + new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) ); + + ArchivaDavResource metadataChecksumResource = + new ArchivaDavResource( metadataChecksum.getAbsolutePath(), logicalResource.getPath(), null, + request.getRemoteAddr(), request.getDavSession(), archivaLocator, this, + mimeTypes, auditListeners, consumers ); + availableResources.add( 0, metadataChecksumResource ); + } + } + else + { // merge the metadata of all repos under group + ArchivaRepositoryMetadata mergedMetadata = new ArchivaRepositoryMetadata(); + for ( String resourceAbsPath : resourcesInAbsolutePath ) + { + try + { + File metadataFile = new File( resourceAbsPath ); + ArchivaRepositoryMetadata repoMetadata = RepositoryMetadataReader.read( metadataFile ); + mergedMetadata = RepositoryMetadataMerge.merge( mergedMetadata, repoMetadata ); + } + catch ( RepositoryMetadataException r ) + { + throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Error occurred while reading metadata file." ); + } + } + + try + { + File resourceFile = writeMergedMetadataToFile( mergedMetadata, filePath ); + + LogicalResource logicalResource = + new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) ); + + ArchivaDavResource metadataResource = + new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), null, + request.getRemoteAddr(), request.getDavSession(), archivaLocator, this, + mimeTypes, auditListeners, consumers ); + availableResources.add( 0, metadataResource ); + } + catch ( RepositoryMetadataException r ) + { + throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Error occurred while writing metadata file." ); + } + catch ( IOException ie ) + { + throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Error occurred while generating checksum files." ); + } + catch ( DigesterException de ) + { + throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "Error occurred while generating checksum files." ); + } + } + } } + + DavResource resource = availableResources.get( 0 ); + setHeaders(response, locator, resource ); - throw e; + // compatibility with MRM-440 to ensure browsing the repository works ok + if ( resource.isCollection() && !request.getRequestURI().endsWith("/" ) ) + { + throw new BrowserRedirectException( resource.getHref() ); + } + resource.addLockManager(lockManager); + return resource; } - + public DavResource createResource( final DavResourceLocator locator, final DavSession davSession ) throws DavException - { + { checkLocatorIsInstanceOfRepositoryLocator( locator ); ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator; @@ -568,7 +688,7 @@ public class ArchivaDavResourceFactory //We need to specify this so connecting wagons can work correctly response.addDateHeader("last-modified", resource.getModificationTime()); - + // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots) } @@ -622,87 +742,87 @@ public class ArchivaDavResourceFactory this.path = path; } } - + protected boolean isAuthorized( DavServletRequest request, String repositoryId ) throws DavException { try { - AuthenticationResult result = httpAuth.getAuthenticationResult( request, null ); + AuthenticationResult result = httpAuth.getAuthenticationResult( request, null ); SecuritySession securitySession = httpAuth.getSecuritySession(); - + return servletAuth.isAuthenticated( request, result ) && servletAuth.isAuthorized( request, securitySession, repositoryId, WebdavMethodUtil.isWriteMethod( request.getMethod() ) ); } catch ( AuthenticationException e ) - { + { throw new UnauthorizedDavException( repositoryId, "You are not authenticated" ); } catch ( MustChangePasswordException e ) - { + { throw new UnauthorizedDavException( repositoryId, "You must change your password." ); } catch ( AccountLockedException e ) - { + { throw new UnauthorizedDavException( repositoryId, "User account is locked." ); } catch ( AuthorizationException e ) - { + { throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Fatal Authorization Subsystem Error." ); } catch ( UnauthorizedException e ) - { + { throw new UnauthorizedDavException( repositoryId, e.getMessage() ); } } - + private DavResource getResource( DavServletRequest request, List<String> repositories, ArchivaDavResourceLocator locator ) throws DavException { - List<File> mergedRepositoryContents = new ArrayList<File>(); + List<File> mergedRepositoryContents = new ArrayList<File>(); LogicalResource logicalResource = new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) ); - - // flow: + + // flow: // if the current user logged in has permission to any of the repositories, allow user to // browse the repo group but displaying only the repositories which the user has permission to access. // otherwise, prompt for authentication. - + // put the current session in the session map which will be passed to ArchivaXworkUser Map<String, Object> sessionMap = new HashMap<String, Object>(); if( request.getSession().getAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY ) != null ) { - sessionMap.put( SecuritySystemConstants.SECURITY_SESSION_KEY, + sessionMap.put( SecuritySystemConstants.SECURITY_SESSION_KEY, request.getSession().getAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY ) ); } - - String activePrincipal = ArchivaXworkUser.getActivePrincipal( sessionMap ); + + String activePrincipal = ArchivaXworkUser.getActivePrincipal( sessionMap ); boolean allow = isAllowedToContinue( request, repositories, activePrincipal ); - + if( allow ) - { + { for( String repository : repositories ) - { + { // for prompted authentication if( httpAuth.getSecuritySession() != null ) { try - { - if( isAuthorized( request, repository ) ) + { + if( isAuthorized( request, repository ) ) { getResource( locator, mergedRepositoryContents, logicalResource, repository ); } - } + } catch ( DavException e ) - { + { continue; } } else { - // for the current user logged in + // for the current user logged in try { if( servletAuth.isAuthorizedToAccessVirtualRepository( activePrincipal, repository ) ) @@ -710,27 +830,27 @@ public class ArchivaDavResourceFactory getResource( locator, mergedRepositoryContents, logicalResource, repository ); } } - catch ( UnauthorizedException e ) - { + catch ( UnauthorizedException e ) + { continue; } - } + } } } else { throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." ); } - + ArchivaVirtualDavResource resource = new ArchivaVirtualDavResource( mergedRepositoryContents, logicalResource.getPath(), mimeTypes, locator, this ); - + // compatibility with MRM-440 to ensure browsing the repository group works ok if ( resource.isCollection() && !request.getRequestURI().endsWith("/" ) ) { throw new BrowserRedirectException( resource.getHref() ); } - + return resource; } @@ -748,34 +868,34 @@ public class ArchivaDavResourceFactory { throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid managed repository <" + repository + ">" ); - } - + } + if ( !locator.getResourcePath().startsWith( ArchivaVirtualDavResource.HIDDEN_PATH_PREFIX ) ) { if( managedRepository != null ) - { + { File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() ); if( resourceFile.exists() ) - { + { mergedRepositoryContents.add( resourceFile ); - } + } } } } - + /** * Check if the current user is authorized to access any of the repos - * + * * @param request * @param repositories * @param activePrincipal * @return */ - private boolean isAllowedToContinue( DavServletRequest request, List<String> repositories, String activePrincipal ) + private boolean isAllowedToContinue( DavServletRequest request, List<String> repositories, String activePrincipal ) { boolean allow = false; - - + + // if securitySession != null, it means that the user was prompted for authentication if( httpAuth.getSecuritySession() != null ) { @@ -790,13 +910,13 @@ public class ArchivaDavResourceFactory } } catch( DavException e ) - { + { continue; } - } + } } else - { + { for( String repository : repositories ) { try @@ -808,13 +928,58 @@ public class ArchivaDavResourceFactory } } catch ( UnauthorizedException e ) - { + { continue; } - } + } } - + return allow; } + + private File writeMergedMetadataToFile( ArchivaRepositoryMetadata mergedMetadata, String outputFilename ) + throws RepositoryMetadataException, DigesterException, IOException + { + File outputFile = new File( outputFilename ); + if( outputFile.exists() ) + { + FileUtils.deleteQuietly( outputFile ); + } + + outputFile.getParentFile().mkdirs(); + RepositoryMetadataWriter.write( mergedMetadata, outputFile ); + createChecksumFile( outputFilename, digestSha1 ); + createChecksumFile( outputFilename, digestMd5 ); + + return outputFile; + } + + private void createChecksumFile( String path, Digester digester ) + throws DigesterException, IOException + { + File checksumFile = new File( path + digester.getFilenameExtension() ); + if ( !checksumFile.exists() ) + { + FileUtils.deleteQuietly( checksumFile ); + checksum.createChecksum( new File( path ), digester ); + } + else if ( !checksumFile.isFile() ) + { + log.error( "Checksum file is not a file." ); + } + } + + private boolean isProjectReference( String requestedResource ) + { + try + { + VersionedReference versionRef = metadataTools.toVersionedReference( requestedResource ); + return false; + } + catch ( RepositoryMetadataException re ) + { + return true; + } + } } diff --git a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/RepositoryServlet.java b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/RepositoryServlet.java index 4cdffad27..5fa2d3bd7 100644 --- a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/RepositoryServlet.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/RepositoryServlet.java @@ -28,6 +28,8 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.DavLocatorFactory; import org.apache.jackrabbit.webdav.DavMethods; @@ -44,7 +46,6 @@ import org.apache.maven.archiva.configuration.ArchivaConfiguration; import org.apache.maven.archiva.configuration.ConfigurationEvent; import org.apache.maven.archiva.configuration.ConfigurationListener; import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; -import org.apache.maven.archiva.repository.audit.AuditEvent; import org.apache.maven.archiva.security.ServletAuthenticator; import org.codehaus.plexus.redback.xwork.filter.authentication.HttpAuthenticator; import org.codehaus.plexus.spring.PlexusToSpringUtils; @@ -104,6 +105,7 @@ public class RepositoryServlet DavMethods.isDeltaVMethod( webdavRequest ) && !( DavMethods.DAV_VERSION_CONTROL == methodCode || DavMethods.DAV_REPORT == methodCode ); WebdavResponse webdavResponse = new WebdavResponseImpl( response, noCache ); + DavResource resource = null; try { @@ -114,7 +116,7 @@ public class RepositoryServlet } // check matching if=header for lock-token relevant operations - DavResource resource = + resource = getResourceFactory().createResource( webdavRequest.getRequestLocator(), webdavRequest, webdavResponse ); if ( !isPreconditionValid( webdavRequest, resource ) ) @@ -155,7 +157,7 @@ public class RepositoryServlet } } finally - { + { getDavSessionProvider().releaseSession( webdavRequest ); } } diff --git a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/RepositoryServletRepositoryGroupTest.java b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/RepositoryServletRepositoryGroupTest.java index 57453ea81..26ff32ee1 100644 --- a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/RepositoryServletRepositoryGroupTest.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/RepositoryServletRepositoryGroupTest.java @@ -30,6 +30,8 @@ import org.apache.commons.io.FileUtils; import org.apache.maven.archiva.configuration.Configuration; import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; import org.apache.maven.archiva.configuration.RepositoryGroupConfiguration; +import org.apache.maven.archiva.model.ArchivaRepositoryMetadata; +import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader; import com.meterware.httpunit.GetMethodWebRequest; import com.meterware.httpunit.PutMethodWebRequest; @@ -176,7 +178,7 @@ public class RepositoryServletRepositoryGroupTest WebResponse response = sc.getResponse( request ); assertResponseNotFound( response ); - } + } /* * Test Case 3.a @@ -212,11 +214,79 @@ public class RepositoryServletRepositoryGroupTest throws Exception { WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/" + REPO_GROUP_WITH_VALID_REPOS ); - WebResponse response = sc.getResponse( request ); + WebResponse response = sc.getResponse( request ); assertNotNull( "Should have received a response", response ); assertEquals( "Should have been an 401 response code.", HttpServletResponse.SC_UNAUTHORIZED, response.getResponseCode() ); } + + // MRM-872 + public void testGetMergedMetadata() + throws Exception + { + // first metadata file + String resourceName = "dummy/dummy-merged-metadata-resource/maven-metadata.xml"; + + File dummyInternalResourceFile = new File( repoRootFirst, resourceName ); + dummyInternalResourceFile.getParentFile().mkdirs(); + FileUtils.writeStringToFile( dummyInternalResourceFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<metadata>\n<groupId>dummy</groupId>\n<artifactId>dummy-merged-metadata-resource</artifactId>\n" + + "<versioning>\n<latest>1.0</latest>\n<release>1.0</release>\n<versions>\n<version>1.0</version>\n" + + "<version>2.5</version>\n</versions>\n<lastUpdated>20080708095554</lastUpdated>\n</versioning>\n</metadata>", null ); + + //second metadata file + resourceName = "dummy/dummy-merged-metadata-resource/maven-metadata.xml"; + dummyInternalResourceFile = new File( repoRootLast, resourceName ); + dummyInternalResourceFile.getParentFile().mkdirs(); + FileUtils.writeStringToFile( dummyInternalResourceFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + + "<metadata><groupId>dummy</groupId><artifactId>dummy-merged-metadata-resource</artifactId>" + + "<versioning><latest>2.0</latest><release>2.0</release><versions><version>1.0</version>" + + "<version>1.5</version><version>2.0</version></versions><lastUpdated>20080709095554</lastUpdated>" + + "</versioning></metadata>", null ); + + WebRequest request = + new GetMethodWebRequest( "http://machine.com/repository/" + REPO_GROUP_WITH_VALID_REPOS + "/dummy/" + + "dummy-merged-metadata-resource/maven-metadata.xml" ); + WebResponse response = sc.getResource( request ); + + File returnedMetadata = new File( getBasedir(), "/target/test-classes/retrievedMetadataFile.xml"); + FileUtils.writeStringToFile( returnedMetadata, response.getText() ); + ArchivaRepositoryMetadata metadata = RepositoryMetadataReader.read( returnedMetadata ); + + assertResponseOK( response ); + assertEquals( "Versions list size", 4, metadata.getAvailableVersions().size() ); + assertTrue( "Versions list contains version 1.0", metadata.getAvailableVersions().contains( "1.0" ) ); + assertTrue( "Versions list contains version 1.5", metadata.getAvailableVersions().contains( "1.5" ) ); + assertTrue( "Versions list contains version 2.0", metadata.getAvailableVersions().contains( "2.0" ) ); + assertTrue( "Versions list contains version 2.5", metadata.getAvailableVersions().contains( "2.5" ) ); + + //check if the checksum files were generated + File checksumFileSha1 = new File( repoRootFirst, resourceName + ".sha1" ); + checksumFileSha1.getParentFile().mkdirs(); + FileUtils.writeStringToFile( checksumFileSha1, "3290853214d3687134", null ); + + File checksumFileMd5 = new File( repoRootFirst, resourceName + ".md5" ); + checksumFileMd5.getParentFile().mkdirs(); + FileUtils.writeStringToFile( checksumFileMd5, "98745897234eda12836423", null ); + + // request the sha1 checksum of the metadata + request = + new GetMethodWebRequest( "http://machine.com/repository/" + REPO_GROUP_WITH_VALID_REPOS + "/dummy/" + + "dummy-merged-metadata-resource/maven-metadata.xml.sha1" ); + response = sc.getResource( request ); + + assertResponseOK( response ); + assertEquals( "d2321a573e0488bca571b624f891104009408dd8 maven-metadata-group-with-valid-repos.xml", response.getText() ); + + // request the md5 checksum of the metadata + request = + new GetMethodWebRequest( "http://machine.com/repository/" + REPO_GROUP_WITH_VALID_REPOS + "/dummy/" + + "dummy-merged-metadata-resource/maven-metadata.xml.md5" ); + response = sc.getResource( request ); + + assertResponseOK( response ); + assertEquals( "79d271fbe8bd1d17b23273937750d407 maven-metadata-group-with-valid-repos.xml", response.getText().trim() ); + } protected void assertResponseMethodNotAllowed( WebResponse response ) { diff --git a/archiva-modules/archiva-web/archiva-webdav/src/test/resources/org/apache/maven/archiva/webdav/RepositoryServletTest.xml b/archiva-modules/archiva-web/archiva-webdav/src/test/resources/org/apache/maven/archiva/webdav/RepositoryServletTest.xml index f40d694f2..83fd2a4e6 100644 --- a/archiva-modules/archiva-web/archiva-webdav/src/test/resources/org/apache/maven/archiva/webdav/RepositoryServletTest.xml +++ b/archiva-modules/archiva-web/archiva-webdav/src/test/resources/org/apache/maven/archiva/webdav/RepositoryServletTest.xml @@ -152,6 +152,20 @@ <role>org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers</role> <role-hint>default</role-hint> </requirement> + <requirement> + <role>org.codehaus.plexus.digest.ChecksumFile</role> + <field-name>checksum</field-name> + </requirement> + <requirement> + <role>org.codehaus.plexus.digest.Digester</role> + <role-hint>sha1</role-hint> + <field-name>digestSha1</field-name> + </requirement> + <requirement> + <role>org.codehaus.plexus.digest.Digester</role> + <role-hint>md5</role-hint> + <field-name>digestMd5</field-name> + </requirement> </requirements> </component> </components> @@ -229,6 +229,11 @@ </exclusions> </dependency> <dependency> + <groupId>xerces</groupId> + <artifactId>xercesImpl</artifactId> + <version>2.8.1</version> + </dependency> + <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1</version> @@ -826,6 +831,12 @@ <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-http-lightweight</artifactId> <version>${wagon.version}</version> + <exclusions> + <exclusion> + <groupId>nekohtml</groupId> + <artifactId>xercesMinimal</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.apache.maven.wagon</groupId> |