View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.labs.fileUploads;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.lang.exception.ExceptionUtils;
20  import org.kuali.rice.core.api.CoreApiServiceLocator;
21  import org.kuali.rice.core.api.impex.xml.CompositeXmlDocCollection;
22  import org.kuali.rice.core.api.impex.xml.FileXmlDocCollection;
23  import org.kuali.rice.core.api.impex.xml.XmlDoc;
24  import org.kuali.rice.core.api.impex.xml.XmlDocCollection;
25  import org.kuali.rice.core.api.impex.xml.ZipXmlDocCollection;
26  import org.kuali.rice.krad.util.GlobalVariables;
27  import org.kuali.rice.krad.web.controller.UifControllerBase;
28  import org.kuali.rice.krad.web.form.UifFormBase;
29  import org.springframework.stereotype.Controller;
30  import org.springframework.validation.BindingResult;
31  import org.springframework.web.bind.annotation.ModelAttribute;
32  import org.springframework.web.bind.annotation.RequestMapping;
33  import org.springframework.web.bind.annotation.RequestMethod;
34  import org.springframework.web.multipart.MultipartFile;
35  import org.springframework.web.servlet.ModelAndView;
36  
37  import javax.servlet.http.HttpServletRequest;
38  import javax.servlet.http.HttpServletResponse;
39  import java.io.File;
40  import java.io.FileOutputStream;
41  import java.io.IOException;
42  import java.util.ArrayList;
43  import java.util.Collection;
44  import java.util.List;
45  
46  /**
47   * Controller for the XML Ingester View
48   *
49   * <p>
50   *     Displays the initial Ingester view page and processes file upload requests.
51   * </p>
52   *
53   * @author Kuali Rice Team (rice.collab@kuali.org)
54   *
55   */
56  @Controller
57  @RequestMapping(value = "/ingester")
58  public class XmlIngesterController extends UifControllerBase {
59  
60      /**
61       * @see org.kuali.rice.krad.web.controller.UifControllerBase#createInitialForm(javax.servlet.http.HttpServletRequest)
62       */
63      @Override
64      protected XmlIngesterForm createInitialForm(HttpServletRequest request) {
65          return new XmlIngesterForm();
66      }
67  
68      @Override
69      @RequestMapping(params = "methodToCall=start")
70      public ModelAndView start(@ModelAttribute("KualiForm") UifFormBase form, 
71              HttpServletRequest request, HttpServletResponse response) {
72  
73          XmlIngesterForm ingesterForm = (XmlIngesterForm)form;
74  
75          return super.start(ingesterForm, request, response);
76      }
77  
78      @RequestMapping(method = RequestMethod.POST, params = "methodToCall=upload")
79      public ModelAndView upload(@ModelAttribute("KualiForm") XmlIngesterForm ingesterForm, BindingResult result,
80              HttpServletRequest request, HttpServletResponse response) {
81          List<File> tempFiles = new ArrayList<File>();
82          List<XmlDocCollection> collections = copyInputFiles(ingesterForm.getFiles(), tempFiles);
83          try {
84              if (collections.size() == 0) {
85                  String message = "No valid files to ingest";
86                  GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_NO_VALID_FILES);
87              } else {
88                  if (ingestFiles(collections) == 0) {
89                      //	                String message = "No xml docs ingested";
90                      GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_NO_XMLS);
91                  }
92              }
93          } finally {
94              if (tempFiles.size() > 0) {
95                  for (File tempFile : tempFiles)
96                  {
97                      if (!tempFile.delete())
98                      {
99                          //LOG.warn("Error deleting temp file: " + tempFile);
100                     }
101                 }
102             }
103         }
104         return getUIFModelAndView(ingesterForm);
105     }
106 
107     /**
108      * Copies the MultipartFiles into an XmlDocCollection list
109      *
110      * <p>
111      * Reads each of the input files into temporary files to get File reference needed
112      * to create FileXmlDocCollection objects.  Also verifies that only .xml or .zip files are
113      * to be processed.
114      * </p>
115      *
116      * @param fileList list of MultipartFiles selected for ingestion
117      * @param tempFiles temporary files used to get File reference
118      *
119      * @return uploaded files in a List of XmlDocCollections
120      */
121     protected  List<XmlDocCollection> copyInputFiles(List<MultipartFile> fileList, List<File> tempFiles){
122         List<XmlDocCollection> collections = new ArrayList<XmlDocCollection>();
123         for (MultipartFile file : fileList) {
124             if (file == null || StringUtils.isBlank(file.getOriginalFilename())) {
125                 continue;
126             }
127 
128             // Need to copy into temp file get File reference because XmlDocs based on ZipFile
129             // can't be constructed without a file reference.
130             FileOutputStream fos = null;
131             File temp = null;
132             try{
133                 temp = File.createTempFile("ingester", null);
134                 tempFiles.add(temp);
135                 fos = new FileOutputStream(temp);
136                 fos.write(file.getBytes());
137             } catch (IOException ioe) {
138                 GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID,
139                         XmlIngesterConstants.ERROR_INGESTER_COPY_FILE , file.getOriginalFilename(), ExceptionUtils.getFullStackTrace(ioe));
140                 continue;
141             } finally{
142                 if (fos != null) {
143                     try{
144                         fos.close();
145                     } catch (IOException ioe){
146                         //                          LOG.error("Error closing temp file output stream: " + temp, ioe);
147                     }
148                 }
149             }
150 
151             // only .zip and .xml files will be processed
152             if (file.getOriginalFilename().toLowerCase().endsWith(".zip"))
153             {
154                 try {
155                     collections.add(new ZipXmlDocCollection(temp));
156                 } catch (IOException ioe) {
157                     GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_LOAD_FILE, file.getOriginalFilename());
158                 }
159             } else if (file.getOriginalFilename().endsWith(".xml")) {
160                 collections.add(new FileXmlDocCollection(temp, file.getOriginalFilename()));
161             } else {
162                 GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_EXTRANEOUS_FILE, file.getOriginalFilename());
163             }
164         }
165 
166         return collections;
167     }
168 
169     /**
170      * Ingests the list of files into the system
171      *
172      * @param collections xml documents to be ingested
173      * @return the number of files successfully ingested
174      */
175     protected int ingestFiles(List<XmlDocCollection> collections){
176         // wrap in composite collection to make transactional
177         CompositeXmlDocCollection compositeCollection = new CompositeXmlDocCollection(collections);
178         int totalProcessed = 0;
179         List<XmlDocCollection> c = new ArrayList<XmlDocCollection>(1);
180         c.add(compositeCollection);
181         try {
182             // ingest the collection of files
183             Collection<XmlDocCollection> failed = CoreApiServiceLocator.getXmlIngesterService().ingest(c, GlobalVariables.getUserSession().getPrincipalId());
184             boolean txFailed = failed.size() > 0;
185             if (txFailed) {
186                 GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_FAILED);
187             }
188 
189             // loop through the results, collecting the error messages for each doc
190             collectIngestionMessages(collections, txFailed);
191         } catch (Exception e) {
192             GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_DURING_INJECT, ExceptionUtils.getFullStackTrace(e));
193         }
194 
195         return totalProcessed;
196     }
197 
198     /**
199      * loop through the results, returns the number of successfully processed files
200      *
201      * <p>
202      * Also collects the error messages for each doc
203      * </p>
204      *
205      * @param collections the list of processed documents
206      * @param txFailed flag whether upload contained errors
207 
208      * @return the number of files successfully ingested
209      */
210     protected int collectIngestionMessages(List<XmlDocCollection> collections, boolean txFailed){
211         int totalProcessed = 0;
212         for (XmlDocCollection collection1 : collections)
213         {
214             List<? extends XmlDoc> docs = collection1.getXmlDocs();
215             for (XmlDoc doc1 : docs)
216             {
217                 if (doc1.isProcessed())
218                 {
219                     if (!txFailed)
220                     {
221                         totalProcessed++;
222                         GlobalVariables.getMessageMap().putInfoForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.INFO_INGESTER_SUCCESS, doc1.getName(),doc1.getProcessingMessage());
223                     } else {
224                         GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_ROLLEDBACK, doc1.getName(),doc1.getProcessingMessage());
225                     }
226                 } else
227                 {GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_FAILED_XML, doc1.getName(),doc1.getProcessingMessage());
228                 }
229             }
230         }
231         return totalProcessed;
232     }
233 
234     @RequestMapping(method = RequestMethod.POST, params = "methodToCall=close")
235     public ModelAndView close(@ModelAttribute("KualiForm") XmlIngesterForm ingesterForm, BindingResult result,
236             HttpServletRequest request, HttpServletResponse response) {
237 
238         return null;
239     }
240 
241 }