001package org.apache.archiva.webdav; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.admin.model.RepositoryAdminException; 023import org.apache.archiva.configuration.ArchivaConfiguration; 024import org.apache.archiva.configuration.ConfigurationEvent; 025import org.apache.archiva.configuration.ConfigurationListener; 026import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator; 027import org.apache.archiva.repository.base.ArchivaRepositoryRegistry; 028import org.apache.archiva.repository.ManagedRepository; 029import org.apache.archiva.repository.RepositoryRegistry; 030import org.apache.archiva.security.ServletAuthenticator; 031import org.apache.jackrabbit.webdav.DavException; 032import org.apache.jackrabbit.webdav.DavLocatorFactory; 033import org.apache.jackrabbit.webdav.DavMethods; 034import org.apache.jackrabbit.webdav.DavResource; 035import org.apache.jackrabbit.webdav.DavResourceFactory; 036import org.apache.jackrabbit.webdav.DavServletResponse; 037import org.apache.jackrabbit.webdav.DavSessionProvider; 038import org.apache.jackrabbit.webdav.WebdavRequest; 039import org.apache.jackrabbit.webdav.WebdavRequestImpl; 040import org.apache.jackrabbit.webdav.WebdavResponse; 041import org.apache.jackrabbit.webdav.WebdavResponseImpl; 042import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045import org.springframework.context.ConfigurableApplicationContext; 046import org.springframework.web.context.WebApplicationContext; 047import org.springframework.web.context.support.WebApplicationContextUtils; 048 049import javax.servlet.ServletConfig; 050import javax.servlet.ServletException; 051import javax.servlet.http.HttpServletRequest; 052import javax.servlet.http.HttpServletResponse; 053import java.io.IOException; 054import java.util.concurrent.locks.ReentrantReadWriteLock; 055 056/** 057 * RepositoryServlet 058 */ 059public class RepositoryServlet 060 extends AbstractWebdavServlet 061 implements ConfigurationListener 062{ 063 private Logger log = LoggerFactory.getLogger( RepositoryServlet.class ); 064 065 private ArchivaConfiguration configuration; 066 067 RepositoryRegistry repositoryRegistry; 068 069 private DavLocatorFactory locatorFactory; 070 071 private DavResourceFactory resourceFactory; 072 073 private DavSessionProvider sessionProvider; 074 075 protected final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); 076 077 @Override 078 public void init( ServletConfig servletConfig ) 079 throws ServletException 080 { 081 super.init( servletConfig ); 082 initServers( servletConfig ); 083 } 084 085 /** 086 * Service the given request. This method has been overridden and copy/pasted to allow better exception handling and 087 * to support different realms 088 * 089 * @param request 090 * @param response 091 * @throws ServletException 092 * @throws java.io.IOException 093 */ 094 @Override 095 protected void service( HttpServletRequest request, HttpServletResponse response ) 096 throws ServletException, IOException 097 { 098 WebdavRequest webdavRequest = new WebdavRequestImpl( request, getLocatorFactory() ); 099 // DeltaV requires 'Cache-Control' header for all methods except 'VERSION-CONTROL' and 'REPORT'. 100 int methodCode = DavMethods.getMethodCode( request.getMethod() ); 101 boolean noCache = DavMethods.isDeltaVMethod( webdavRequest ) && !( DavMethods.DAV_VERSION_CONTROL == methodCode 102 || DavMethods.DAV_REPORT == methodCode ); 103 WebdavResponse webdavResponse = new WebdavResponseImpl( response, noCache ); 104 DavResource resource = null; 105 106 try 107 { 108 // make sure there is a authenticated user 109 if ( !getDavSessionProvider().attachSession( webdavRequest ) ) 110 { 111 return; 112 } 113 114 // check matching if=header for lock-token relevant operations 115 resource = 116 getResourceFactory().createResource( webdavRequest.getRequestLocator(), webdavRequest, webdavResponse ); 117 118 if ( !isPreconditionValid( webdavRequest, resource ) ) 119 { 120 webdavResponse.sendError( DavServletResponse.SC_PRECONDITION_FAILED ); 121 return; 122 } 123 if ( !execute( webdavRequest, webdavResponse, methodCode, resource ) ) 124 { 125 super.service( request, response ); 126 } 127 128 } 129 catch ( UnauthorizedDavException e ) 130 { 131 webdavResponse.setHeader( "WWW-Authenticate", getAuthenticateHeaderValue( e.getRepositoryName() ) ); 132 webdavResponse.sendError( e.getErrorCode(), e.getStatusPhrase() ); 133 } 134 catch ( BrowserRedirectException e ) 135 { 136 response.sendRedirect( e.getLocation() ); 137 } 138 catch ( DavException e ) 139 { 140 if ( e.getErrorCode() == HttpServletResponse.SC_UNAUTHORIZED ) 141 { 142 final String msg = "Should throw " + UnauthorizedDavException.class.getName(); 143 log.error( msg ); 144 webdavResponse.sendError( e.getErrorCode(), msg ); 145 } 146 else if ( e.getCause() != null ) 147 { 148 webdavResponse.sendError( e.getErrorCode(), e.getCause().getMessage() ); 149 } 150 else 151 { 152 webdavResponse.sendError( e.getErrorCode(), e.getMessage() ); 153 } 154 } 155 finally 156 { 157 getDavSessionProvider().releaseSession( webdavRequest ); 158 } 159 } 160 161 public void initServers( ServletConfig servletConfig ) { 162 163 long start = System.currentTimeMillis(); 164 165 WebApplicationContext wac = 166 WebApplicationContextUtils.getRequiredWebApplicationContext( servletConfig.getServletContext() ); 167 168 rwLock.writeLock().lock(); 169 try { 170 configuration = wac.getBean("archivaConfiguration#default", ArchivaConfiguration.class); 171 configuration.addListener(this); 172 173 repositoryRegistry = wac.getBean( ArchivaRepositoryRegistry.class); 174 resourceFactory = wac.getBean("davResourceFactory#archiva", DavResourceFactory.class); 175 locatorFactory = new ArchivaDavLocatorFactory(); 176 177 ServletAuthenticator servletAuth = wac.getBean(ServletAuthenticator.class); 178 HttpAuthenticator httpAuth = wac.getBean("httpAuthenticator#basic", HttpAuthenticator.class); 179 180 sessionProvider = new ArchivaDavSessionProvider(servletAuth, httpAuth); 181 } finally { 182 rwLock.writeLock().unlock(); 183 } 184 long end = System.currentTimeMillis(); 185 186 log.debug( "initServers done in {} ms", (end - start) ); 187 } 188 189 @Override 190 public void configurationEvent( ConfigurationEvent event ) 191 { 192 if ( event.getType() == ConfigurationEvent.SAVED ) 193 { 194 try 195 { 196 initRepositories(); 197 } 198 catch ( RepositoryAdminException e ) 199 { 200 log.error( e.getMessage(), e ); 201 throw new RuntimeException( e.getMessage(), e ); 202 } 203 } 204 } 205 206 private void initRepositories() 207 throws RepositoryAdminException 208 { 209 initServers( getServletConfig() ); 210 } 211 212 public ManagedRepository getRepository( String prefix ) 213 throws RepositoryAdminException 214 { 215 return repositoryRegistry.getManagedRepository( prefix ); 216 } 217 218 ArchivaConfiguration getConfiguration() 219 { 220 return configuration; 221 } 222 223 @Override 224 protected boolean isPreconditionValid( final WebdavRequest request, final DavResource davResource ) 225 { 226 // check for read or write access to the resource when resource-based permission is implemented 227 228 return true; 229 } 230 231 @Override 232 public DavSessionProvider getDavSessionProvider() 233 { 234 return sessionProvider; 235 } 236 237 @Override 238 public void setDavSessionProvider( final DavSessionProvider davSessionProvider ) 239 { 240 this.sessionProvider = davSessionProvider; 241 } 242 243 @Override 244 public DavLocatorFactory getLocatorFactory() 245 { 246 return locatorFactory; 247 } 248 249 @Override 250 public void setLocatorFactory( final DavLocatorFactory davLocatorFactory ) 251 { 252 locatorFactory = davLocatorFactory; 253 } 254 255 @Override 256 public DavResourceFactory getResourceFactory() 257 { 258 return resourceFactory; 259 } 260 261 @Override 262 public void setResourceFactory( final DavResourceFactory davResourceFactory ) 263 { 264 resourceFactory = davResourceFactory; 265 } 266 267 @Override 268 public String getAuthenticateHeaderValue() 269 { 270 throw new UnsupportedOperationException(); 271 } 272 273 public String getAuthenticateHeaderValue( String repository ) 274 { 275 return "Basic realm=\"Repository Archiva Managed " + repository + " Repository\""; 276 } 277 278 @Override 279 public void destroy() 280 { 281 rwLock.writeLock().lock(); 282 try { 283 configuration.removeListener(this); 284 285 resourceFactory = null; 286 configuration = null; 287 locatorFactory = null; 288 sessionProvider = null; 289 290 WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); 291 292 if (wac instanceof ConfigurableApplicationContext) { 293 ((ConfigurableApplicationContext) wac).close(); 294 } 295 super.destroy(); 296 } finally { 297 rwLock.writeLock().unlock(); 298 } 299 } 300}