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 // TODO: [MRM-440] - If webdav URL lacks a trailing /, navigating to all links in the listing return 404.
150 // TODO: Issue redirect with proper pathing.
152 // Process the request.
153 davServer.process( request, response );
159 // At this point the incoming request can either be in default or legacy layout format.
162 // Perform an adjustment of the resource to the managed repository expected path.
163 resource = repositoryRequest.toNativePath( request.getLogicalResource(), managedRepository );
164 resourceFile = new File( managedRepository.getRepoRoot(), resource );
166 // Adjust the pathInfo resource to be in the format that the dav server impl expects.
167 request.getRequest().setPathInfo( resource );
169 // Attempt to fetch the resource from any defined proxy.
170 fetchContentFromProxies( request, resource );
172 catch ( LayoutException e )
174 // Invalid resource, pass it on.
175 respondResourceMissing( request, response, e );
181 if ( resourceFile.exists() )
183 // [MRM-503] - Metadata file need Pragma:no-cache response header.
184 if ( request.getLogicalResource().endsWith( "/maven-metadata.xml" ) )
186 response.addHeader( "Pragma", "no-cache" );
187 response.addHeader( "Cache-Control", "no-cache" );
190 // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
192 davServer.process( request, response );
196 respondResourceMissing( request, response, null );
202 /* Create parent directories that don't exist when writing a file
203 * This actually makes this implementation not compliant to the
204 * WebDAV RFC - but we have enough knowledge
205 * about how the collection is being used to do this reasonably and
206 * some versions of Maven's WebDAV don't
207 * correctly create the collections themselves.
210 File rootDirectory = getRootDirectory();
211 if ( rootDirectory != null )
213 new File( rootDirectory, request.getLogicalResource() ).getParentFile().mkdirs();
216 // Allow the dav server to process the put request.
217 davServer.process( request, response );
224 private void respondResourceMissing( DavServerRequest request, HttpServletResponse response, Throwable t )
226 response.setStatus( HttpServletResponse.SC_NOT_FOUND );
230 StringBuffer missingUrl = new StringBuffer();
231 missingUrl.append( request.getRequest().getScheme() ).append( "://" );
232 missingUrl.append( request.getRequest().getServerName() ).append( ":" );
233 missingUrl.append( request.getRequest().getServerPort() );
234 missingUrl.append( request.getRequest().getServletPath() );
236 String message = "Error 404 Not Found";
238 PrintWriter out = new PrintWriter( response.getOutputStream() );
240 response.setContentType( "text/html; charset=\"UTF-8\"" );
242 out.println( "<html>" );
243 out.println( "<head><title>" + message + "</title></head>" );
244 out.println( "<body>" );
246 out.print( "<p><h1>" );
247 out.print( message );
248 out.println( "</h1></p>" );
250 out.print( "<p>The following resource does not exist: <a href=\"" );
251 out.print( missingUrl.toString() );
252 out.println( "\">" );
253 out.print( missingUrl.toString() );
254 out.println( "</a></p>" );
258 out.println( "<pre>" );
259 t.printStackTrace( out );
260 out.println( "</pre>" );
263 out.println( "</body></html>" );
267 catch ( IOException e )
273 private void fetchContentFromProxies( DavServerRequest request, String resource )
274 throws ServletException
276 if ( repositoryRequest.isSupportFile( resource ) )
278 // Checksums are fetched with artifact / metadata.
280 // Need to adjust the path for the checksum resource.
284 // Is it a Metadata resource?
285 if ( repositoryRequest.isDefault( resource ) && repositoryRequest.isMetadata( resource ) )
287 if ( fetchMetadataFromProxies( request, resource ) )
293 // Not any of the above? Then it's gotta be an artifact reference.
296 // Get the artifact reference in a layout neutral way.
297 ArtifactReference artifact = repositoryRequest.toArtifactReference( resource );
299 if ( artifact != null )
301 applyServerSideRelocation( artifact );
303 connectors.fetchFromProxies( managedRepository, artifact );
305 // Set the path to the resource using managed repository specific layout format.
306 request.getRequest().setPathInfo( managedRepository.toPath( artifact ) );
310 catch ( LayoutException e )
314 catch ( ProxyException e )
316 throw new ServletException( "Unable to fetch artifact resource.", e );
320 private boolean fetchMetadataFromProxies( DavServerRequest request, String resource )
321 throws ServletException
323 ProjectReference project;
324 VersionedReference versioned;
329 versioned = metadataTools.toVersionedReference( resource );
330 if ( versioned != null )
332 connectors.fetchFromProxies( managedRepository, versioned );
336 catch ( RepositoryMetadataException e )
340 catch ( ProxyException e )
342 throw new ServletException( "Unable to fetch versioned metadata resource.", e );
347 project = metadataTools.toProjectReference( resource );
348 if ( project != null )
350 connectors.fetchFromProxies( managedRepository, project );
354 catch ( RepositoryMetadataException e )
358 catch ( ProxyException e )
360 throw new ServletException( "Unable to fetch project metadata resource.", e );
367 * A relocation capable client will request the POM prior to the artifact,
368 * and will then read meta-data and do client side relocation. A simplier
369 * client (like maven 1) will only request the artifact and not use the
372 * For such clients, archiva does server-side relocation by reading itself
373 * the <relocation> element in metadatas and serving the expected
376 protected void applyServerSideRelocation( ArtifactReference artifact )
377 throws ProxyException
379 if ( "pom".equals( artifact.getType() ) )
384 // Build the artifact POM reference
385 ArtifactReference pomReference = new ArtifactReference();
386 pomReference.setGroupId( artifact.getGroupId() );
387 pomReference.setArtifactId( artifact.getArtifactId() );
388 pomReference.setVersion( artifact.getVersion() );
389 pomReference.setType( "pom" );
391 // Get the artifact POM from proxied repositories if needed
392 connectors.fetchFromProxies( managedRepository, pomReference );
394 // Open and read the POM from the managed repo
395 File pom = managedRepository.toFile( pomReference );
404 Model model = new MavenXpp3Reader().read( new FileReader( pom ) );
405 DistributionManagement dist = model.getDistributionManagement();
408 Relocation relocation = dist.getRelocation();
409 if ( relocation != null )
411 // artifact is relocated : update the repositoryPath
412 if ( relocation.getGroupId() != null )
414 artifact.setGroupId( relocation.getGroupId() );
416 if ( relocation.getArtifactId() != null )
418 artifact.setArtifactId( relocation.getArtifactId() );
420 if ( relocation.getVersion() != null )
422 artifact.setVersion( relocation.getVersion() );
427 catch ( FileNotFoundException e )
429 // Artifact has no POM in repo : ignore
431 catch ( IOException e )
433 // Unable to read POM : ignore.
435 catch ( XmlPullParserException e )
437 // Invalid POM : ignore
442 public void addListener( DavServerListener listener )
444 super.addListener( listener );
445 davServer.addListener( listener );
449 public boolean isUseIndexHtml()
451 return davServer.isUseIndexHtml();
455 public boolean hasResource( String resource )
457 return davServer.hasResource( resource );
461 public void removeListener( DavServerListener listener )
463 davServer.removeListener( listener );
467 public void setUseIndexHtml( boolean useIndexHtml )
469 super.setUseIndexHtml( useIndexHtml );
470 davServer.setUseIndexHtml( useIndexHtml );
473 public ManagedRepositoryContent getRepository()
475 return managedRepository;