View Javadoc
1   /*
2    * Copyright (c) 2002-2020, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.appcenter.modules.fastdeployapplication.uploadhandler;
35  
36  import java.util.ArrayList;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Locale;
40  import java.util.Map;
41  import java.util.concurrent.ConcurrentHashMap;
42  import java.util.regex.Matcher;
43  import java.util.regex.Pattern;
44  
45  import javax.servlet.http.HttpServletRequest;
46  import javax.servlet.http.HttpSession;
47  
48  import org.apache.commons.fileupload.FileItem;
49  import org.apache.commons.lang.StringUtils;
50  
51  import fr.paris.lutece.plugins.asynchronousupload.service.AbstractAsynchronousUploadHandler;
52  import fr.paris.lutece.portal.service.i18n.I18nService;
53  import fr.paris.lutece.portal.service.util.AppException;
54  import fr.paris.lutece.portal.service.util.AppPropertiesService;
55  import fr.paris.lutece.util.filesystem.UploadUtil;
56  
57  public class DeploymentUploadHandler extends AbstractAsynchronousUploadHandler
58  {
59      public static final String BEAN_NAME = "appcenter-fastdeployapplication.deploymentUploadHandler";
60      private static final int DEFAULT_MAX_FILE_SIZE = 2097152;
61      private static final String HANDLER_NAME = "deploymentUploadHandler";
62  
63      // Error messages
64      private static final String ERROR_MESSAGE_MULTIPLE_UPLOAD = "module.appcenter.fastdeployapplication.upload.message.multipleUpload";
65      private static final String ERROR_MESSAGE_UPLOAD_MAX_SIZE = "module.appcenter.fastdeployapplication.upload.message.uploadMaxSize";
66      private static final String ERROR_MESSAGE_NOT_SQL_FILE = "module.appcenter.fastdeployapplication.upload.message.uploadNotSqlFile";
67  
68      // PROPERTY
69      private static final String PROPERTY_UPLOAD_MAX_SIZE = "appcenter.modules.fastdeployapplication.uploadMaxFileSize";
70  
71      /* contains uploaded file items */
72      private static Map<String, Map<String, List<FileItem>>> _mapAsynchronousUpload = new ConcurrentHashMap<String, Map<String, List<FileItem>>>( );
73      private static Map<String, Map<String, List<FileItem>>> _mapPartialContentFileUpload = new ConcurrentHashMap<String, Map<String, List<FileItem>>>( );
74  
75      @Override
76      public void addFileItemToUploadedFilesList( FileItem fileItem, String strFieldName, HttpServletRequest request )
77      {
78          // This is the name that will be displayed in the form. We keep
79          // the original name, but clean it to make it cross-platform.
80          String strFileName = UploadUtil.cleanFileName( fileItem.getName( ).trim( ) );
81  
82          initMap( request.getSession( ).getId( ), strFieldName );
83  
84          // Check if this file has not already been uploaded
85          List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, request.getSession( ) );
86  
87          if ( uploadedFiles != null )
88          {
89              boolean bNew = true;
90  
91              if ( !uploadedFiles.isEmpty( ) )
92              {
93                  Iterator<FileItem> iterUploadedFiles = uploadedFiles.iterator( );
94  
95                  while ( bNew && iterUploadedFiles.hasNext( ) )
96                  {
97                      FileItem uploadedFile = iterUploadedFiles.next( );
98                      String strUploadedFileName = UploadUtil.cleanFileName( uploadedFile.getName( ).trim( ) );
99                      // If we find a file with the same name and the same
100                     // length, we consider that the current file has
101                     // already been uploaded
102                     bNew = !( StringUtils.equals( strUploadedFileName, strFileName ) && ( uploadedFile.getSize( ) == fileItem.getSize( ) ) );
103                 }
104             }
105 
106             if ( bNew )
107             {
108                 uploadedFiles.add( fileItem );
109             }
110         }
111     }
112 
113     @Override
114     public String canUploadFiles( HttpServletRequest request, String strFieldName, List<FileItem> listFileItemsToUpload, Locale locale )
115     {
116         Long nMaxSize = AppPropertiesService.getPropertyLong( PROPERTY_UPLOAD_MAX_SIZE, DEFAULT_MAX_FILE_SIZE );
117 
118         // Check if this file has not already been uploaded
119         List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, request.getSession( ) );
120 
121         if ( uploadedFiles != null && uploadedFiles.size( ) >= 1 )
122         {
123             return I18nService.getLocalizedString( ERROR_MESSAGE_MULTIPLE_UPLOAD, request.getLocale( ) );
124         }
125 
126         for ( FileItem fileItem : listFileItemsToUpload )
127         {
128 
129             if ( fileItem.getName( ) != null && !fileItem.getName( ).endsWith( ".sql" ) )
130             {
131                 return I18nService.getLocalizedString( ERROR_MESSAGE_NOT_SQL_FILE, request.getLocale( ) );
132             }
133 
134             if ( fileItem.getSize( ) > nMaxSize )
135             {
136                 return I18nService.getLocalizedString( ERROR_MESSAGE_UPLOAD_MAX_SIZE, request.getLocale( ) );
137             }
138 
139         }
140 
141         return null;
142 
143     }
144 
145     @Override
146     public String getHandlerName( )
147     {
148         return HANDLER_NAME;
149     }
150 
151     @Override
152     public List<FileItem> getListUploadedFiles( String strFieldName, HttpSession session )
153     {
154         if ( StringUtils.isBlank( strFieldName ) )
155         {
156             throw new AppException( "id field name is not provided for the current file upload" );
157         }
158 
159         initMap( session.getId( ), strFieldName );
160 
161         // find session-related files in the map
162         Map<String, List<FileItem>> mapFileItemsSession = _mapAsynchronousUpload.get( session.getId( ) );
163 
164         return mapFileItemsSession.get( strFieldName );
165     }
166 
167     @Override
168     public void removeFileItem( String strFieldName, HttpSession session, int nIndex )
169     {
170         // Remove the file (this will also delete the file physically)
171         List<FileItem> uploadedFiles = getListUploadedFiles( strFieldName, session );
172 
173         if ( ( uploadedFiles != null ) && !uploadedFiles.isEmpty( ) && ( uploadedFiles.size( ) > nIndex ) )
174         {
175             // Remove the object from the Hashmap
176             FileItem fileItem = uploadedFiles.remove( nIndex );
177             fileItem.delete( );
178         }
179     }
180 
181     /**
182      * Init the map Copy paste from genericAttribute AbstractGenAttUploadHandler from
183      * http://wiki.lutece.paris.fr/lutece/jsp/site/Portal.jsp?page=wiki&page_name=asynchronous_upload&view=page
184      * 
185      * @param strSessionId
186      *            the session id
187      * @param strFieldName
188      *            the field name
189      */
190     private void initMap( String strSessionId, String strFieldName )
191     {
192         // find session-related files in the map
193         Map<String, List<FileItem>> mapFileItemsSession = _mapAsynchronousUpload.get( strSessionId );
194         Map<String, List<FileItem>> mapPartialsContentsItemsSession = _mapPartialContentFileUpload.get( strSessionId );
195 
196         // create map if not exists
197         if ( mapFileItemsSession == null )
198         {
199             synchronized( this )
200             {
201                 // Ignore double check locking error : assignation and instanciation of objects are separated.
202                 mapFileItemsSession = _mapAsynchronousUpload.get( strSessionId );
203 
204                 if ( mapFileItemsSession == null )
205                 {
206                     mapFileItemsSession = new ConcurrentHashMap<String, List<FileItem>>( );
207                     _mapAsynchronousUpload.put( strSessionId, mapFileItemsSession );
208                     mapPartialsContentsItemsSession = new ConcurrentHashMap<String, List<FileItem>>( );
209                     _mapPartialContentFileUpload.put( strSessionId, mapPartialsContentsItemsSession );
210 
211                 }
212             }
213         }
214 
215         List<FileItem> listFileItems = mapFileItemsSession.get( strFieldName );
216 
217         if ( listFileItems == null )
218         {
219             listFileItems = new ArrayList<FileItem>( );
220             mapFileItemsSession.put( strFieldName, listFileItems );
221         }
222 
223         List<FileItem> listPartialContentItems = mapPartialsContentsItemsSession.get( strFieldName );
224 
225         if ( listPartialContentItems == null )
226         {
227             listPartialContentItems = new ArrayList<FileItem>( );
228             mapPartialsContentsItemsSession.put( strFieldName, listPartialContentItems );
229         }
230 
231     }
232 
233     @Override
234     public void addFileItemToPartialUploadedFilesList( FileItem fileItem, String strFieldName, HttpServletRequest request )
235     {
236 
237         initMap( request.getSession( ).getId( ), strFieldName );
238 
239         // Check if this file has not already been uploaded
240         List<FileItem> uploadedPartialFiles = getListPartialUploadedFiles( strFieldName, request.getSession( ) );
241 
242         if ( uploadedPartialFiles != null )
243         {
244             uploadedPartialFiles.add( fileItem );
245 
246         }
247 
248     }
249 
250     @Override
251     public void removeAllFileItem( HttpSession session )
252     {
253         // do not reference anymore file item
254         // the temporary file item will be delete at the next garbage collector
255         _mapAsynchronousUpload.remove( session.getId( ) );
256         _mapPartialContentFileUpload.remove( session.getId( ) );
257 
258     }
259 
260     @Override
261     public boolean isManagePartialContent( )
262     {
263         return true;
264 
265     }
266 
267     @Override
268     public List<FileItem> getListPartialUploadedFiles( String strFieldName, HttpSession session )
269     {
270         if ( StringUtils.isBlank( strFieldName ) )
271         {
272             throw new AppException( "id field name is not provided for the current file upload" );
273         }
274 
275         initMap( session.getId( ), strFieldName );
276 
277         // find session-related files in the map
278         Map<String, List<FileItem>> mapPartialContentFileItemsSession = _mapPartialContentFileUpload.get( session.getId( ) );
279 
280         return mapPartialContentFileItemsSession.get( strFieldName );
281     }
282 
283 }