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.edl.impl.service.impl;
17  
18  import org.apache.log4j.Logger;
19  import org.kuali.rice.core.api.config.property.ConfigContext;
20  import org.kuali.rice.core.api.impex.ExportDataSet;
21  import org.kuali.rice.core.api.impex.xml.XmlIngestionException;
22  import org.kuali.rice.coreservice.api.style.StyleService;
23  import org.kuali.rice.core.api.util.xml.XmlException;
24  import org.kuali.rice.core.api.util.xml.XmlJotter;
25  import org.kuali.rice.edl.impl.EDLController;
26  import org.kuali.rice.edl.impl.EDLControllerFactory;
27  import org.kuali.rice.edl.impl.EDLGlobalConfig;
28  import org.kuali.rice.edl.impl.EDLGlobalConfigFactory;
29  import org.kuali.rice.edl.impl.EDLXmlUtils;
30  import org.kuali.rice.edl.impl.bo.EDocLiteAssociation;
31  import org.kuali.rice.edl.impl.bo.EDocLiteDefinition;
32  import org.kuali.rice.edl.impl.dao.EDocLiteDAO;
33  import org.kuali.rice.edl.impl.service.EDocLiteService;
34  import org.kuali.rice.edl.impl.xml.EDocLiteXmlParser;
35  import org.kuali.rice.edl.impl.xml.export.EDocLiteXmlExporter;
36  import org.kuali.rice.kew.api.WorkflowRuntimeException;
37  import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
38  import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
39  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
40  import org.kuali.rice.kew.rule.bo.RuleAttribute;
41  import org.kuali.rice.kew.service.KEWServiceLocator;
42  import org.kuali.rice.kew.api.KewApiConstants;
43  import org.w3c.dom.Document;
44  import org.w3c.dom.Element;
45  import org.w3c.dom.Node;
46  import org.w3c.dom.NodeList;
47  import org.xml.sax.InputSource;
48  
49  import javax.xml.parsers.DocumentBuilderFactory;
50  import javax.xml.transform.Templates;
51  import javax.xml.transform.TransformerConfigurationException;
52  import javax.xml.xpath.XPath;
53  import javax.xml.xpath.XPathConstants;
54  import javax.xml.xpath.XPathExpressionException;
55  import javax.xml.xpath.XPathFactory;
56  import java.io.InputStream;
57  import java.io.StringReader;
58  import java.util.ArrayList;
59  import java.util.Collection;
60  import java.util.Iterator;
61  import java.util.List;
62  import java.util.concurrent.atomic.AtomicReference;
63  
64  /**
65   * DAO-based EDocLiteService implementation
66   *
67   * @author Kuali Rice Team (rice.collab@kuali.org)
68   */
69  public class EDocLiteServiceImpl implements EDocLiteService {
70      private static final Logger LOG = Logger.getLogger(EDocLiteServiceImpl.class);
71  
72  	private final AtomicReference<EDLGlobalConfig> edlGlobalConfig = new AtomicReference<EDLGlobalConfig>(null);
73      /**
74       * The Spring-wired DAO bean
75       */
76      private EDocLiteDAO dao;
77      /**
78       * Spring wired StyleService bean
79       */
80      private StyleService styleService;
81  
82      // ---- Spring DAO setters
83  
84      public void setEDocLiteDAO(EDocLiteDAO dao) {
85          this.dao = dao;
86      }
87  
88      public EDLController getEDLControllerUsingEdlName(String edlName) {
89  		EDocLiteAssociation edlAssociation = this.dao.getEDocLiteAssociation(edlName);
90          if (edlAssociation == null) {
91              throw new WorkflowRuntimeException("No document association active for EDL: " + edlName);
92          }
93  		initEDLGlobalConfig();
94  		return EDLControllerFactory.createEDLController(edlAssociation, edlGlobalConfig.get());
95  	}
96  
97  	public EDLController getEDLControllerUsingDocumentId(String documentId) {
98  		DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
99  		String edlName = document.getAppDocId();//components working with workflow docs will need to know this, perhaps through a document utils.
100 		if (edlName == null) {
101 			edlName = document.getDocumentType().getName();
102 		}
103 		EDocLiteAssociation edlAssociation = this.dao.getEDocLiteAssociation(edlName);
104         if (edlAssociation == null) {
105             throw new WorkflowRuntimeException("No document association active for EDL: " + edlName);
106         }
107         initEDLGlobalConfig();
108 		return EDLControllerFactory.createEDLController(edlAssociation, edlGlobalConfig.get(), document);
109 	}
110 
111     @Override
112     public void initEDLGlobalConfig() {
113         edlGlobalConfig.compareAndSet(null, getEDLGlobalConfig());
114     }
115 
116 	private EDLGlobalConfig getEDLGlobalConfig() {
117 		try {
118 			return EDLGlobalConfigFactory.createEDLGlobalConfig(ConfigContext.getCurrentContextConfig().getEDLConfigLocation());
119 		} catch (Exception e) {
120 			throw new WorkflowRuntimeException(e);
121 		}
122 	}
123 
124 	public Document getDefinitionXml(EDocLiteAssociation edlAssociation) {
125 		try {
126 			Document def = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(
127 			new StringReader(getEDocLiteDefinition(edlAssociation.getDefinition()).getXmlContent())));
128 			return def;
129 		} catch (Exception e) {
130 			throw new WorkflowRuntimeException("Caught exception parsing EDL definition " + edlAssociation.getDefinition(), e);
131 		}
132 	}
133 
134 	private static XmlIngestionException generateException(String error, Throwable cause) {
135         return new XmlIngestionException(error, cause);
136     }
137 
138     private static XmlIngestionException generateMissingAttribException(String element, String attrib) {
139         return generateException("EDocLite '" + element + "' element must contain a '" + attrib + "' attribute", null);
140     }
141 
142     private static XmlIngestionException generateMissingChildException(String element, String child) {
143         return generateException("EDocLite '" + element + "' element must contain a '" + child + "' child element", null);
144     }
145 
146     private static XmlIngestionException generateSerializationException(String element, XmlException cause) {
147         return generateException("Error serializing EDocLite '" + element + "' element", cause);
148     }
149 
150     /**
151      * Parses an arbitrary XML stream
152      *
153      * @param stream
154      *            stream containing XML doc content
155      * @return parsed Document object
156      */
157     private static Document parse(InputStream stream) {
158         try {
159             return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream);
160         } catch (Exception e) {
161             WorkflowServiceErrorException wsee = new WorkflowServiceErrorException("Error parsing EDocLite XML file", new WorkflowServiceErrorImpl("Error parsing XML file.", KewApiConstants.XML_FILE_PARSE_ERROR));
162             wsee.initCause(e);
163             throw wsee;
164         }
165     }
166 
167     /**
168      * Parses an EDocLiteAssocation
169      *
170      * @param e
171      *            element to parse
172      * @return an EDocLiteAssocation
173      */
174     private static EDocLiteAssociation parseEDocLiteAssociation(Element e) {
175         String docType = EDLXmlUtils.getChildElementTextValue(e, "docType");
176         if (docType == null) {
177             throw generateMissingChildException("association", "docType");
178         }
179         EDocLiteAssociation assoc = new EDocLiteAssociation();
180         assoc.setEdlName(docType);
181         assoc.setDefinition(EDLXmlUtils.getChildElementTextValue(e, "definition"));
182         assoc.setStyle(EDLXmlUtils.getChildElementTextValue(e, "style"));
183         assoc.setActiveInd(Boolean.valueOf(EDLXmlUtils.getChildElementTextValue(e, "active")));
184         return assoc;
185     }
186 
187     /**
188      * Parses an EDocLiteDefinition
189      *
190      * @param e
191      *            element to parse
192      * @return an EDocLiteDefinition
193      */
194     private static EDocLiteDefinition parseEDocLiteDefinition(Element e) {
195         EDocLiteDefinition def = new EDocLiteDefinition();
196         String name = e.getAttribute("name");
197         if (name == null || name.length() == 0) {
198             throw generateMissingAttribException(EDLXmlUtils.EDL_E, "name");
199         }
200         def.setName(name);
201 
202         // do some validation to ensure that any attributes referenced actually exist
203         // blow up if there is a problem
204 
205         XPath xpath = XPathFactory.newInstance().newXPath();
206         NodeList fields;
207         try {
208             fields = (NodeList) xpath.evaluate("fieldDef", e, XPathConstants.NODESET);
209         } catch (XPathExpressionException xpee) {
210             throw new RuntimeException("Invalid EDocLiteDefinition", xpee);
211         }
212 
213         if (fields != null) {
214             Collection invalidAttributes = new ArrayList(5);
215             for (int i = 0; i < fields.getLength(); i++) {
216                 Node node = (Node) fields.item(i);
217                 // they should all be Element...
218                 if (node instanceof Element) {
219                     Element field = (Element) node;
220                     // rely on XML validation to ensure this is present
221                     String fieldName = field.getAttribute("name");
222                     String attribute = field.getAttribute("attributeName");
223                     if (attribute != null && attribute.length() > 0) {
224                         RuleAttribute ruleAttrib = KEWServiceLocator.getRuleAttributeService().findByName(attribute);
225                         if (ruleAttrib == null) {
226                             LOG.error("Invalid attribute referenced in EDocLite definition: " + attribute);
227                             invalidAttributes.add("Attribute '" + attribute + "' referenced in field '" + fieldName + "' not found");
228                         }
229                     }
230                 }
231             }
232             if (invalidAttributes.size() > 0) {
233                 LOG.error("Invalid attributes referenced in EDocLite definition");
234                 StringBuffer message = new StringBuffer("EDocLite definition contains references to non-existent attributes;\n");
235                 Iterator it = invalidAttributes.iterator();
236                 while (it.hasNext()) {
237                     message.append(it.next());
238                     message.append("\n");
239                 }
240                 throw new RuntimeException(message.toString());
241             }
242         }
243 
244         try {
245             def.setXmlContent(XmlJotter.jotNode(e, true));
246         } catch (XmlException te) {
247             throw generateSerializationException(EDLXmlUtils.EDL_E, te);
248         }
249         return def;
250     }
251 
252     // ---- helper methods
253 
254     public void saveEDocLiteDefinition(EDocLiteDefinition data) {
255         EDocLiteDefinition existingData = getEDocLiteDefinition(data.getName());
256         if (existingData != null) {
257             existingData.setActiveInd(Boolean.FALSE);
258             dao.saveEDocLiteDefinition(existingData);
259         }
260         // if not specified (from xml), mark it as active
261         if (data.getActiveInd() == null) {
262             data.setActiveInd(Boolean.TRUE);
263         }
264         dao.saveEDocLiteDefinition(data);
265     }
266 
267     public void saveEDocLiteAssociation(EDocLiteAssociation assoc) {
268         EDocLiteAssociation existingData = getEDocLiteAssociation(assoc.getEdlName());
269         if (existingData != null) {
270             existingData.setActiveInd(Boolean.FALSE);
271             dao.saveEDocLiteAssociation(existingData);
272         }
273         // if not specified (from xml), mark it as active
274         if (assoc.getActiveInd() == null) {
275             assoc.setActiveInd(Boolean.TRUE);
276         }
277         dao.saveEDocLiteAssociation(assoc);
278     }
279 
280     // ---- EDocLiteService interface implementation
281 
282     public void saveEDocLiteDefinition(InputStream xml) {
283         // convert xml to EDocLiteDefinition
284         EDocLiteDefinition data = parseEDocLiteDefinition(parse(xml).getDocumentElement());
285         saveEDocLiteDefinition(data);
286     }
287 
288     public void saveEDocLiteAssociation(InputStream xml) {
289         // convert xml to EDocLiteAssociation
290         EDocLiteAssociation assoc = parseEDocLiteAssociation(parse(xml).getDocumentElement());
291         saveEDocLiteAssociation(assoc);
292     }
293 
294     public EDocLiteDefinition getEDocLiteDefinition(String definitionName) {
295         return dao.getEDocLiteDefinition(definitionName);
296     }
297 
298     public EDocLiteAssociation getEDocLiteAssociation(String docTypeName) {
299         return dao.getEDocLiteAssociation(docTypeName);
300     }
301 
302     public List getEDocLiteDefinitions() {
303         return dao.getEDocLiteDefinitions();
304     }
305 
306     public List getEDocLiteAssociations() {
307         return dao.getEDocLiteAssociations();
308     }
309 
310     public Templates getStyleAsTranslet(String name) throws TransformerConfigurationException {
311         if (name == null || "null".equals(name)) { //"name".equals(name) - from a null value in the lookupable
312             name = "Default";
313         }
314 
315         return styleService.getStyleAsTranslet(name);
316     }
317 
318     public List search(EDocLiteAssociation edocLite) {
319         return this.dao.search(edocLite);
320     }
321 
322     public EDocLiteAssociation getEDocLiteAssociation(Long associationId) {
323         return dao.getEDocLiteAssociation(associationId);
324     }
325 
326     // ---- XmlLoader interface implementation
327 
328     public void loadXml(InputStream inputStream, String principalId) {
329     	EDocLiteXmlParser.loadXml(inputStream, principalId);
330     }
331 
332     // ---- XmlExporter interface implementation
333 	public org.jdom.Element export(ExportDataSet dataSet) {
334 		return new EDocLiteXmlExporter().export(dataSet);
335 	}
336 
337 	@Override
338 	public boolean supportPrettyPrint() {
339 		return false;
340 	}
341 	
342 	public void setStyleService(StyleService styleService) {
343 		this.styleService = styleService;
344 	}
345 	
346 }