]> source.dussan.org Git - archiva.git/blob
9986526182fa0fea233c29ad5999b36b4f3d83da
[archiva.git] /
1 package org.apache.maven.archiva.web.repository;
2
3 /*
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
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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;
46
47 import java.io.File;
48 import java.io.FileNotFoundException;
49 import java.io.FileReader;
50 import java.io.IOException;
51 import java.io.PrintWriter;
52
53 import javax.servlet.ServletConfig;
54 import javax.servlet.ServletException;
55 import javax.servlet.http.HttpServletResponse;
56
57 /**
58  * ProxiedDavServer
59  *
60  * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
61  * @version $Id$
62  * @plexus.component role="org.codehaus.plexus.webdav.DavServerComponent"
63  * role-hint="proxied" instantiation-strategy="per-lookup"
64  */
65 public class ProxiedDavServer
66     extends AbstractDavServerComponent
67 {
68     /**
69      * @plexus.requirement role-hint="simple"
70      */
71     private DavServerComponent davServer;
72
73     /**
74      * @plexus.requirement
75      */
76     private RepositoryContentFactory repositoryFactory;
77
78     /**
79      * @plexus.requirement
80      */
81     private RepositoryRequest repositoryRequest;
82
83     /**
84      * @plexus.requirement role-hint="default"
85      */
86     private RepositoryProxyConnectors connectors;
87
88     /**
89      * @plexus.requirement
90      */
91     private MetadataTools metadataTools;
92
93     private ManagedRepositoryContent managedRepository;
94
95     public String getPrefix()
96     {
97         return davServer.getPrefix();
98     }
99
100     public File getRootDirectory()
101     {
102         return davServer.getRootDirectory();
103     }
104
105     public void setPrefix( String prefix )
106     {
107         davServer.setPrefix( prefix );
108     }
109
110     public void setRootDirectory( File rootDirectory )
111     {
112         davServer.setRootDirectory( rootDirectory );
113     }
114
115     public void init( ServletConfig servletConfig )
116         throws DavServerException
117     {
118         davServer.init( servletConfig );
119
120         try
121         {
122             managedRepository = repositoryFactory.getManagedRepositoryContent( getPrefix() );
123         }
124         catch ( RepositoryNotFoundException e )
125         {
126             throw new DavServerException( e.getMessage(), e );
127         }
128         catch ( RepositoryException e )
129         {
130             throw new DavServerException( e.getMessage(), e );
131         }
132     }
133
134     public void process( DavServerRequest request, HttpServletResponse response )
135         throws DavServerException, ServletException, IOException
136     {
137         boolean isGet = WebdavMethodUtil.isReadMethod( request.getRequest().getMethod() );
138         boolean isPut = WebdavMethodUtil.isWriteMethod( request.getRequest().getMethod() );
139         
140         if ( isGet )
141         {
142             // Default behaviour is to treat the resource natively.
143             String resource = request.getLogicalResource();
144             File resourceFile = new File( managedRepository.getRepoRoot(), resource );
145
146             // If this a directory resource, then we are likely browsing.
147             if ( resourceFile.exists() && resourceFile.isDirectory() )
148             {
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.
151                 
152                 // Process the request.
153                 davServer.process( request, response );
154                 
155                 // All done.
156                 return;
157             }
158
159             // At this point the incoming request can either be in default or legacy layout format.
160             try
161             {
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 );
165
166                 // Adjust the pathInfo resource to be in the format that the dav server impl expects.
167                 request.getRequest().setPathInfo( resource );
168
169                 // Attempt to fetch the resource from any defined proxy.
170                 fetchContentFromProxies( request, resource );
171             }
172             catch ( LayoutException e )
173             {
174                 // Invalid resource, pass it on.
175                 respondResourceMissing( request, response, e );
176
177                 // All done.
178                 return;
179             }
180
181             if ( resourceFile.exists() )
182             {
183                 // [MRM-503] - Metadata file need Pragma:no-cache response header.
184                 if ( request.getLogicalResource().endsWith( "/maven-metadata.xml" ) )
185                 {
186                     response.addHeader( "Pragma", "no-cache" );
187                     response.addHeader( "Cache-Control", "no-cache" );
188                 }
189
190                 // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
191
192                 davServer.process( request, response );
193             }
194             else
195             {
196                 respondResourceMissing( request, response, null );
197             }
198         }
199
200         if ( isPut )
201         {
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.
208              */
209
210             File rootDirectory = getRootDirectory();
211             if ( rootDirectory != null )
212             {
213                 new File( rootDirectory, request.getLogicalResource() ).getParentFile().mkdirs();
214             }
215             
216             // Allow the dav server to process the put request.
217             davServer.process( request, response );
218             
219             // All done.
220             return;
221         }
222     }
223
224     private void respondResourceMissing( DavServerRequest request, HttpServletResponse response, Throwable t )
225     {
226         response.setStatus( HttpServletResponse.SC_NOT_FOUND );
227
228         try
229         {
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() );
235
236             String message = "Error 404 Not Found";
237
238             PrintWriter out = new PrintWriter( response.getOutputStream() );
239
240             response.setContentType( "text/html; charset=\"UTF-8\"" );
241
242             out.println( "<html>" );
243             out.println( "<head><title>" + message + "</title></head>" );
244             out.println( "<body>" );
245
246             out.print( "<p><h1>" );
247             out.print( message );
248             out.println( "</h1></p>" );
249
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>" );
255             
256             if ( t != null )
257             {
258                 out.println( "<pre>" );
259                 t.printStackTrace( out );
260                 out.println( "</pre>" );
261             }
262
263             out.println( "</body></html>" );
264
265             out.flush();
266         }
267         catch ( IOException e )
268         {
269             e.printStackTrace();
270         }
271     }
272
273     private void fetchContentFromProxies( DavServerRequest request, String resource )
274         throws ServletException
275     {
276         if ( repositoryRequest.isSupportFile( resource ) )
277         {
278             // Checksums are fetched with artifact / metadata.
279             
280             // Need to adjust the path for the checksum resource.
281             return;
282         }
283
284         // Is it a Metadata resource?
285         if ( repositoryRequest.isDefault( resource ) && repositoryRequest.isMetadata( resource ) )
286         {
287             if ( fetchMetadataFromProxies( request, resource ) )
288             {
289                 return;
290             }
291         }
292
293         // Not any of the above? Then it's gotta be an artifact reference.
294         try
295         {
296             // Get the artifact reference in a layout neutral way.
297             ArtifactReference artifact = repositoryRequest.toArtifactReference( resource );
298             
299             if ( artifact != null )
300             {
301                 applyServerSideRelocation( artifact );
302
303                 connectors.fetchFromProxies( managedRepository, artifact );
304                 
305                 // Set the path to the resource using managed repository specific layout format.
306                 request.getRequest().setPathInfo( managedRepository.toPath( artifact ) );
307                 return;
308             }
309         }
310         catch ( LayoutException e )
311         {
312             /* eat it */
313         }
314         catch ( ProxyException e )
315         {
316             throw new ServletException( "Unable to fetch artifact resource.", e );
317         }
318     }
319
320     private boolean fetchMetadataFromProxies( DavServerRequest request, String resource )
321         throws ServletException
322     {
323         ProjectReference project;
324         VersionedReference versioned;
325
326         try
327         {
328
329             versioned = metadataTools.toVersionedReference( resource );
330             if ( versioned != null )
331             {
332                 connectors.fetchFromProxies( managedRepository, versioned );
333                 return true;
334             }
335         }
336         catch ( RepositoryMetadataException e )
337         {
338             /* eat it */
339         }
340         catch ( ProxyException e )
341         {
342             throw new ServletException( "Unable to fetch versioned metadata resource.", e );
343         }
344
345         try
346         {
347             project = metadataTools.toProjectReference( resource );
348             if ( project != null )
349             {
350                 connectors.fetchFromProxies( managedRepository, project );
351                 return true;
352             }
353         }
354         catch ( RepositoryMetadataException e )
355         {
356             /* eat it */
357         }
358         catch ( ProxyException e )
359         {
360             throw new ServletException( "Unable to fetch project metadata resource.", e );
361         }
362         
363         return false;
364     }
365
366     /**
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
370      * metadatas.
371      * <p>
372      * For such clients, archiva does server-side relocation by reading itself
373      * the &lt;relocation&gt; element in metadatas and serving the expected
374      * artifact.
375      */
376     protected void applyServerSideRelocation( ArtifactReference artifact )
377         throws ProxyException
378     {
379         if ( "pom".equals( artifact.getType() ) )
380         {
381             return;
382         }
383
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" );
390
391         // Get the artifact POM from proxied repositories if needed
392         connectors.fetchFromProxies( managedRepository, pomReference );
393
394         // Open and read the POM from the managed repo
395         File pom = managedRepository.toFile( pomReference );
396         
397         if( !pom.exists() )
398         {
399             return;
400         }
401         
402         try
403         {
404             Model model = new MavenXpp3Reader().read( new FileReader( pom ) );
405             DistributionManagement dist = model.getDistributionManagement();
406             if ( dist != null )
407             {
408                 Relocation relocation = dist.getRelocation();
409                 if ( relocation != null )
410                 {
411                     // artifact is relocated : update the repositoryPath
412                     if ( relocation.getGroupId() != null )
413                     {
414                         artifact.setGroupId( relocation.getGroupId() );
415                     }
416                     if ( relocation.getArtifactId() != null )
417                     {
418                         artifact.setArtifactId( relocation.getArtifactId() );
419                     }
420                     if ( relocation.getVersion() != null )
421                     {
422                         artifact.setVersion( relocation.getVersion() );
423                     }
424                 }
425             }
426         }
427         catch ( FileNotFoundException e )
428         {
429             // Artifact has no POM in repo : ignore
430         }
431         catch ( IOException e )
432         {
433             // Unable to read POM : ignore.
434         }
435         catch ( XmlPullParserException e )
436         {
437             // Invalid POM : ignore
438         }
439     }
440     
441     @Override
442     public void addListener( DavServerListener listener )
443     {
444         super.addListener( listener );
445         davServer.addListener( listener );
446     }
447     
448     @Override
449     public boolean isUseIndexHtml()
450     {
451         return davServer.isUseIndexHtml();
452     }
453     
454     @Override
455     public boolean hasResource( String resource )
456     {
457         return davServer.hasResource( resource );
458     }
459     
460     @Override
461     public void removeListener( DavServerListener listener )
462     {
463         davServer.removeListener( listener );
464     }
465     
466     @Override
467     public void setUseIndexHtml( boolean useIndexHtml )
468     {
469         super.setUseIndexHtml( useIndexHtml );
470         davServer.setUseIndexHtml( useIndexHtml );
471     }
472     
473     public ManagedRepositoryContent getRepository()
474     {
475         return managedRepository;
476     }
477 }