001    // Copyright 2007, 2008, 2009 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.upload.services;
016    
017    import org.apache.commons.fileupload.FileItemFactory;
018    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
019    import org.apache.commons.io.FileCleaner;
020    import org.apache.tapestry5.ioc.Configuration;
021    import org.apache.tapestry5.ioc.MappedConfiguration;
022    import org.apache.tapestry5.ioc.OrderedConfiguration;
023    import org.apache.tapestry5.ioc.ScopeConstants;
024    import org.apache.tapestry5.ioc.annotations.Autobuild;
025    import org.apache.tapestry5.ioc.annotations.Inject;
026    import org.apache.tapestry5.ioc.annotations.Scope;
027    import org.apache.tapestry5.ioc.annotations.Symbol;
028    import org.apache.tapestry5.ioc.services.PerthreadManager;
029    import org.apache.tapestry5.ioc.services.RegistryShutdownHub;
030    import org.apache.tapestry5.ioc.services.RegistryShutdownListener;
031    import org.apache.tapestry5.services.ComponentEventRequestFilter;
032    import org.apache.tapestry5.services.HttpServletRequestFilter;
033    import org.apache.tapestry5.services.LibraryMapping;
034    import org.apache.tapestry5.upload.internal.services.MultipartDecoderImpl;
035    import org.apache.tapestry5.upload.internal.services.MultipartServletRequestFilter;
036    import org.apache.tapestry5.upload.internal.services.UploadExceptionFilter;
037    
038    import java.io.File;
039    import java.util.concurrent.atomic.AtomicBoolean;
040    
041    public class UploadModule
042    {
043        private static final String NO_LIMIT = "-1";
044    
045        private static final AtomicBoolean needToAddShutdownListener = new AtomicBoolean(true);
046    
047        public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration)
048        {
049            // Add the component to the "core" library.
050    
051            configuration.add(new LibraryMapping("core", "org.apache.tapestry5.upload"));
052        }
053    
054        @Scope(ScopeConstants.PERTHREAD)
055        public static MultipartDecoder buildMultipartDecoder(PerthreadManager perthreadManager,
056    
057                                                             RegistryShutdownHub shutdownHub,
058    
059                                                             @Autobuild MultipartDecoderImpl multipartDecoder)
060        {
061            // This is proabably overkill since the FileCleaner should catch temporary files, but lets
062            // be safe.
063            perthreadManager.addThreadCleanupListener(multipartDecoder);
064    
065            if (needToAddShutdownListener.getAndSet(false))
066            {
067                shutdownHub.addRegistryShutdownListener(new RegistryShutdownListener()
068                {
069                    public void registryDidShutdown()
070                    {
071                        FileCleaner.exitWhenFinished();
072                    }
073                });
074            }
075    
076            return multipartDecoder;
077        }
078    
079        /**
080         * Contributes a filter, "MultipartFilter" after "IgnoredPaths".
081         */
082        public static void contributeHttpServletRequestHandler(OrderedConfiguration<HttpServletRequestFilter> configuration,
083                                                               MultipartDecoder multipartDecoder)
084        {
085            configuration.add("MultipartFilter", new MultipartServletRequestFilter(multipartDecoder), "after:IgnoredPaths");
086        }
087    
088        /**
089         * Adds UploadException to the pipeline, between Secure and Ajax (both provided by TapestryModule). UploadException
090         * is responsible for triggering the {@linkplain org.apache.tapestry5.upload.services.UploadEvents#UPLOAD_EXCEPTION
091         * upload exception event}.
092         */
093        public static void contributeComponentEventRequestHandler(
094                OrderedConfiguration<ComponentEventRequestFilter> configuration)
095        {
096            configuration.addInstance("UploadException", UploadExceptionFilter.class, "after:Secure",
097                                      "before:Ajax");
098        }
099    
100        /**
101         * The default FileItemFactory used by the MultipartDecoder is {@link org.apache.commons.fileupload.disk.DiskFileItemFactory}.
102         */
103        public static FileItemFactory buildDefaultFileItemFactory(
104                @Symbol(UploadSymbols.REPOSITORY_THRESHOLD)
105                int repositoryThreshold,
106    
107                @Inject @Symbol(UploadSymbols.REPOSITORY_LOCATION)
108                String repositoryLocation)
109        {
110            return new DiskFileItemFactory(repositoryThreshold, new File(repositoryLocation));
111        }
112    
113        public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
114        {
115            configuration.add(UploadSymbols.REPOSITORY_THRESHOLD, Integer
116                    .toString(DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD));
117            configuration.add(UploadSymbols.REPOSITORY_LOCATION, System.getProperty("java.io.tmpdir"));
118            configuration.add(UploadSymbols.REQUESTSIZE_MAX, NO_LIMIT);
119            configuration.add(UploadSymbols.FILESIZE_MAX, NO_LIMIT);
120        }
121    }