]> source.dussan.org Git - archiva.git/blob
69dca5d1aa4a0bd8fc0e86dc37f2420560ce1214
[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                 String requestURL = request.getRequest().getRequestURL().toString();
150                 
151                 // [MRM-440] - If webdav URL lacks a trailing /, navigating to all links in the listing return 404.
152                 if( !requestURL.endsWith( "/" ) )
153                 {
154                     String redirectToLocation = requestURL + "/";
155                     response.sendRedirect( redirectToLocation );
156                     return;
157                 }
158                 
159                 // Process the request.
160                 davServer.process( request, response );
161                 
162                 // All done.
163                 return;
164             }
165
166             // At this point the incoming request can either be in default or legacy layout format.
167             try
168             {
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 );
172
173                 // Adjust the pathInfo resource to be in the format that the dav server impl expects.
174                 request.getRequest().setPathInfo( resource );
175
176                 // Attempt to fetch the resource from any defined proxy.
177                 fetchContentFromProxies( request, resource );
178             }
179             catch ( LayoutException e )
180             {
181                 // Invalid resource, pass it on.
182                 respondResourceMissing( request, response, e );
183
184                 // All done.
185                 return;
186             }
187
188             if ( resourceFile.exists() )
189             {
190                 // [MRM-503] - Metadata file need Pragma:no-cache response header.
191                 if ( request.getLogicalResource().endsWith( "/maven-metadata.xml" ) )
192                 {
193                     response.addHeader( "Pragma", "no-cache" );
194                     response.addHeader( "Cache-Control", "no-cache" );
195                 }
196
197                 // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
198
199                 davServer.process( request, response );
200             }
201             else
202             {
203                 respondResourceMissing( request, response, null );
204             }
205         }
206
207         if ( isPut )
208         {
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.
215              */
216
217             File rootDirectory = getRootDirectory();
218             if ( rootDirectory != null )
219             {
220                 new File( rootDirectory, request.getLogicalResource() ).getParentFile().mkdirs();
221             }
222             
223             // Allow the dav server to process the put request.
224             davServer.process( request, response );
225             
226             // All done.
227             return;
228         }
229     }
230
231     private void respondResourceMissing( DavServerRequest request, HttpServletResponse response, Throwable t )
232     {
233         response.setStatus( HttpServletResponse.SC_NOT_FOUND );
234
235         try
236         {
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() );
242
243             String message = "Error 404 Not Found";
244
245             PrintWriter out = new PrintWriter( response.getOutputStream() );
246
247             response.setContentType( "text/html; charset=\"UTF-8\"" );
248
249             out.println( "<html>" );
250             out.println( "<head><title>" + message + "</title></head>" );
251             out.println( "<body>" );
252
253             out.print( "<p><h1>" );
254             out.print( message );
255             out.println( "</h1></p>" );
256
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>" );
262             
263             if ( t != null )
264             {
265                 out.println( "<pre>" );
266                 t.printStackTrace( out );
267                 out.println( "</pre>" );
268             }
269
270             out.println( "</body></html>" );
271
272             out.flush();
273         }
274         catch ( IOException e )
275         {
276             e.printStackTrace();
277         }
278     }
279
280     private void fetchContentFromProxies( DavServerRequest request, String resource )
281         throws ServletException
282     {
283         if ( repositoryRequest.isSupportFile( resource ) )
284         {
285             // Checksums are fetched with artifact / metadata.
286             
287             // Need to adjust the path for the checksum resource.
288             return;
289         }
290
291         // Is it a Metadata resource?
292         if ( repositoryRequest.isDefault( resource ) && repositoryRequest.isMetadata( resource ) )
293         {
294             if ( fetchMetadataFromProxies( request, resource ) )
295             {
296                 return;
297             }
298         }
299
300         // Not any of the above? Then it's gotta be an artifact reference.
301         try
302         {
303             // Get the artifact reference in a layout neutral way.
304             ArtifactReference artifact = repositoryRequest.toArtifactReference( resource );
305             
306             if ( artifact != null )
307             {
308                 applyServerSideRelocation( artifact );
309
310                 connectors.fetchFromProxies( managedRepository, artifact );
311                 
312                 // Set the path to the resource using managed repository specific layout format.
313                 request.getRequest().setPathInfo( managedRepository.toPath( artifact ) );
314                 return;
315             }
316         }
317         catch ( LayoutException e )
318         {
319             /* eat it */
320         }
321         catch ( ProxyException e )
322         {
323             throw new ServletException( "Unable to fetch artifact resource.", e );
324         }
325     }
326
327     private boolean fetchMetadataFromProxies( DavServerRequest request, String resource )
328         throws ServletException
329     {
330         ProjectReference project;
331         VersionedReference versioned;
332
333         try
334         {
335
336             versioned = metadataTools.toVersionedReference( resource );
337             if ( versioned != null )
338             {
339                 connectors.fetchFromProxies( managedRepository, versioned );
340                 return true;
341             }
342         }
343         catch ( RepositoryMetadataException e )
344         {
345             /* eat it */
346         }
347         catch ( ProxyException e )
348         {
349             throw new ServletException( "Unable to fetch versioned metadata resource.", e );
350         }
351
352         try
353         {
354             project = metadataTools.toProjectReference( resource );
355             if ( project != null )
356             {
357                 connectors.fetchFromProxies( managedRepository, project );
358                 return true;
359             }
360         }
361         catch ( RepositoryMetadataException e )
362         {
363             /* eat it */
364         }
365         catch ( ProxyException e )
366         {
367             throw new ServletException( "Unable to fetch project metadata resource.", e );
368         }
369         
370         return false;
371     }
372
373     /**
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
377      * metadatas.
378      * <p>
379      * For such clients, archiva does server-side relocation by reading itself
380      * the &lt;relocation&gt; element in metadatas and serving the expected
381      * artifact.
382      */
383     protected void applyServerSideRelocation( ArtifactReference artifact )
384         throws ProxyException
385     {
386         if ( "pom".equals( artifact.getType() ) )
387         {
388             return;
389         }
390
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" );
397
398         // Get the artifact POM from proxied repositories if needed
399         connectors.fetchFromProxies( managedRepository, pomReference );
400
401         // Open and read the POM from the managed repo
402         File pom = managedRepository.toFile( pomReference );
403         
404         if( !pom.exists() )
405         {
406             return;
407         }
408         
409         try
410         {
411             Model model = new MavenXpp3Reader().read( new FileReader( pom ) );
412             DistributionManagement dist = model.getDistributionManagement();
413             if ( dist != null )
414             {
415                 Relocation relocation = dist.getRelocation();
416                 if ( relocation != null )
417                 {
418                     // artifact is relocated : update the repositoryPath
419                     if ( relocation.getGroupId() != null )
420                     {
421                         artifact.setGroupId( relocation.getGroupId() );
422                     }
423                     if ( relocation.getArtifactId() != null )
424                     {
425                         artifact.setArtifactId( relocation.getArtifactId() );
426                     }
427                     if ( relocation.getVersion() != null )
428                     {
429                         artifact.setVersion( relocation.getVersion() );
430                     }
431                 }
432             }
433         }
434         catch ( FileNotFoundException e )
435         {
436             // Artifact has no POM in repo : ignore
437         }
438         catch ( IOException e )
439         {
440             // Unable to read POM : ignore.
441         }
442         catch ( XmlPullParserException e )
443         {
444             // Invalid POM : ignore
445         }
446     }
447     
448     @Override
449     public void addListener( DavServerListener listener )
450     {
451         super.addListener( listener );
452         davServer.addListener( listener );
453     }
454     
455     @Override
456     public boolean isUseIndexHtml()
457     {
458         return davServer.isUseIndexHtml();
459     }
460     
461     @Override
462     public boolean hasResource( String resource )
463     {
464         return davServer.hasResource( resource );
465     }
466     
467     @Override
468     public void removeListener( DavServerListener listener )
469     {
470         davServer.removeListener( listener );
471     }
472     
473     @Override
474     public void setUseIndexHtml( boolean useIndexHtml )
475     {
476         super.setUseIndexHtml( useIndexHtml );
477         davServer.setUseIndexHtml( useIndexHtml );
478     }
479     
480     public ManagedRepositoryContent getRepository()
481     {
482         return managedRepository;
483     }
484 }