View Javadoc

1   /* ===================================================================
2    * Copyright 2002-04 SRI International.
3    * Released under the MOZILLA PUBLIC LICENSE Version 1.1
4    * which can be obtained at http://www.mozilla.org/MPL/MPL-1.1.html
5    * This software is distributed on an "AS IS"
6    * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
7    * See the License for the specific language governing rights and
8    * limitations under the License.
9    * =================================================================== */
10  
11  package com.sri.emo.controller;
12  
13  import com.jcorporate.expresso.core.controller.*;
14  import com.jcorporate.expresso.core.db.DBException;
15  import com.jcorporate.expresso.core.dbobj.ValidValue;
16  import com.jcorporate.expresso.core.registry.RequestRegistry;
17  import com.sri.emo.dbobj.NodeType;
18  import org.apache.log4j.Logger;
19  import org.dom4j.Document;
20  import org.dom4j.DocumentException;
21  import org.dom4j.Element;
22  import org.dom4j.io.SAXReader;
23  
24  import java.io.*;
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  
30  /***
31   * Allows import and export of Entities and their related nodes.
32   * <p> A controller is a rough model of a finite state machine for a web application.
33   * Each state provides user interaction elements that can be rendered in a web
34   * page or other medium such as a command line app.</p>
35   *
36   * @todo Transactionize the import.
37   */
38  public class ImportExportModel extends com.sri.common.controller.
39          AbstractDBController {
40  
41      /***
42  	 * 
43  	 */
44  	private static final long serialVersionUID = 1L;
45  
46  	/***
47       * The log4j logger instance for this class type.  Send all logging output
48       * to this variable
49       */
50      private static final transient Logger log = Logger.getLogger(ImportExportModel.class);
51  
52      /***
53       * Constant for access to state: Prompt
54       */
55      public static final String STATE_PROMPT = "prompt";
56  
57  
58      /***
59       * Constant for access to state: Prompt Export Model
60       */
61      public static final String STATE_PROMPT_EXPORT = "promptExportModel";
62  
63      /***
64       * Constant for access to state: Export an Entire Model with Nodes
65       */
66      public static final String STATE_EXPORTMODEL = "exportModel";
67  
68      /***
69       * Constant for access to state: Import an Entire Model With Nodes
70       */
71      public static final String STATE_IMPORTMODEL = "importModel";
72  
73  
74      /***
75       * Constant for access to state: Prompt Import
76       */
77      public static final String STATE_PROMPT_IMPORTMODEL = "promptImportModel";
78  
79  
80      /***
81       * Creates an instance of ImportExportModel.  The Controller constructor
82       * also defines what states and parameters are officially defined by the
83       * Controller.
84       */
85      public ImportExportModel() {
86          super();
87          State s;
88  
89          s = new State(STATE_PROMPT, "Prompt");
90          addState(s);
91  
92          s = new State(STATE_PROMPT_EXPORT, "Prompt for Export");
93          addState(s);
94  
95          s = new State(STATE_EXPORTMODEL, "Export an Entire Model with Nodes");
96          addState(s);
97  
98          s = new State(STATE_IMPORTMODEL, "Import an Entire Model With Nodes");
99          addState(s);
100 
101         s = new State(STATE_PROMPT_IMPORTMODEL, "Prompt for Import");
102         addState(s);
103 
104         setInitialState("prompt");
105 
106     }
107 
108 
109     /***
110      * Runs the Prompt state.
111      *
112      * @param request  The <code>ExpressoRequest</code> object handed to us
113      *                 by the framework.
114      *                 The ExpressoRequest contains all the parameters such as web
115      *                 parameters, user
116      *                 security credentials and user locale
117      * @param response The <code>ExpressoResponse</code> object handed to us
118      *                 by the framework.
119      *                 The ExpressoResponse will be populated with the Inputs/Outputs
120      *                 /Transitions/Blocks
121      *                 for ths sytem.
122      * @throws ControllerException    upon error
123      * @throws NonHandleableException upon fatal error
124      */
125     protected void runPromptState(ExpressoRequest request,
126                                   ExpressoResponse response) throws
127             ControllerException,
128             NonHandleableException {
129         Input i = new Input("fauxstate", "Choose Action");
130 
131         List vv = new ArrayList(3);
132         vv.add(new ValidValue("", "choose action..."));
133         vv.add(new ValidValue(STATE_PROMPT_IMPORTMODEL, "import model"));
134         vv.add(new ValidValue(STATE_PROMPT_EXPORT, "export model"));
135         i.setValidValues(vv);
136         i.setDefaultValue("");
137         response.add(i);
138 
139         //
140         //Important, the underlying JSP must NOT use 'submitbuttonparams'
141         //tag so that the state isn't mucked with.
142         //
143         Transition t = new Transition("submit", "Perform Action",
144                 getClass(), STATE_PROMPT_IMPORTMODEL);
145 
146         response.add(t);
147     }
148 
149     /***
150      * Runs the Prompt Export.
151      *
152      * @param request  The <code>ExpressoRequest</code> object handed to us by
153      *                 the framework.
154      *                 The ExpressoRequest contains all the parameters such as web
155      *                 parameters, user
156      *                 security credentials and user locale
157      * @param response The <code>ExpressoResponse</code> object handed to us
158      *                 by the framework.
159      *                 The ExpressoResponse will be populated with the Inputs/Outputs
160      *                 /Transitions/Blocks
161      *                 for ths sytem.
162      * @throws ControllerException    upon error
163      * @throws NonHandleableException upon fatal error
164      */
165     protected void runPromptExportModelState(final ExpressoRequest request,
166                                              final ExpressoResponse response) throws ControllerException,
167             NonHandleableException {
168         try {
169             Input i = new Input("model", "Choose Model To Export");
170             NodeType nt[] = NodeType.getAllTypes();
171             List validValues = new ArrayList(nt.length + 1);
172             validValues.add(new ValidValue("", "choose a model to export..."));
173             for (int j = 0; j < nt.length; j++) {
174                 validValues.add(new ValidValue(nt[j].getId(), nt[j].getDisplayName()));
175             }
176             i.setValidValues(validValues);
177             i.setDefaultValue(response);
178             response.add(i);
179 
180             i = new Input("exportNodes", "Export Model Nodes As Well?");
181             i.setAttribute("checkbox", "Y");
182             i.setDefaultValue(response);
183 
184             response.add(i);
185 
186             Transition t = new Transition("export", "Export", getClass(),
187                     STATE_EXPORTMODEL);
188             response.add(t);
189 
190         } catch (DBException ex) {
191             throw new ControllerException("Error Accessing Database", ex);
192         }
193 
194     }
195 
196     /***
197      * Runs the Export an Entire Model with Nodes state
198      *
199      * @param request  The <code>ExpressoRequest</code> object handed to us
200      *                 by the framework.
201      *                 The ExpressoRequest contains all the parameters such as web
202      *                 parameters, user
203      *                 security credentials and user locale
204      * @param response The <code>ExpressoResponse</code> object handed to us
205      *                 by the framework.
206      *                 The ExpressoResponse will be populated with the Inputs/Outputs
207      *                 Transitions/Blocks
208      *                 for ths sytem.
209      * @throws ControllerException    upon error
210      * @throws NonHandleableException upon fatal error
211      * @throws IOException            upon error writing the XML data.
212      */
213     protected void runExportModelState(ExpressoRequest request,
214                                        ExpressoResponse response) throws
215             ControllerException,
216             NonHandleableException, IOException {
217         ErrorCollection ec = request.getErrorCollection();
218         if (ec == null) {
219             ec = new ErrorCollection();
220         }
221 
222         String modelString = request.getParameter("model");
223         if (modelString == null || modelString.length() == 0) {
224             ec.addError("You need to choose a model");
225         }
226 
227         try {
228             NodeType model = new NodeType(RequestRegistry.getUser());
229             model.setField(NodeType.NODE_TYPE_ID, modelString);
230             model.retrieve();
231 
232             if (ec.getErrorCount() > 0) {
233                 if (log.isDebugEnabled()) {
234                     log.debug("Errors in error collection: "
235                             + ec.getErrorCount()
236                             + " errors.");
237                 }
238                 response.saveErrors(ec);
239                 response.setFormCache();
240                 transition(STATE_PROMPT_EXPORT, request, response);
241             }
242 
243             ModelXMLWriter writer = new ModelXMLWriter();
244             Document d = writer.renderEntityAsXml(model, request, true);
245             response.setCustomResponse(true);
246             outputXML(d, request);
247         } catch (DBException ex) {
248             throw new ControllerException("Error Accessing Database", ex);
249         }
250     }
251 
252     /***
253      * Runs the Import an Entire Model With Nodes state
254      *
255      * @param request  The <code>ExpressoRequest</code> object
256      *                 handed to us by the framework.
257      *                 The ExpressoRequest contains all the parameters such as
258      *                 web parameters, user
259      *                 security credentials and user locale
260      * @param response The <code>ExpressoResponse</code> object handed
261      *                 to us by the framework.
262      *                 The ExpressoResponse will be populated with the
263      *                 Inputs/Outputs/Transitions/Blocks
264      *                 for ths sytem.
265      * @throws ControllerException    upon error
266      * @throws NonHandleableException upon fatal error
267      */
268     protected void runImportModelState(final ExpressoRequest request,
269                                        final ExpressoResponse response) throws ControllerException,
270             NonHandleableException {
271         ErrorCollection ec = request.getErrorCollection();
272         if (ec == null) {
273             ec = new ErrorCollection();
274         }
275 
276         String importNodeString = request.getParameter("importNode");
277         boolean importNode;
278         if (importNodeString != null && importNodeString.equals("Y")) {
279             importNode = true;
280         } else {
281             importNode = false;
282         }
283 
284         String data = getXmlData(request, ec);
285         Element docRoot = null;
286         if (data != null && ec.getErrorCount() == 0) {
287             try {
288                 docRoot = parseXmlDataString(data);
289             } catch (DocumentException ex) {
290                 log.error("Error parsing XML data", ex);
291                 ec.addError("Error Parsing XML Data: " + ex.toString());
292             }
293 
294         }
295 
296         if (ec.getErrorCount() > 0) {
297             response.saveErrors(ec);
298             response.setFormCache();
299             transition(STATE_PROMPT_IMPORTMODEL, request, response);
300             return;
301         }
302 
303         assert data != null;
304         assert data.length() > 0;
305         assert ec.getErrorCount() == 0;
306         assert docRoot != null;
307 
308         //Now begins the actual import model.
309         try {
310             importXmlDocument(request, response, importNode, docRoot);
311 
312         } catch (DBException ex1) {
313             throw new ControllerException("Database Error Importing Data", ex1);
314         } catch (Exception ex1) {
315             throw new ControllerException("Other Error Importing Data", ex1);
316         }
317     }
318 
319     private void importXmlDocument(ExpressoRequest request,
320                                    ExpressoResponse response,
321                                    boolean importNode,
322                                    Element docRoot) throws ControllerException, DBException, NumberFormatException,
323             Exception {
324 
325         ModelXMLReader reader = new ModelXMLReader();
326         NodeType nt = reader.parseEntityAndParts(docRoot);
327         response.add(new Output("Imported Entity: " + nt.getId()));
328         if (importNode) {
329             List allItems = docRoot.selectNodes("//" + ModelXMLWriter.DATA_NODES + "/*");
330 
331             for (Iterator i = allItems.iterator(); i.hasNext();) {
332                 Element oneElement = (Element) i.next();
333                 String subselect = "//Entity/" + ModelXMLWriter.DATA_NODES + "/"
334                         + oneElement.getQualifiedName();
335 
336                 Map createdNodes = reader.parseImportXmlAndCreateNodes(false, false, oneElement, request, subselect);
337 
338                 for (Iterator j = createdNodes.keySet().iterator(); j.hasNext();) {
339                     String oneKey = (String) j.next();
340                     response.add(new Output("Added Node: " + oneKey));
341                 }
342             }
343 
344         }
345     }
346 
347     /***
348      * Parse the XML data from either the data parameter or the file parameter.
349      *
350      * @param request ExpressoRequest The ExpressoRequest object
351      * @param ec      ErrorCollection the ErrorCollection
352      * @return String the resulting data.
353      */
354     protected String getXmlData(ExpressoRequest request, ErrorCollection ec) {
355         String data = request.getParameter("data");
356         String radioValue = request.getParameter("radiobutton");
357 
358         if (data != null && data.length() > 0) {
359             String file = request.getParameter("file");
360             if (file != null && file.length() > 0 && "uploadFile".equals(radioValue)) {
361                 //Now we go by the radio button because both fields had data.
362                 data = getXmlDataFromFile(request, ec);
363             }
364         } else {
365             String file = request.getParameter("file");
366             if (file == null || file.length() == 0) {
367                 ec.addError("You should define either manual data or a file to upload");
368             } else {
369                 data = getXmlDataFromFile(request, ec);
370             }
371         }
372         return data;
373     }
374 
375     /***
376      * Returns an element from the given data.
377      *
378      * @param data String the actual data to parse.
379      * @return Element Dom4j Element
380      * @throws DocumentException upon parsing error.
381      */
382     protected Element parseXmlDataString(String data) throws DocumentException {
383         SAXReader xmlReader = new SAXReader();
384         Document doc = xmlReader.read(new StringReader(data));
385         return doc.getRootElement();
386     }
387 
388     /***
389      * Retrieve a String of the XML data.
390      *
391      * @param request ExpressoRequest
392      * @param ec      ErrorCollection the ErrorCollection that we populate if
393      *                things go awry.
394      * @return String
395      */
396     private String getXmlDataFromFile(ExpressoRequest request,
397                                       ErrorCollection ec) {
398         String fileName = request.getFileName("file");
399         if (fileName == null) {
400             ec.addError("There was an error uploading your file.  Please enter the"
401                     + " data manually");
402         }
403 
404         String data = "";
405         try {
406             File f = new File(fileName);
407             if (f == null) {
408                 ec.addError("There was an error uploading your file.  "
409                         + "Please enter the data manually");
410             } else {
411                 //Read the whole puppy in one shot.
412                 FileReader reader = new FileReader(f);
413                 char buffer[] = new char[(int) f.length()];
414                 reader.read(buffer);
415                 data = new String(buffer);
416                 if (data.length() == 0) {
417                     ec.addError("Read a zero length file");
418                 }
419             }
420         } catch (FileNotFoundException ex) {
421             log.error("Error parsing file", ex);
422             ec.addError("Error reading file. The error has been logged, please"
423                     + " enter the data manually");
424         } catch (IOException ex) {
425             log.error("Error reading file", ex);
426             ec.addError("Error reading file. The error has been logged, please enter"
427                     + " the data manually");
428         }
429         return data;
430     }
431 
432     /***
433      * Prompts for input.
434      *
435      * @param request  The <code>ExpressoRequest</code> object handed
436      *                 to us by the framework.
437      *                 The ExpressoRequest contains all the parameters such as web
438      *                 parameters, user
439      *                 security credentials and user locale
440      * @param response The <code>ExpressoResponse</code> object handed to us
441      *                 by the framework.
442      *                 The ExpressoResponse will be populated with the Inputs/Outputs
443      *                 /Transitions/Blocks for ths sytem.
444      * @throws ControllerException    upon error
445      * @throws NonHandleableException upon fatal error
446      */
447     protected void runPromptImportModelState(ExpressoRequest request,
448                                              ExpressoResponse response) throws ControllerException,
449             NonHandleableException {
450 
451         assert request != null;
452         assert response != null;
453 
454         Input i = new Input("data", "XML Data To Import");
455         i.setDefaultValue(response);
456         response.add(i);
457 
458         i = new Input("importNode", "Import XML Nodes As Well?");
459         i.setAttribute("checkbox", "Y");
460         i.setDefaultValue(response);
461         response.add(i);
462 
463         i = new Input("file", "File to Upload for Import");
464         i.setDefaultValue(response);
465         response.add(i);
466 
467         Transition t = new Transition("import", "import", getClass(),
468                 STATE_IMPORTMODEL);
469         response.add(t);
470 
471     }
472 
473 
474     /***
475      * Override of Controller.getTitle() to provide a meaningful name to this
476      * controller.
477      *
478      * @return java.lang.String
479      */
480     public String getTitle() {
481         return "Import/Export Models";
482     }
483 }