1 package org.apache.archiva.webdav;
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.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.configuration.ArchivaConfiguration;
24 import org.apache.archiva.configuration.ConfigurationEvent;
25 import org.apache.archiva.configuration.ConfigurationListener;
26 import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator;
27 import org.apache.archiva.repository.ManagedRepository;
28 import org.apache.archiva.repository.RepositoryRegistry;
29 import org.apache.archiva.repository.content.StorageAsset;
30 import org.apache.archiva.security.ServletAuthenticator;
31 import org.apache.jackrabbit.webdav.DavException;
32 import org.apache.jackrabbit.webdav.DavLocatorFactory;
33 import org.apache.jackrabbit.webdav.DavMethods;
34 import org.apache.jackrabbit.webdav.DavResource;
35 import org.apache.jackrabbit.webdav.DavResourceFactory;
36 import org.apache.jackrabbit.webdav.DavServletResponse;
37 import org.apache.jackrabbit.webdav.DavSessionProvider;
38 import org.apache.jackrabbit.webdav.WebdavRequest;
39 import org.apache.jackrabbit.webdav.WebdavRequestImpl;
40 import org.apache.jackrabbit.webdav.WebdavResponse;
41 import org.apache.jackrabbit.webdav.WebdavResponseImpl;
42 import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45 import org.springframework.context.ConfigurableApplicationContext;
46 import org.springframework.web.context.WebApplicationContext;
47 import org.springframework.web.context.support.WebApplicationContextUtils;
49 import javax.servlet.ServletConfig;
50 import javax.servlet.ServletException;
51 import javax.servlet.http.HttpServletRequest;
52 import javax.servlet.http.HttpServletResponse;
53 import java.io.IOException;
54 import java.nio.file.Files;
55 import java.nio.file.Path;
56 import java.nio.file.Paths;
57 import java.util.LinkedHashMap;
59 import java.util.concurrent.locks.ReentrantReadWriteLock;
60 import java.util.stream.Collectors;
65 public class RepositoryServlet
66 extends AbstractWebdavServlet
67 implements ConfigurationListener
69 private Logger log = LoggerFactory.getLogger( RepositoryServlet.class );
71 private ArchivaConfiguration configuration;
73 RepositoryRegistry repositoryRegistry;
75 private Map<String, ManagedRepository> repositoryMap;
77 private DavLocatorFactory locatorFactory;
79 private DavResourceFactory resourceFactory;
81 private DavSessionProvider sessionProvider;
83 protected final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
86 public void init( ServletConfig servletConfig )
87 throws ServletException
89 super.init( servletConfig );
90 initServers( servletConfig );
94 * Service the given request. This method has been overridden and copy/pasted to allow better exception handling and
95 * to support different realms
99 * @throws ServletException
100 * @throws java.io.IOException
103 protected void service( HttpServletRequest request, HttpServletResponse response )
104 throws ServletException, IOException
106 WebdavRequest webdavRequest = new WebdavRequestImpl( request, getLocatorFactory() );
107 // DeltaV requires 'Cache-Control' header for all methods except 'VERSION-CONTROL' and 'REPORT'.
108 int methodCode = DavMethods.getMethodCode( request.getMethod() );
109 boolean noCache = DavMethods.isDeltaVMethod( webdavRequest ) && !( DavMethods.DAV_VERSION_CONTROL == methodCode
110 || DavMethods.DAV_REPORT == methodCode );
111 WebdavResponse webdavResponse = new WebdavResponseImpl( response, noCache );
112 DavResource resource = null;
116 // make sure there is a authenticated user
117 if ( !getDavSessionProvider().attachSession( webdavRequest ) )
122 // check matching if=header for lock-token relevant operations
124 getResourceFactory().createResource( webdavRequest.getRequestLocator(), webdavRequest, webdavResponse );
126 if ( !isPreconditionValid( webdavRequest, resource ) )
128 webdavResponse.sendError( DavServletResponse.SC_PRECONDITION_FAILED );
131 if ( !execute( webdavRequest, webdavResponse, methodCode, resource ) )
133 super.service( request, response );
137 catch ( UnauthorizedDavException e )
139 webdavResponse.setHeader( "WWW-Authenticate", getAuthenticateHeaderValue( e.getRepositoryName() ) );
140 webdavResponse.sendError( e.getErrorCode(), e.getStatusPhrase() );
142 catch ( BrowserRedirectException e )
144 response.sendRedirect( e.getLocation() );
146 catch ( DavException e )
148 if ( e.getErrorCode() == HttpServletResponse.SC_UNAUTHORIZED )
150 final String msg = "Should throw " + UnauthorizedDavException.class.getName();
152 webdavResponse.sendError( e.getErrorCode(), msg );
154 else if ( e.getCause() != null )
156 webdavResponse.sendError( e.getErrorCode(), e.getCause().getMessage() );
160 webdavResponse.sendError( e.getErrorCode(), e.getMessage() );
165 getDavSessionProvider().releaseSession( webdavRequest );
169 public void initServers( ServletConfig servletConfig ) {
171 long start = System.currentTimeMillis();
173 WebApplicationContext wac =
174 WebApplicationContextUtils.getRequiredWebApplicationContext( servletConfig.getServletContext() );
176 rwLock.writeLock().lock();
178 configuration = wac.getBean("archivaConfiguration#default", ArchivaConfiguration.class);
179 configuration.addListener(this);
181 repositoryRegistry = wac.getBean(RepositoryRegistry.class);
182 repositoryMap = new LinkedHashMap<>();
186 for (ManagedRepository repo : repositoryMap.values()) {
187 StorageAsset repoDir = repo.getAsset("");
189 if (!repoDir.exists()) {
192 } catch (IOException e) {
193 log.info("Unable to create missing directory for {}", repo.getLocation());
199 resourceFactory = wac.getBean("davResourceFactory#archiva", DavResourceFactory.class);
200 locatorFactory = new ArchivaDavLocatorFactory();
202 ServletAuthenticator servletAuth = wac.getBean(ServletAuthenticator.class);
203 HttpAuthenticator httpAuth = wac.getBean("httpAuthenticator#basic", HttpAuthenticator.class);
205 sessionProvider = new ArchivaDavSessionProvider(servletAuth, httpAuth);
207 rwLock.writeLock().unlock();
209 long end = System.currentTimeMillis();
211 log.debug( "initServers done in {} ms", (end - start) );
214 private void fillRepositoryMap() {
215 final Map<String, ManagedRepository> repos = repositoryRegistry.getManagedRepositories().stream().collect(Collectors.toMap(r -> r.getId(), r -> r));
216 rwLock.writeLock().lock();
218 repositoryMap.clear();
219 repositoryMap.putAll(repos);
221 rwLock.writeLock().unlock();
226 public void configurationEvent( ConfigurationEvent event )
228 if ( event.getType() == ConfigurationEvent.SAVED )
234 catch ( RepositoryAdminException e )
236 log.error( e.getMessage(), e );
237 throw new RuntimeException( e.getMessage(), e );
242 private void initRepositories()
243 throws RepositoryAdminException
245 initServers( getServletConfig() );
248 public ManagedRepository getRepository( String prefix )
249 throws RepositoryAdminException
251 rwLock.readLock().lock();
253 if (repositoryMap.isEmpty()) {
254 rwLock.readLock().unlock();
256 rwLock.readLock().lock();
258 return repositoryMap.get(prefix);
260 rwLock.readLock().unlock();
264 ArchivaConfiguration getConfiguration()
266 return configuration;
270 protected boolean isPreconditionValid( final WebdavRequest request, final DavResource davResource )
272 // check for read or write access to the resource when resource-based permission is implemented
278 public DavSessionProvider getDavSessionProvider()
280 return sessionProvider;
284 public void setDavSessionProvider( final DavSessionProvider davSessionProvider )
286 this.sessionProvider = davSessionProvider;
290 public DavLocatorFactory getLocatorFactory()
292 return locatorFactory;
296 public void setLocatorFactory( final DavLocatorFactory davLocatorFactory )
298 locatorFactory = davLocatorFactory;
302 public DavResourceFactory getResourceFactory()
304 return resourceFactory;
308 public void setResourceFactory( final DavResourceFactory davResourceFactory )
310 resourceFactory = davResourceFactory;
314 public String getAuthenticateHeaderValue()
316 throw new UnsupportedOperationException();
319 public String getAuthenticateHeaderValue( String repository )
321 return "Basic realm=\"Repository Archiva Managed " + repository + " Repository\"";
325 public void destroy()
327 rwLock.writeLock().lock();
329 configuration.removeListener(this);
331 resourceFactory = null;
332 configuration = null;
333 locatorFactory = null;
334 sessionProvider = null;
335 repositoryMap.clear();
336 repositoryMap = null;
338 WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
340 if (wac instanceof ConfigurableApplicationContext) {
341 ((ConfigurableApplicationContext) wac).close();
345 rwLock.writeLock().unlock();