1 package org.apache.maven.archiva.web.repository;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.maven.archiva.model.ArtifactReference;
23 import org.apache.maven.archiva.model.ProjectReference;
24 import org.apache.maven.archiva.model.VersionedReference;
25 import org.apache.maven.archiva.proxy.ProxyException;
26 import org.apache.maven.archiva.proxy.RepositoryProxyConnectors;
27 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
28 import org.apache.maven.archiva.repository.RepositoryContentFactory;
29 import org.apache.maven.archiva.repository.RepositoryException;
30 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
31 import org.apache.maven.archiva.repository.content.RepositoryRequest;
32 import org.apache.maven.archiva.repository.layout.LayoutException;
33 import org.apache.maven.archiva.repository.metadata.MetadataTools;
34 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
35 import org.apache.maven.model.DistributionManagement;
36 import org.apache.maven.model.Model;
37 import org.apache.maven.model.Relocation;
38 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
39 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
40 import org.codehaus.plexus.webdav.AbstractDavServerComponent;
41 import org.codehaus.plexus.webdav.DavServerComponent;
42 import org.codehaus.plexus.webdav.DavServerException;
43 import org.codehaus.plexus.webdav.DavServerListener;
44 import org.codehaus.plexus.webdav.servlet.DavServerRequest;
45 import org.codehaus.plexus.webdav.util.WebdavMethodUtil;
48 import java.io.FileNotFoundException;
49 import java.io.FileReader;
50 import java.io.IOException;
51 import java.io.PrintWriter;
53 import javax.servlet.ServletConfig;
54 import javax.servlet.ServletException;
55 import javax.servlet.http.HttpServletResponse;
60 * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
62 * @plexus.component role="org.codehaus.plexus.webdav.DavServerComponent"
63 * role-hint="proxied" instantiation-strategy="per-lookup"
65 public class ProxiedDavServer
66 extends AbstractDavServerComponent
69 * @plexus.requirement role-hint="simple"
71 private DavServerComponent davServer;
76 private RepositoryContentFactory repositoryFactory;
81 private RepositoryRequest repositoryRequest;
84 * @plexus.requirement role-hint="default"
86 private RepositoryProxyConnectors connectors;
91 private MetadataTools metadataTools;
93 private ManagedRepositoryContent managedRepository;
95 public String getPrefix()
97 return davServer.getPrefix();
100 public File getRootDirectory()
102 return davServer.getRootDirectory();
105 public void setPrefix( String prefix )
107 davServer.setPrefix( prefix );
110 public void setRootDirectory( File rootDirectory )
112 davServer.setRootDirectory( rootDirectory );
115 public void init( ServletConfig servletConfig )
116 throws DavServerException
118 davServer.init( servletConfig );
122 managedRepository = repositoryFactory.getManagedRepositoryContent( getPrefix() );
124 catch ( RepositoryNotFoundException e )
126 throw new DavServerException( e.getMessage(), e );
128 catch ( RepositoryException e )
130 throw new DavServerException( e.getMessage(), e );
134 public void process( DavServerRequest request, HttpServletResponse response )
135 throws DavServerException, ServletException, IOException
137 boolean isGet = WebdavMethodUtil.isReadMethod( request.getRequest().getMethod() );
138 boolean isPut = WebdavMethodUtil.isWriteMethod( request.getRequest().getMethod() );
142 // Default behaviour is to treat the resource natively.
143 String resource = request.getLogicalResource();
144 File resourceFile = new File( managedRepository.getRepoRoot(), resource );
146 // If this a directory resource, then we are likely browsing.
147 if ( resourceFile.exists() && resourceFile.isDirectory() )
149 String requestURL = request.getRequest().getRequestURL().toString();
151 // [MRM-440] - If webdav URL lacks a trailing /, navigating to all links in the listing return 404.
152 if( !requestURL.endsWith( "/" ) )
154 String redirectToLocation = requestURL + "/";
155 response.sendRedirect( redirectToLocation );
159 // Process the request.
160 davServer.process( request, response );
166 // At this point the incoming request can either be in default or legacy layout format.
169 // Perform an adjustment of the resource to the managed repository expected path.
170 resource = repositoryRequest.toNativePath( request.getLogicalResource(), managedRepository );
171 resourceFile = new File( managedRepository.getRepoRoot(), resource );
173 // Adjust the pathInfo resource to be in the format that the dav server impl expects.
174 request.getRequest().setPathInfo( resource );
176 // Attempt to fetch the resource from any defined proxy.
177 fetchContentFromProxies( request, resource );
179 catch ( LayoutException e )
181 // Invalid resource, pass it on.
182 respondResourceMissing( request, response, e );
188 if ( resourceFile.exists() )
190 // [MRM-503] - Metadata file need Pragma:no-cache response header.
191 if ( request.getLogicalResource().endsWith( "/maven-metadata.xml" ) )
193 response.addHeader( "Pragma", "no-cache" );
194 response.addHeader( "Cache-Control", "no-cache" );
197 // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
199 davServer.process( request, response );
203 respondResourceMissing( request, response, null );
209 /* Create parent directories that don't exist when writing a file
210 * This actually makes this implementation not compliant to the
211 * WebDAV RFC - but we have enough knowledge
212 * about how the collection is being used to do this reasonably and
213 * some versions of Maven's WebDAV don't
214 * correctly create the collections themselves.
217 File rootDirectory = getRootDirectory();
218 if ( rootDirectory != null )
220 new File( rootDirectory, request.getLogicalResource() ).getParentFile().mkdirs();
223 // Allow the dav server to process the put request.
224 davServer.process( request, response );
231 private void respondResourceMissing( DavServerRequest request, HttpServletResponse response, Throwable t )
233 response.setStatus( HttpServletResponse.SC_NOT_FOUND );
237 StringBuffer missingUrl = new StringBuffer();
238 missingUrl.append( request.getRequest().getScheme() ).append( "://" );
239 missingUrl.append( request.getRequest().getServerName() ).append( ":" );
240 missingUrl.append( request.getRequest().getServerPort() );
241 missingUrl.append( request.getRequest().getServletPath() );
243 String message = "Error 404 Not Found";
245 PrintWriter out = new PrintWriter( response.getOutputStream() );
247 response.setContentType( "text/html; charset=\"UTF-8\"" );
249 out.println( "<html>" );
250 out.println( "<head><title>" + message + "</title></head>" );
251 out.println( "<body>" );
253 out.print( "<p><h1>" );
254 out.print( message );
255 out.println( "</h1></p>" );
257 out.print( "<p>The following resource does not exist: <a href=\"" );
258 out.print( missingUrl.toString() );
259 out.println( "\">" );
260 out.print( missingUrl.toString() );
261 out.println( "</a></p>" );
265 out.println( "<pre>" );
266 t.printStackTrace( out );
267 out.println( "</pre>" );
270 out.println( "</body></html>" );
274 catch ( IOException e )
280 private void fetchContentFromProxies( DavServerRequest request, String resource )
281 throws ServletException
283 if ( repositoryRequest.isSupportFile( resource ) )
285 // Checksums are fetched with artifact / metadata.
287 // Need to adjust the path for the checksum resource.
291 // Is it a Metadata resource?
292 if ( repositoryRequest.isDefault( resource ) && repositoryRequest.isMetadata( resource ) )
294 if ( fetchMetadataFromProxies( request, resource ) )
300 // Not any of the above? Then it's gotta be an artifact reference.
303 // Get the artifact reference in a layout neutral way.
304 ArtifactReference artifact = repositoryRequest.toArtifactReference( resource );
306 if ( artifact != null )
308 applyServerSideRelocation( artifact );
310 connectors.fetchFromProxies( managedRepository, artifact );
312 // Set the path to the resource using managed repository specific layout format.
313 request.getRequest().setPathInfo( managedRepository.toPath( artifact ) );
317 catch ( LayoutException e )
321 catch ( ProxyException e )
323 throw new ServletException( "Unable to fetch artifact resource.", e );
327 private boolean fetchMetadataFromProxies( DavServerRequest request, String resource )
328 throws ServletException
330 ProjectReference project;
331 VersionedReference versioned;
336 versioned = metadataTools.toVersionedReference( resource );
337 if ( versioned != null )
339 connectors.fetchFromProxies( managedRepository, versioned );
343 catch ( RepositoryMetadataException e )
347 catch ( ProxyException e )
349 throw new ServletException( "Unable to fetch versioned metadata resource.", e );
354 project = metadataTools.toProjectReference( resource );
355 if ( project != null )
357 connectors.fetchFromProxies( managedRepository, project );
361 catch ( RepositoryMetadataException e )
365 catch ( ProxyException e )
367 throw new ServletException( "Unable to fetch project metadata resource.", e );
374 * A relocation capable client will request the POM prior to the artifact,
375 * and will then read meta-data and do client side relocation. A simplier
376 * client (like maven 1) will only request the artifact and not use the
379 * For such clients, archiva does server-side relocation by reading itself
380 * the <relocation> element in metadatas and serving the expected
383 protected void applyServerSideRelocation( ArtifactReference artifact )
384 throws ProxyException
386 if ( "pom".equals( artifact.getType() ) )
391 // Build the artifact POM reference
392 ArtifactReference pomReference = new ArtifactReference();
393 pomReference.setGroupId( artifact.getGroupId() );
394 pomReference.setArtifactId( artifact.getArtifactId() );
395 pomReference.setVersion( artifact.getVersion() );
396 pomReference.setType( "pom" );
398 // Get the artifact POM from proxied repositories if needed
399 connectors.fetchFromProxies( managedRepository, pomReference );
401 // Open and read the POM from the managed repo
402 File pom = managedRepository.toFile( pomReference );
411 Model model = new MavenXpp3Reader().read( new FileReader( pom ) );
412 DistributionManagement dist = model.getDistributionManagement();
415 Relocation relocation = dist.getRelocation();
416 if ( relocation != null )
418 // artifact is relocated : update the repositoryPath
419 if ( relocation.getGroupId() != null )
421 artifact.setGroupId( relocation.getGroupId() );
423 if ( relocation.getArtifactId() != null )
425 artifact.setArtifactId( relocation.getArtifactId() );
427 if ( relocation.getVersion() != null )
429 artifact.setVersion( relocation.getVersion() );
434 catch ( FileNotFoundException e )
436 // Artifact has no POM in repo : ignore
438 catch ( IOException e )
440 // Unable to read POM : ignore.
442 catch ( XmlPullParserException e )
444 // Invalid POM : ignore
449 public void addListener( DavServerListener listener )
451 super.addListener( listener );
452 davServer.addListener( listener );
456 public boolean isUseIndexHtml()
458 return davServer.isUseIndexHtml();
462 public boolean hasResource( String resource )
464 return davServer.hasResource( resource );
468 public void removeListener( DavServerListener listener )
470 davServer.removeListener( listener );
474 public void setUseIndexHtml( boolean useIndexHtml )
476 super.setUseIndexHtml( useIndexHtml );
477 davServer.setUseIndexHtml( useIndexHtml );
480 public ManagedRepositoryContent getRepository()
482 return managedRepository;