001    package 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    
022    import org.apache.archiva.admin.model.RepositoryAdminException;
023    import org.apache.archiva.admin.model.beans.ManagedRepository;
024    import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
025    import org.apache.archiva.configuration.ArchivaConfiguration;
026    import org.apache.archiva.configuration.ConfigurationEvent;
027    import org.apache.archiva.configuration.ConfigurationListener;
028    import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator;
029    import org.apache.archiva.security.ServletAuthenticator;
030    import org.apache.jackrabbit.webdav.DavException;
031    import org.apache.jackrabbit.webdav.DavLocatorFactory;
032    import org.apache.jackrabbit.webdav.DavMethods;
033    import org.apache.jackrabbit.webdav.DavResource;
034    import org.apache.jackrabbit.webdav.DavResourceFactory;
035    import org.apache.jackrabbit.webdav.DavServletResponse;
036    import org.apache.jackrabbit.webdav.DavSessionProvider;
037    import org.apache.jackrabbit.webdav.WebdavRequest;
038    import org.apache.jackrabbit.webdav.WebdavRequestImpl;
039    import org.apache.jackrabbit.webdav.WebdavResponse;
040    import org.apache.jackrabbit.webdav.WebdavResponseImpl;
041    import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet;
042    import org.slf4j.Logger;
043    import org.slf4j.LoggerFactory;
044    import org.springframework.context.ConfigurableApplicationContext;
045    import org.springframework.web.context.WebApplicationContext;
046    import org.springframework.web.context.support.WebApplicationContextUtils;
047    
048    import javax.servlet.ServletConfig;
049    import javax.servlet.ServletException;
050    import javax.servlet.http.HttpServletRequest;
051    import javax.servlet.http.HttpServletResponse;
052    import java.io.File;
053    import java.io.IOException;
054    import java.util.Map;
055    
056    /**
057     * RepositoryServlet
058     */
059    public class RepositoryServlet
060        extends AbstractWebdavServlet
061        implements ConfigurationListener
062    {
063        private Logger log = LoggerFactory.getLogger( RepositoryServlet.class );
064    
065        private ArchivaConfiguration configuration;
066    
067        private ManagedRepositoryAdmin managedRepositoryAdmin;
068    
069        private Map<String, ManagedRepository> repositoryMap;
070    
071        private DavLocatorFactory locatorFactory;
072    
073        private DavResourceFactory resourceFactory;
074    
075        private DavSessionProvider sessionProvider;
076    
077        private final Object reloadLock = new Object();
078    
079        public void init( ServletConfig servletConfig )
080            throws ServletException
081        {
082            super.init( servletConfig );
083            try
084            {
085                initServers( servletConfig );
086            }
087            catch ( RepositoryAdminException e )
088            {
089                log.error( e.getMessage(), e );
090                throw new ServletException( e.getMessage(), e );
091            }
092        }
093    
094        /**
095         * Service the given request. This method has been overridden and copy/pasted to allow better exception handling and
096         * to support different realms
097         *
098         * @param request
099         * @param response
100         * @throws ServletException
101         * @throws java.io.IOException
102         */
103        @Override
104        protected void service( HttpServletRequest request, HttpServletResponse response )
105            throws ServletException, IOException
106        {
107            WebdavRequest webdavRequest = new WebdavRequestImpl( request, getLocatorFactory() );
108            // DeltaV requires 'Cache-Control' header for all methods except 'VERSION-CONTROL' and 'REPORT'.
109            int methodCode = DavMethods.getMethodCode( request.getMethod() );
110            boolean noCache = DavMethods.isDeltaVMethod( webdavRequest ) && !( DavMethods.DAV_VERSION_CONTROL == methodCode
111                || DavMethods.DAV_REPORT == methodCode );
112            WebdavResponse webdavResponse = new WebdavResponseImpl( response, noCache );
113            DavResource resource = null;
114    
115            try
116            {
117                // make sure there is a authenticated user
118                if ( !getDavSessionProvider().attachSession( webdavRequest ) )
119                {
120                    return;
121                }
122    
123                // check matching if=header for lock-token relevant operations
124                resource =
125                    getResourceFactory().createResource( webdavRequest.getRequestLocator(), webdavRequest, webdavResponse );
126    
127                if ( !isPreconditionValid( webdavRequest, resource ) )
128                {
129                    webdavResponse.sendError( DavServletResponse.SC_PRECONDITION_FAILED );
130                    return;
131                }
132                if ( !execute( webdavRequest, webdavResponse, methodCode, resource ) )
133                {
134                    super.service( request, response );
135                }
136    
137            }
138            catch ( UnauthorizedDavException e )
139            {
140                webdavResponse.setHeader( "WWW-Authenticate", getAuthenticateHeaderValue( e.getRepositoryName() ) );
141                webdavResponse.sendError( e.getErrorCode(), e.getStatusPhrase() );
142            }
143            catch ( BrowserRedirectException e )
144            {
145                response.sendRedirect( e.getLocation() );
146            }
147            catch ( DavException e )
148            {
149                if ( e.getErrorCode() == HttpServletResponse.SC_UNAUTHORIZED )
150                {
151                    final String msg = "Should throw " + UnauthorizedDavException.class.getName();
152                    log.error( msg );
153                    webdavResponse.sendError( e.getErrorCode(), msg );
154                }
155                else if ( e.getCause() != null )
156                {
157                    webdavResponse.sendError( e.getErrorCode(), e.getCause().getMessage() );
158                }
159                else
160                {
161                    webdavResponse.sendError( e.getErrorCode(), e.getMessage() );
162                }
163            }
164            finally
165            {
166                getDavSessionProvider().releaseSession( webdavRequest );
167            }
168        }
169    
170        public synchronized void initServers( ServletConfig servletConfig )
171            throws RepositoryAdminException
172        {
173    
174            long start = System.currentTimeMillis();
175    
176            WebApplicationContext wac =
177                WebApplicationContextUtils.getRequiredWebApplicationContext( servletConfig.getServletContext() );
178    
179            configuration = wac.getBean( "archivaConfiguration#default", ArchivaConfiguration.class );
180            configuration.addListener( this );
181    
182            managedRepositoryAdmin = wac.getBean( ManagedRepositoryAdmin.class );
183    
184            repositoryMap = managedRepositoryAdmin.getManagedRepositoriesAsMap();
185    
186            for ( ManagedRepository repo : repositoryMap.values() )
187            {
188                File repoDir = new File( repo.getLocation() );
189    
190                if ( !repoDir.exists() )
191                {
192                    if ( !repoDir.mkdirs() )
193                    {
194                        // Skip invalid directories.
195                        log.info( "Unable to create missing directory for {}", repo.getLocation() );
196                        continue;
197                    }
198                }
199            }
200    
201            resourceFactory = wac.getBean( "davResourceFactory#archiva", DavResourceFactory.class );
202            locatorFactory = new ArchivaDavLocatorFactory();
203    
204            ServletAuthenticator servletAuth = wac.getBean( ServletAuthenticator.class );
205            HttpAuthenticator httpAuth = wac.getBean( "httpAuthenticator#basic", HttpAuthenticator.class );
206    
207            sessionProvider = new ArchivaDavSessionProvider( servletAuth, httpAuth );
208    
209            long end = System.currentTimeMillis();
210    
211            log.info( "initServers done in {} ms", (end - start) );
212        }
213    
214        public void configurationEvent( ConfigurationEvent event )
215        {
216            if ( event.getType() == ConfigurationEvent.SAVED )
217            {
218                try
219                {
220                    initRepositories();
221                }
222                catch ( RepositoryAdminException e )
223                {
224                    log.error( e.getMessage(), e );
225                    throw new RuntimeException( e.getMessage(), e );
226                }
227            }
228        }
229    
230        private void initRepositories()
231            throws RepositoryAdminException
232        {
233            synchronized ( repositoryMap )
234            {
235                repositoryMap.clear();
236                repositoryMap.putAll( managedRepositoryAdmin.getManagedRepositoriesAsMap() );
237            }
238    
239            synchronized ( reloadLock )
240            {
241                initServers( getServletConfig() );
242            }
243        }
244    
245        public synchronized ManagedRepository getRepository( String prefix )
246            throws RepositoryAdminException
247        {
248            if ( repositoryMap.isEmpty() )
249            {
250                repositoryMap.putAll( managedRepositoryAdmin.getManagedRepositoriesAsMap() );
251            }
252            return repositoryMap.get( prefix );
253        }
254    
255        ArchivaConfiguration getConfiguration()
256        {
257            return configuration;
258        }
259    
260        protected boolean isPreconditionValid( final WebdavRequest request, final DavResource davResource )
261        {
262            // check for read or write access to the resource when resource-based permission is implemented
263    
264            return true;
265        }
266    
267        public DavSessionProvider getDavSessionProvider()
268        {
269            return sessionProvider;
270        }
271    
272        public void setDavSessionProvider( final DavSessionProvider davSessionProvider )
273        {
274            this.sessionProvider = davSessionProvider;
275        }
276    
277        public DavLocatorFactory getLocatorFactory()
278        {
279            return locatorFactory;
280        }
281    
282        public void setLocatorFactory( final DavLocatorFactory davLocatorFactory )
283        {
284            locatorFactory = davLocatorFactory;
285        }
286    
287        public DavResourceFactory getResourceFactory()
288        {
289            return resourceFactory;
290        }
291    
292        public void setResourceFactory( final DavResourceFactory davResourceFactory )
293        {
294            resourceFactory = davResourceFactory;
295        }
296    
297        public String getAuthenticateHeaderValue()
298        {
299            throw new UnsupportedOperationException();
300        }
301    
302        public String getAuthenticateHeaderValue( String repository )
303        {
304            return "Basic realm=\"Repository Archiva Managed " + repository + " Repository\"";
305        }
306    
307        @Override
308        public void destroy()
309        {
310            configuration.removeListener( this );
311    
312            resourceFactory = null;
313            configuration = null;
314            locatorFactory = null;
315            sessionProvider = null;
316            repositoryMap.clear();
317            repositoryMap = null;
318    
319            WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext( getServletContext() );
320    
321            if ( wac instanceof ConfigurableApplicationContext )
322            {
323                ( (ConfigurableApplicationContext) wac ).close();
324            }
325            super.destroy();
326        }
327    }