Using new methods in RepositoryRequest to identify native resource path early and using it. Adding PolicingServletRequest to deal with bad formatted request paths. Beefing up RepositoryServletTest to test proxy-less (for now) requests ... * Browse * Get Checksum (default layout) * Get Checksum (legacy layout) * Get Metadata (versioned + default layout) * Get Metadata (project + default layout) * Get Artifact (default layout) Adding custom mime-types.txt to get proper "Content-Type" headers on GET requests. git-svn-id: https://svn.apache.org/repos/asf/maven/archiva/trunk@587708 13f79535-47bb-0310-9956-ffa450edef68tags/archiva-1.0-beta-3
@@ -0,0 +1,69 @@ | |||
package org.apache.maven.archiva.web.repository; | |||
/* | |||
* 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 org.apache.commons.lang.StringUtils; | |||
import org.codehaus.plexus.util.FileUtils; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletRequestWrapper; | |||
/** | |||
* PolicingServletRequest is for policing the incoming request for naughty bits, such as a double slashes, | |||
* or paths that include "/../" type syntax, or query string. Stripping out all things that are | |||
* not appropriate. | |||
* | |||
* @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a> | |||
* @version $Id$ | |||
*/ | |||
public class PolicingServletRequest | |||
extends HttpServletRequestWrapper | |||
implements HttpServletRequest | |||
{ | |||
private String fixedPathInfo; | |||
public PolicingServletRequest( HttpServletRequest originalRequest ) | |||
{ | |||
super( originalRequest ); | |||
fixedPathInfo = originalRequest.getPathInfo(); | |||
if ( StringUtils.isNotBlank( fixedPathInfo ) ) | |||
{ | |||
/* Perform a simple security normalization of the requested pathinfo. | |||
* This is to cleanup requests that use "/../" or "///" type hacks. | |||
*/ | |||
fixedPathInfo = FileUtils.normalize( fixedPathInfo ); | |||
} | |||
} | |||
@Override | |||
public String getPathInfo() | |||
{ | |||
return fixedPathInfo; | |||
} | |||
@Override | |||
public String getQueryString() | |||
{ | |||
// No query string allowed. | |||
return null; | |||
} | |||
} |
@@ -138,29 +138,46 @@ public class ProxiedDavServer | |||
if ( isGet ) | |||
{ | |||
fetchContentFromProxies( request ); | |||
} | |||
// Default behaviour is to treat the resource natively. | |||
String resource = request.getLogicalResource(); | |||
File resourceFile = new File( managedRepository.getRepoRoot(), resource ); | |||
if ( isPut ) | |||
{ | |||
/* Create parent directories that don't exist when writing a file | |||
* This actually makes this implementation not compliant to the | |||
* WebDAV RFC - but we have enough knowledge | |||
* about how the collection is being used to do this reasonably and | |||
* some versions of Maven's WebDAV don't | |||
* correctly create the collections themselves. | |||
*/ | |||
// If this a directory resource, then we are likely browsing. | |||
if ( resourceFile.exists() && resourceFile.isDirectory() ) | |||
{ | |||
// TODO: [MRM-440] - If webdav URL lacks a trailing /, navigating to all links in the listing return 404. | |||
// TODO: Issue redirect with proper pathing. | |||
// Process the request. | |||
davServer.process( request, response ); | |||
// All done. | |||
return; | |||
} | |||
File rootDirectory = getRootDirectory(); | |||
if ( rootDirectory != null ) | |||
// At this point the incoming request can either be in default or legacy layout format. | |||
try | |||
{ | |||
new File( rootDirectory, request.getLogicalResource() ).getParentFile().mkdirs(); | |||
// Perform an adjustment of the resource to the managed repository expected path. | |||
resource = repositoryRequest.toNativePath( request.getLogicalResource(), managedRepository ); | |||
resourceFile = new File( managedRepository.getRepoRoot(), resource ); | |||
// Adjust the pathInfo resource to be in the format that the dav server impl expects. | |||
request.getRequest().setPathInfo( resource ); | |||
// Attempt to fetch the resource from any defined proxy. | |||
fetchContentFromProxies( request, resource ); | |||
} | |||
} | |||
catch ( LayoutException e ) | |||
{ | |||
// Invalid resource, pass it on. | |||
respondResourceMissing( request, response, e ); | |||
if ( isGet ) | |||
{ | |||
if ( resourceExists( request ) ) | |||
// All done. | |||
return; | |||
} | |||
if ( resourceFile.exists() ) | |||
{ | |||
// [MRM-503] - Metadata file need Pragma:no-cache response header. | |||
if ( request.getLogicalResource().endsWith( "/maven-metadata.xml" ) ) | |||
@@ -175,17 +192,35 @@ public class ProxiedDavServer | |||
} | |||
else | |||
{ | |||
respondResourceMissing( request, response ); | |||
respondResourceMissing( request, response, null ); | |||
} | |||
} | |||
if ( isPut ) | |||
{ | |||
/* Create parent directories that don't exist when writing a file | |||
* This actually makes this implementation not compliant to the | |||
* WebDAV RFC - but we have enough knowledge | |||
* about how the collection is being used to do this reasonably and | |||
* some versions of Maven's WebDAV don't | |||
* correctly create the collections themselves. | |||
*/ | |||
File rootDirectory = getRootDirectory(); | |||
if ( rootDirectory != null ) | |||
{ | |||
new File( rootDirectory, request.getLogicalResource() ).getParentFile().mkdirs(); | |||
} | |||
// Allow the dav server to process the put request. | |||
davServer.process( request, response ); | |||
// All done. | |||
return; | |||
} | |||
} | |||
private void respondResourceMissing( DavServerRequest request, HttpServletResponse response ) | |||
private void respondResourceMissing( DavServerRequest request, HttpServletResponse response, Throwable t ) | |||
{ | |||
response.setStatus( HttpServletResponse.SC_NOT_FOUND ); | |||
@@ -196,7 +231,6 @@ public class ProxiedDavServer | |||
missingUrl.append( request.getRequest().getServerName() ).append( ":" ); | |||
missingUrl.append( request.getRequest().getServerPort() ); | |||
missingUrl.append( request.getRequest().getServletPath() ); | |||
// missingUrl.append( request.getRequest().getPathInfo() ); | |||
String message = "Error 404 Not Found"; | |||
@@ -217,6 +251,13 @@ public class ProxiedDavServer | |||
out.println( "\">" ); | |||
out.print( missingUrl.toString() ); | |||
out.println( "</a></p>" ); | |||
if ( t != null ) | |||
{ | |||
out.println( "<pre>" ); | |||
t.printStackTrace( out ); | |||
out.println( "</pre>" ); | |||
} | |||
out.println( "</body></html>" ); | |||
@@ -228,74 +269,23 @@ public class ProxiedDavServer | |||
} | |||
} | |||
private boolean resourceExists( DavServerRequest request ) | |||
{ | |||
String resource = request.getLogicalResource(); | |||
File resourceFile = new File( managedRepository.getRepoRoot(), resource ); | |||
return resourceFile.exists(); | |||
} | |||
private void fetchContentFromProxies( DavServerRequest request ) | |||
private void fetchContentFromProxies( DavServerRequest request, String resource ) | |||
throws ServletException | |||
{ | |||
String resource = request.getLogicalResource(); | |||
// Cleanup bad requests from maven 1. | |||
// Often seen is a double slash. | |||
// example: http://hostname:8080/archiva/repository/internal//pmd/jars/pmd-3.0.jar | |||
if ( resource.startsWith( "/" ) ) | |||
{ | |||
resource = resource.substring( 1 ); | |||
} | |||
if ( resource.endsWith( ".sha1" ) || resource.endsWith( ".md5" ) ) | |||
if ( repositoryRequest.isSupportFile( resource ) ) | |||
{ | |||
// Checksums are fetched with artifact / metadata. | |||
// Need to adjust the path for the checksum resource. | |||
return; | |||
} | |||
// Is it a Metadata resource? | |||
if ( resource.endsWith( "/" + MetadataTools.MAVEN_METADATA ) ) | |||
if ( repositoryRequest.isDefault( resource ) && repositoryRequest.isMetadata( resource ) ) | |||
{ | |||
ProjectReference project; | |||
VersionedReference versioned; | |||
try | |||
{ | |||
versioned = metadataTools.toVersionedReference( resource ); | |||
if ( versioned != null ) | |||
{ | |||
connectors.fetchFromProxies( managedRepository, versioned ); | |||
request.getRequest().setPathInfo( metadataTools.toPath( versioned ) ); | |||
return; | |||
} | |||
} | |||
catch ( RepositoryMetadataException e ) | |||
{ | |||
/* eat it */ | |||
} | |||
catch ( ProxyException e ) | |||
if ( fetchMetadataFromProxies( request, resource ) ) | |||
{ | |||
throw new ServletException( "Unable to fetch versioned metadata resource.", e ); | |||
} | |||
try | |||
{ | |||
project = metadataTools.toProjectReference( resource ); | |||
if ( project != null ) | |||
{ | |||
connectors.fetchFromProxies( managedRepository, project ); | |||
request.getRequest().setPathInfo( metadataTools.toPath( project ) ); | |||
} | |||
} | |||
catch ( RepositoryMetadataException e ) | |||
{ | |||
/* eat it */ | |||
} | |||
catch ( ProxyException e ) | |||
{ | |||
throw new ServletException( "Unable to fetch project metadata resource.", e ); | |||
return; | |||
} | |||
} | |||
@@ -326,6 +316,52 @@ public class ProxiedDavServer | |||
} | |||
} | |||
private boolean fetchMetadataFromProxies( DavServerRequest request, String resource ) | |||
throws ServletException | |||
{ | |||
ProjectReference project; | |||
VersionedReference versioned; | |||
try | |||
{ | |||
versioned = metadataTools.toVersionedReference( resource ); | |||
if ( versioned != null ) | |||
{ | |||
connectors.fetchFromProxies( managedRepository, versioned ); | |||
return true; | |||
} | |||
} | |||
catch ( RepositoryMetadataException e ) | |||
{ | |||
/* eat it */ | |||
} | |||
catch ( ProxyException e ) | |||
{ | |||
throw new ServletException( "Unable to fetch versioned metadata resource.", e ); | |||
} | |||
try | |||
{ | |||
project = metadataTools.toProjectReference( resource ); | |||
if ( project != null ) | |||
{ | |||
connectors.fetchFromProxies( managedRepository, project ); | |||
return true; | |||
} | |||
} | |||
catch ( RepositoryMetadataException e ) | |||
{ | |||
/* eat it */ | |||
} | |||
catch ( ProxyException e ) | |||
{ | |||
throw new ServletException( "Unable to fetch project metadata resource.", e ); | |||
} | |||
return false; | |||
} | |||
/** | |||
* A relocation capable client will request the POM prior to the artifact, | |||
* and will then read meta-data and do client side relocation. A simplier | |||
@@ -400,6 +436,13 @@ public class ProxiedDavServer | |||
// Invalid POM : ignore | |||
} | |||
} | |||
@Override | |||
public void setUseIndexHtml( boolean useIndexHtml ) | |||
{ | |||
super.setUseIndexHtml( useIndexHtml ); | |||
davServer.setUseIndexHtml( useIndexHtml ); | |||
} | |||
public ManagedRepositoryContent getRepository() | |||
{ |
@@ -103,9 +103,18 @@ public class RepositoryServlet | |||
DavServerComponent server = createServer( repo.getId(), repoDir, servletConfig ); | |||
server.setUseIndexHtml( true ); | |||
server.addListener( audit ); | |||
} | |||
} | |||
@Override | |||
protected void service( HttpServletRequest httpRequest, HttpServletResponse httpResponse ) | |||
throws ServletException, IOException | |||
{ | |||
// Wrap the incoming request to adjust paths and whatnot. | |||
super.service( new PolicingServletRequest( httpRequest ), httpResponse ); | |||
} | |||
public synchronized ManagedRepositoryConfiguration getRepository( String prefix ) | |||
{ |
@@ -0,0 +1,129 @@ | |||
# This is a comment. I love comments. | |||
# This file controls what Internet media types are sent to the client for | |||
# given file extension(s). Sending the correct media type to the client | |||
# is important so they know how to handle the content of the file. | |||
# Extra types can either be added here or by using an AddType directive | |||
# in your config files. For more information about Internet media types, | |||
# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type | |||
# registry is at <http://www.iana.org/assignments/media-types/>. | |||
# MIME type Extensions | |||
application/andrew-inset ez | |||
application/atom+xml atom | |||
application/mac-binhex40 hqx | |||
application/mac-compactpro cpt | |||
application/mathml+xml mathml | |||
application/msword doc | |||
application/octet-stream bin dms lha lzh exe class so dll dmg | |||
application/oda oda | |||
application/ogg ogg | |||
application/pdf pdf | |||
application/pgp-encrypted pgp | |||
application/postscript ai eps ps | |||
application/rdf+xml rdf | |||
application/smil smi smil | |||
application/srgs gram | |||
application/srgs+xml grxml | |||
application/vnd.mif mif | |||
application/vnd.mozilla.xul+xml xul | |||
application/vnd.ms-excel xls | |||
application/vnd.ms-powerpoint ppt | |||
application/vnd.rn-realmedia rm | |||
application/vnd.wap.wbxml wbxml | |||
application/vnd.wap.wmlc wmlc | |||
application/vnd.wap.wmlscriptc wmlsc | |||
application/voicexml+xml vxml | |||
application/x-bcpio bcpio | |||
application/x-cdlink vcd | |||
application/x-chess-pgn pgn | |||
application/x-cpio cpio | |||
application/x-csh csh | |||
application/x-director dcr dir dxr | |||
application/x-dvi dvi | |||
application/x-futuresplash spl | |||
application/x-gtar gtar | |||
application/x-hdf hdf | |||
application/x-jar jar | |||
application/x-java-jnlp-file jnlp | |||
application/x-javascript js | |||
application/x-koan skp skd skt skm | |||
application/x-latex latex | |||
application/x-netcdf nc cdf | |||
application/x-sh sh | |||
application/x-shar shar | |||
application/x-shockwave-flash swf | |||
application/x-stuffit sit | |||
application/x-sv4cpio sv4cpio | |||
application/x-sv4crc sv4crc | |||
application/x-tar tar | |||
application/x-tcl tcl | |||
application/x-tex tex | |||
application/x-texinfo texinfo texi | |||
application/x-troff t tr roff | |||
application/x-troff-man man | |||
application/x-troff-me me | |||
application/x-troff-ms ms | |||
application/x-ustar ustar | |||
application/x-wais-source src | |||
application/xhtml+xml xhtml xht | |||
application/xml xml xsl | |||
application/xml-dtd dtd | |||
application/xslt+xml xslt | |||
application/zip zip | |||
audio/basic au snd | |||
audio/midi mid midi kar | |||
audio/mpeg mpga mp2 mp3 | |||
audio/x-aiff aif aiff aifc | |||
audio/x-mpegurl m3u | |||
audio/x-pn-realaudio ram ra | |||
audio/x-wav wav | |||
chemical/x-pdb pdb | |||
chemical/x-xyz xyz | |||
image/bmp bmp | |||
image/cgm cgm | |||
image/gif gif | |||
image/ief ief | |||
image/jp2 jp2 | |||
image/jpeg jpeg jpg jpe | |||
image/pict pict pic pct | |||
image/png png | |||
image/svg+xml svg | |||
image/tiff tiff tif | |||
image/vnd.djvu djvu djv | |||
image/vnd.wap.wbmp wbmp | |||
image/x-cmu-raster ras | |||
image/x-icon ico | |||
image/x-macpaint pntg pnt mac | |||
image/x-portable-anymap pnm | |||
image/x-portable-bitmap pbm | |||
image/x-portable-graymap pgm | |||
image/x-portable-pixmap ppm | |||
image/x-quicktime qtif qti | |||
image/x-rgb rgb | |||
image/x-xbitmap xbm | |||
image/x-xpixmap xpm | |||
image/x-xwindowdump xwd | |||
model/iges igs iges | |||
model/mesh msh mesh silo | |||
model/vrml wrl vrml | |||
text/calendar ics ifb | |||
text/css css | |||
text/html html htm | |||
text/plain asc txt sha1 md5 | |||
text/richtext rtx | |||
text/rtf rtf | |||
text/sgml sgml sgm | |||
text/tab-separated-values tsv | |||
text/vnd.wap.wml wml | |||
text/vnd.wap.wmlscript wmls | |||
text/x-setext etx | |||
video/mp4 mp4 | |||
video/mpeg mpeg mpg mpe | |||
video/quicktime qt mov | |||
video/vnd.mpegurl mxu m4u | |||
video/x-dv dv dif | |||
video/x-msvideo avi | |||
video/x-sgi-movie movie | |||
x-conference/x-cooltalk ice |
@@ -19,26 +19,36 @@ package org.apache.maven.archiva.web.repository; | |||
* under the License. | |||
*/ | |||
import com.meterware.httpunit.GetMethodWebRequest; | |||
import com.meterware.httpunit.PutMethodWebRequest; | |||
import com.meterware.httpunit.WebLink; | |||
import com.meterware.httpunit.WebRequest; | |||
import com.meterware.httpunit.WebResponse; | |||
import com.meterware.servletunit.ServletRunner; | |||
import com.meterware.servletunit.ServletUnitClient; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.maven.archiva.configuration.ArchivaConfiguration; | |||
import org.apache.maven.archiva.configuration.Configuration; | |||
import org.apache.maven.archiva.configuration.ConfigurationEvent; | |||
import org.apache.maven.archiva.configuration.IndeterminateConfigurationException; | |||
import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; | |||
import org.apache.maven.archiva.configuration.RemoteRepositoryConfiguration; | |||
import org.codehaus.plexus.PlexusConstants; | |||
import org.codehaus.plexus.PlexusTestCase; | |||
import org.codehaus.plexus.registry.RegistryException; | |||
import org.codehaus.plexus.util.FileUtils; | |||
import org.xml.sax.SAXException; | |||
import org.codehaus.plexus.webdav.util.MimeTypes; | |||
import javax.servlet.ServletException; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import javax.servlet.ServletContext; | |||
import javax.servlet.http.HttpServletResponse; | |||
import javax.servlet.http.HttpSession; | |||
/** | |||
* RepositoryServletTest | |||
* | |||
* @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a> | |||
* @version $Id$ | |||
*/ | |||
public class RepositoryServletTest | |||
extends PlexusTestCase | |||
{ | |||
@@ -61,61 +71,66 @@ public class RepositoryServletTest | |||
{ | |||
super.setUp(); | |||
// TODO: purely to quiet logging - shouldn't be needed | |||
String appserverBase = getTestFile( "target/appserver-base" ).getAbsolutePath(); | |||
System.setProperty( "appserver.base", appserverBase ); | |||
configuration = (ArchivaConfiguration) lookup( ArchivaConfiguration.ROLE ); | |||
File testConf = getTestFile( "src/test/resources/repository-archiva.xml" ); | |||
File testConfDest = new File( appserverBase, "conf/archiva.xml" ); | |||
FileUtils.copyFile( testConf, testConfDest ); | |||
configuration = (ArchivaConfiguration) lookup( ArchivaConfiguration.ROLE ); | |||
repositoryLocation = new File( appserverBase, "data/repositories/internal" ); | |||
Configuration config = configuration.getConfiguration(); | |||
config.addManagedRepository( createManagedRepository( "internal", "Internal Test Repo", repositoryLocation ) ); | |||
saveConfiguration(); | |||
ServletRunner sr = new ServletRunner(); | |||
sr.registerServlet( "/repository/*", UnauthenticatedRepositoryServlet.class.getName() ); | |||
sc = sr.newClient(); | |||
sc.getSession( true ).getServletContext().setAttribute( PlexusConstants.PLEXUS_KEY, getContainer() ); | |||
HttpSession session = sc.getSession( true ); | |||
ServletContext servletContext = session.getServletContext(); | |||
servletContext.setAttribute( PlexusConstants.PLEXUS_KEY, getContainer() ); | |||
} | |||
public void testPutWithMissingParentCollection() | |||
throws IOException, SAXException | |||
throws Exception | |||
{ | |||
FileUtils.deleteDirectory( repositoryLocation ); | |||
WebRequest request = new PutMethodWebRequest( REQUEST_PATH, getClass().getResourceAsStream( "/artifact.jar" ), | |||
"application/octet-stream" ); | |||
WebResponse response = sc.getResponse( request ); | |||
assertNotNull( "No response received", response ); | |||
assertEquals( "file contents", "artifact.jar\n", | |||
FileUtils.fileRead( new File( repositoryLocation, "path/to/artifact.jar" ) ) ); | |||
assertNotNull( "Should have received response", response ); | |||
assertEquals( "file contents", "artifact.jar\n", FileUtils | |||
.readFileToString( new File( repositoryLocation, "path/to/artifact.jar" ), null ) ); | |||
} | |||
public void testGetRepository() | |||
throws IOException, ServletException | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
ManagedRepositoryConfiguration repository = servlet.getRepository( REPOSITORY_ID ); | |||
assertNotNull( repository ); | |||
assertEquals( "Archiva Managed Internal Repository", repository.getName() ); | |||
assertRepositoryValid( servlet, REPOSITORY_ID ); | |||
} | |||
public void testGetRepositoryAfterDelete() | |||
throws IOException, ServletException, RegistryException, IndeterminateConfigurationException | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
Configuration c = configuration.getConfiguration(); | |||
c.removeManagedRepository( c.findManagedRepositoryById( REPOSITORY_ID ) ); | |||
// TODO it would be better to use a mock configuration and "save" to more accurately reflect the calls made | |||
servlet.configurationEvent( new ConfigurationEvent( ConfigurationEvent.SAVED) ); | |||
saveConfiguration(); | |||
ManagedRepositoryConfiguration repository = servlet.getRepository( REPOSITORY_ID ); | |||
assertNull( repository ); | |||
} | |||
public void testGetRepositoryAfterAdd() | |||
throws IOException, ServletException, RegistryException, IndeterminateConfigurationException | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
@@ -131,16 +146,221 @@ public class RepositoryServletTest | |||
} | |||
repo.setLocation( repoRoot.getAbsolutePath() ); | |||
c.addManagedRepository( repo ); | |||
// TODO it would be better to use a mock configuration and "save" to more accurately reflect the calls made | |||
servlet.configurationEvent( new ConfigurationEvent( ConfigurationEvent.SAVED) ); | |||
saveConfiguration(); | |||
ManagedRepositoryConfiguration repository = servlet.getRepository( NEW_REPOSITORY_ID ); | |||
assertNotNull( repository ); | |||
assertEquals( NEW_REPOSITORY_NAME, repository.getName() ); | |||
// check other is still intact | |||
repository = servlet.getRepository( REPOSITORY_ID ); | |||
assertNotNull( repository ); | |||
assertEquals( "Archiva Managed Internal Repository", repository.getName() ); | |||
assertRepositoryValid( servlet, REPOSITORY_ID ); | |||
} | |||
public void testBrowse() | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
assertRepositoryValid( servlet, REPOSITORY_ID ); | |||
new File( repositoryLocation, "org/apache/archiva" ).mkdirs(); | |||
new File( repositoryLocation, "net/sourceforge" ).mkdirs(); | |||
new File( repositoryLocation, "commons-lang" ).mkdirs(); | |||
WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" ); | |||
WebResponse response = sc.getResponse( request ); | |||
assertEquals( "Response", HttpServletResponse.SC_OK, response.getResponseCode() ); | |||
// dumpResponse( response ); | |||
WebLink links[] = response.getLinks(); | |||
String expectedLinks[] = new String[] { "./commons-lang/", "./net/", "./org/", "./path/" }; | |||
assertEquals( "Links.length", expectedLinks.length, links.length ); | |||
for ( int i = 0; i < links.length; i++ ) | |||
{ | |||
assertEquals( "Link[" + i + "]", expectedLinks[i], links[i].getURLString() ); | |||
} | |||
} | |||
public void testGetNoProxyChecksumDefaultLayout() | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
assertRepositoryValid( servlet, REPOSITORY_ID ); | |||
String commonsLangSha1 = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar.sha1"; | |||
File checksumFile = new File( repositoryLocation, commonsLangSha1 ); | |||
checksumFile.getParentFile().mkdirs(); | |||
FileUtils.writeStringToFile( checksumFile, "dummy-checksum", null ); | |||
WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangSha1 ); | |||
WebResponse response = sc.getResponse( request ); | |||
assertEquals( "Response OK", HttpServletResponse.SC_OK, response.getResponseCode() ); | |||
assertEquals( "Expected file contents", "dummy-checksum", response.getText() ); | |||
} | |||
public void testGetNoProxyChecksumLegacyLayout() | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
assertRepositoryValid( servlet, REPOSITORY_ID ); | |||
String commonsLangSha1 = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar.sha1"; | |||
File checksumFile = new File( repositoryLocation, commonsLangSha1 ); | |||
checksumFile.getParentFile().mkdirs(); | |||
FileUtils.writeStringToFile( checksumFile, "dummy-checksum", null ); | |||
WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + | |||
"commons-lang/jars/commons-lang-2.1.jar.sha1" ); | |||
WebResponse response = sc.getResponse( request ); | |||
assertEquals( "Response OK", HttpServletResponse.SC_OK, response.getResponseCode() ); | |||
assertEquals( "Expected file contents", "dummy-checksum", response.getText() ); | |||
} | |||
public void testGetNoProxyVersionedMetadataDefaultLayout() | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
assertRepositoryValid( servlet, REPOSITORY_ID ); | |||
String commonsLangMetadata = "commons-lang/commons-lang/2.1/maven-metadata.xml"; | |||
String expectedMetadataContents = "dummy-versioned-metadata"; | |||
File metadataFile = new File( repositoryLocation, commonsLangMetadata ); | |||
metadataFile.getParentFile().mkdirs(); | |||
FileUtils.writeStringToFile( metadataFile, expectedMetadataContents, null ); | |||
WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangMetadata ); | |||
WebResponse response = sc.getResponse( request ); | |||
assertEquals( "Response OK", HttpServletResponse.SC_OK, response.getResponseCode() ); | |||
assertEquals( "Expected file contents", expectedMetadataContents, response.getText() ); | |||
} | |||
public void testGetNoProxyProjectMetadataDefaultLayout() | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
assertRepositoryValid( servlet, REPOSITORY_ID ); | |||
String commonsLangMetadata = "commons-lang/commons-lang/maven-metadata.xml"; | |||
String expectedMetadataContents = "dummy-project-metadata"; | |||
File metadataFile = new File( repositoryLocation, commonsLangMetadata ); | |||
metadataFile.getParentFile().mkdirs(); | |||
FileUtils.writeStringToFile( metadataFile, expectedMetadataContents, null ); | |||
WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangMetadata ); | |||
WebResponse response = sc.getResponse( request ); | |||
assertEquals( "Response OK", HttpServletResponse.SC_OK, response.getResponseCode() ); | |||
assertEquals( "Expected file contents", expectedMetadataContents, response.getText() ); | |||
} | |||
public void testGetNoProxyArtifactDefaultLayout() | |||
throws Exception | |||
{ | |||
RepositoryServlet servlet = (RepositoryServlet) sc.newInvocation( REQUEST_PATH ).getServlet(); | |||
assertNotNull( servlet ); | |||
assertRepositoryValid( servlet, REPOSITORY_ID ); | |||
String commonsLangJar = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar"; | |||
String expectedArtifactContents = "dummy-commons-lang-artifact"; | |||
File artifactFile = new File( repositoryLocation, commonsLangJar ); | |||
artifactFile.getParentFile().mkdirs(); | |||
FileUtils.writeStringToFile( artifactFile, expectedArtifactContents, null ); | |||
WebRequest request = new GetMethodWebRequest( "http://machine.com/repository/internal/" + commonsLangJar ); | |||
WebResponse response = sc.getResponse( request ); | |||
assertEquals( "Response OK", HttpServletResponse.SC_OK, response.getResponseCode() ); | |||
assertEquals( "Expected file contents", expectedArtifactContents, response.getText() ); | |||
} | |||
public void testMimeTypesAvailable() | |||
throws Exception | |||
{ | |||
MimeTypes mimeTypes = (MimeTypes) lookup( MimeTypes.class ); | |||
assertNotNull( mimeTypes ); | |||
// Test for some added types. | |||
assertEquals( "sha1", "text/plain", mimeTypes.getMimeType( "foo.sha1" ) ); | |||
assertEquals( "md5", "text/plain", mimeTypes.getMimeType( "foo.md5" ) ); | |||
assertEquals( "pgp", "application/pgp-encrypted", mimeTypes.getMimeType( "foo.pgp" ) ); | |||
} | |||
private void dumpResponse( WebResponse response ) | |||
{ | |||
System.out.println( "---(response)---" ); | |||
System.out.println( "" + response.getResponseCode() + " " + response.getResponseMessage() ); | |||
String headerNames[] = response.getHeaderFieldNames(); | |||
for ( String headerName : headerNames ) | |||
{ | |||
System.out.println( "[header] " + headerName + ": " + response.getHeaderField( headerName ) ); | |||
} | |||
System.out.println( "---(text)---" ); | |||
try | |||
{ | |||
System.out.println( response.getText() ); | |||
} | |||
catch ( IOException e ) | |||
{ | |||
System.err.print( "[Exception] : " ); | |||
e.printStackTrace( System.err ); | |||
} | |||
} | |||
private void assertRepositoryValid( RepositoryServlet servlet, String repoId ) | |||
{ | |||
ManagedRepositoryConfiguration repository = servlet.getRepository( repoId ); | |||
assertNotNull( "Archiva Managed Repository id:<" + repoId + "> should exist.", repository ); | |||
File repoRoot = new File( repository.getLocation() ); | |||
assertTrue( "Archiva Managed Repository id:<" + repoId + "> should have a valid location on disk.", repoRoot | |||
.exists() | |||
&& repoRoot.isDirectory() ); | |||
} | |||
private void saveConfiguration() | |||
throws Exception | |||
{ | |||
configuration.save( configuration.getConfiguration() ); | |||
// TODO it would be better to use a mock configuration and "save" to more accurately reflect the calls made | |||
// RepositoryServlet servlet | |||
// servlet.configurationEvent( new ConfigurationEvent( ConfigurationEvent.SAVED ) ); | |||
} | |||
private ManagedRepositoryConfiguration createManagedRepository( String id, String name, File location ) | |||
{ | |||
ManagedRepositoryConfiguration repo = new ManagedRepositoryConfiguration(); | |||
repo.setId( id ); | |||
repo.setName( name ); | |||
repo.setLocation( location.getAbsolutePath() ); | |||
return repo; | |||
} | |||
private RemoteRepositoryConfiguration createRemoteRepository( String id, String name, String url ) | |||
{ | |||
RemoteRepositoryConfiguration repo = new RemoteRepositoryConfiguration(); | |||
repo.setId( id ); | |||
repo.setName( name ); | |||
repo.setUrl( url ); | |||
return repo; | |||
} | |||
} |
@@ -29,15 +29,64 @@ | |||
<lifecycle-handler>basic</lifecycle-handler> | |||
</component> | |||
<!-- | |||
| Configuration | |||
--> | |||
<component> | |||
<role>org.apache.maven.archiva.configuration.ArchivaConfiguration</role> | |||
<implementation>org.apache.maven.archiva.configuration.DefaultArchivaConfiguration</implementation> | |||
<requirements> | |||
<requirement> | |||
<role>org.codehaus.plexus.registry.Registry</role> | |||
<role-hint>configured</role-hint> | |||
</requirement> | |||
</requirements> | |||
</component> | |||
<component> | |||
<role>org.codehaus.plexus.registry.Registry</role> | |||
<role-hint>configured</role-hint> | |||
<implementation>org.codehaus.plexus.registry.commons.CommonsConfigurationRegistry</implementation> | |||
<configuration> | |||
<properties> | |||
<system/> | |||
<xml fileName="${appserver.base}/conf/archiva.xml" | |||
config-name="org.apache.maven.archiva.base" config-at="org.apache.maven.archiva"/> | |||
</properties> | |||
</configuration> | |||
</component> | |||
<component> | |||
<role>org.codehaus.plexus.webdav.DavServerManager</role> | |||
<role-hint>default</role-hint> | |||
<implementation>org.codehaus.plexus.webdav.DefaultDavServerManager</implementation> | |||
<description>DefaultDavServerManager</description> | |||
<configuration> | |||
<provider-hint>proxied</provider-hint> | |||
</configuration> | |||
</component> | |||
<component> | |||
<role>org.codehaus.plexus.cache.Cache</role> | |||
<role-hint>url-failures-cache</role-hint> | |||
<implementation>org.codehaus.plexus.cache.ehcache.EhcacheCache</implementation> | |||
<description>URL Failure Cache</description> | |||
<configuration> | |||
<disk-expiry-thread-interval-seconds>600</disk-expiry-thread-interval-seconds> | |||
<disk-persistent>false</disk-persistent> <!--disabling disk persistence for unit testing. --> | |||
<disk-store-path>${java.io.tmpdir}/archiva/urlcache</disk-store-path> | |||
<eternal>false</eternal> | |||
<max-elements-in-memory>1000</max-elements-in-memory> | |||
<memory-eviction-policy>LRU</memory-eviction-policy> | |||
<name>url-failures-cache</name> | |||
<overflow-to-disk>false</overflow-to-disk> | |||
<!-- 45 minutes = 2700 seconds --> | |||
<time-to-idle-seconds>2700</time-to-idle-seconds> | |||
<!-- 30 minutes = 1800 seconds --> | |||
<time-to-live-seconds>1800</time-to-live-seconds> | |||
</configuration> | |||
</component> | |||
<!-- Don't drag in the world just to test this --> | |||
<component> | |||
<role>org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers</role> |
@@ -0,0 +1,111 @@ | |||
<?xml version="1.0" encoding="ISO-8859-1"?> | |||
<!-- | |||
~ 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. | |||
--> | |||
<configuration> | |||
<version>2</version> | |||
<repositoryScanning> | |||
<fileTypes> | |||
<fileType> | |||
<id>artifacts</id> | |||
<patterns> | |||
<pattern>**/*.pom</pattern> | |||
<pattern>**/*.jar</pattern> | |||
<pattern>**/*.ear</pattern> | |||
<pattern>**/*.war</pattern> | |||
<pattern>**/*.car</pattern> | |||
<pattern>**/*.sar</pattern> | |||
<pattern>**/*.mar</pattern> | |||
<pattern>**/*.rar</pattern> | |||
<pattern>**/*.dtd</pattern> | |||
<pattern>**/*.tld</pattern> | |||
<pattern>**/*.tar.gz</pattern> | |||
<pattern>**/*.tar.bz2</pattern> | |||
<pattern>**/*.zip</pattern> | |||
</patterns> | |||
</fileType> | |||
<fileType> | |||
<id>indexable-content</id> | |||
<patterns> | |||
<pattern>**/*.txt</pattern> | |||
<pattern>**/*.TXT</pattern> | |||
<pattern>**/*.block</pattern> | |||
<pattern>**/*.config</pattern> | |||
<pattern>**/*.pom</pattern> | |||
<pattern>**/*.xml</pattern> | |||
<pattern>**/*.xsd</pattern> | |||
<pattern>**/*.dtd</pattern> | |||
<pattern>**/*.tld</pattern> | |||
</patterns> | |||
</fileType> | |||
<fileType> | |||
<id>auto-remove</id> | |||
<patterns> | |||
<pattern>**/*.bak</pattern> | |||
<pattern>**/*~</pattern> | |||
<pattern>**/*-</pattern> | |||
</patterns> | |||
</fileType> | |||
<fileType> | |||
<id>ignored</id> | |||
<patterns> | |||
<pattern>**/.htaccess</pattern> | |||
<pattern>**/KEYS</pattern> | |||
<pattern>**/*.rb</pattern> | |||
<pattern>**/*.sh</pattern> | |||
<pattern>**/.svn/**</pattern> | |||
<pattern>**/.DAV/**</pattern> | |||
</patterns> | |||
</fileType> | |||
</fileTypes> | |||
<knownContentConsumers> | |||
<knownContentConsumer>update-db-artifact</knownContentConsumer> | |||
<knownContentConsumer>create-missing-checksums</knownContentConsumer> | |||
<knownContentConsumer>update-db-repository-metadata</knownContentConsumer> | |||
<knownContentConsumer>validate-checksum</knownContentConsumer> | |||
<knownContentConsumer>validate-signature</knownContentConsumer> | |||
<knownContentConsumer>index-content</knownContentConsumer> | |||
<knownContentConsumer>auto-remove</knownContentConsumer> | |||
<knownContentConsumer>auto-rename</knownContentConsumer> | |||
</knownContentConsumers> | |||
<invalidContentConsumers> | |||
<invalidContentConsumer>update-db-bad-content</invalidContentConsumer> | |||
</invalidContentConsumers> | |||
</repositoryScanning> | |||
<databaseScanning> | |||
<cronExpression>0 0 * * ?</cronExpression> | |||
<unprocessedConsumers> | |||
<unprocessedConsumer>index-artifact</unprocessedConsumer> | |||
<unprocessedConsumer>update-db-project</unprocessedConsumer> | |||
<unprocessedConsumer>validate-repository-metadata</unprocessedConsumer> | |||
<unprocessedConsumer>index-archive-toc</unprocessedConsumer> | |||
<unprocessedConsumer>update-db-bytecode-stats</unprocessedConsumer> | |||
<unprocessedConsumer>index-public-methods</unprocessedConsumer> | |||
</unprocessedConsumers> | |||
<cleanupConsumers> | |||
<cleanupConsumer>not-present-remove-db-artifact</cleanupConsumer> | |||
<cleanupConsumer>not-present-remove-db-project</cleanupConsumer> | |||
<cleanupConsumer>not-present-remove-indexed</cleanupConsumer> | |||
</cleanupConsumers> | |||
</databaseScanning> | |||
</configuration> |