001 /**
002 * Copyright 2005-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krad.labs.fileUploads;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.apache.commons.lang.exception.ExceptionUtils;
020 import org.kuali.rice.core.api.CoreApiServiceLocator;
021 import org.kuali.rice.core.api.impex.xml.CompositeXmlDocCollection;
022 import org.kuali.rice.core.api.impex.xml.FileXmlDocCollection;
023 import org.kuali.rice.core.api.impex.xml.XmlDoc;
024 import org.kuali.rice.core.api.impex.xml.XmlDocCollection;
025 import org.kuali.rice.core.api.impex.xml.ZipXmlDocCollection;
026 import org.kuali.rice.krad.util.GlobalVariables;
027 import org.kuali.rice.krad.web.controller.UifControllerBase;
028 import org.kuali.rice.krad.web.form.UifFormBase;
029 import org.springframework.stereotype.Controller;
030 import org.springframework.validation.BindingResult;
031 import org.springframework.web.bind.annotation.ModelAttribute;
032 import org.springframework.web.bind.annotation.RequestMapping;
033 import org.springframework.web.bind.annotation.RequestMethod;
034 import org.springframework.web.multipart.MultipartFile;
035 import org.springframework.web.servlet.ModelAndView;
036
037 import javax.servlet.http.HttpServletRequest;
038 import javax.servlet.http.HttpServletResponse;
039 import java.io.File;
040 import java.io.FileOutputStream;
041 import java.io.IOException;
042 import java.util.ArrayList;
043 import java.util.Collection;
044 import java.util.List;
045
046 /**
047 * Controller for the XML Ingester View
048 *
049 * <p>
050 * Displays the initial Ingester view page and processes file upload requests.
051 * </p>
052 *
053 * @author Kuali Rice Team (rice.collab@kuali.org)
054 *
055 */
056 @Controller
057 @RequestMapping(value = "/ingester")
058 public class XmlIngesterController extends UifControllerBase {
059
060 /**
061 * @see org.kuali.rice.krad.web.controller.UifControllerBase#createInitialForm(javax.servlet.http.HttpServletRequest)
062 */
063 @Override
064 protected XmlIngesterForm createInitialForm(HttpServletRequest request) {
065 return new XmlIngesterForm();
066 }
067
068 @Override
069 @RequestMapping(params = "methodToCall=start")
070 public ModelAndView start(@ModelAttribute("KualiForm") UifFormBase form,
071 HttpServletRequest request, HttpServletResponse response) {
072
073 XmlIngesterForm ingesterForm = (XmlIngesterForm)form;
074
075 return super.start(ingesterForm, request, response);
076 }
077
078 @RequestMapping(method = RequestMethod.POST, params = "methodToCall=upload")
079 public ModelAndView upload(@ModelAttribute("KualiForm") XmlIngesterForm ingesterForm, BindingResult result,
080 HttpServletRequest request, HttpServletResponse response) {
081 List<File> tempFiles = new ArrayList<File>();
082 List<XmlDocCollection> collections = copyInputFiles(ingesterForm.getFiles(), tempFiles);
083 try {
084 if (collections.size() == 0) {
085 String message = "No valid files to ingest";
086 GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_NO_VALID_FILES);
087 } else {
088 if (ingestFiles(collections) == 0) {
089 // String message = "No xml docs ingested";
090 GlobalVariables.getMessageMap().putErrorForSectionId(XmlIngesterConstants.INGESTER_SECTION_ID, XmlIngesterConstants.ERROR_INGESTER_NO_XMLS);
091 }
092 }
093 } finally {
094 if (tempFiles.size() > 0) {
095 for (File tempFile : tempFiles)
096 {
097 if (!tempFile.delete())
098 {
099 //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 }