001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package edu.iu.uis.eden.edl;
018:
019: import java.io.InputStream;
020: import java.io.StringReader;
021: import java.util.ArrayList;
022: import java.util.Collection;
023: import java.util.Iterator;
024: import java.util.List;
025:
026: import javax.xml.parsers.DocumentBuilderFactory;
027: import javax.xml.transform.Templates;
028: import javax.xml.transform.TransformerConfigurationException;
029: import javax.xml.transform.TransformerException;
030: import javax.xml.xpath.XPath;
031: import javax.xml.xpath.XPathConstants;
032: import javax.xml.xpath.XPathExpressionException;
033: import javax.xml.xpath.XPathFactory;
034:
035: import org.apache.log4j.Logger;
036: import org.kuali.rice.core.Core;
037: import org.w3c.dom.Document;
038: import org.w3c.dom.Element;
039: import org.w3c.dom.Node;
040: import org.w3c.dom.NodeList;
041: import org.xml.sax.InputSource;
042:
043: import edu.iu.uis.eden.EdenConstants;
044: import edu.iu.uis.eden.KEWServiceLocator;
045: import edu.iu.uis.eden.WorkflowServiceErrorException;
046: import edu.iu.uis.eden.WorkflowServiceErrorImpl;
047: import edu.iu.uis.eden.edl.dao.EDocLiteDAO;
048: import edu.iu.uis.eden.exception.WorkflowRuntimeException;
049: import edu.iu.uis.eden.export.ExportDataSet;
050: import edu.iu.uis.eden.routeheader.DocumentRouteHeaderValue;
051: import edu.iu.uis.eden.routetemplate.RuleAttribute;
052: import edu.iu.uis.eden.user.WorkflowUser;
053: import edu.iu.uis.eden.util.XmlHelper;
054: import edu.iu.uis.eden.xml.EDocLiteXmlParser;
055: import edu.iu.uis.eden.xml.export.EDocLiteXmlExporter;
056:
057: /**
058: * DAO-based EDocLiteService implementation
059: *
060: * @author Aaron Hamid (arh14 at cornell dot edu)
061: */
062: public class EDocLiteServiceImpl implements EDocLiteService {
063: private static final Logger LOG = Logger
064: .getLogger(EDocLiteServiceImpl.class);
065:
066: private EDLGlobalConfig edlGlobalConfig;
067: /**
068: * The Spring-wired DAO bean
069: */
070: private EDocLiteDAO dao;
071: /**
072: * Spring wired StyleService bean
073: */
074: private StyleService styleService;
075:
076: // ---- Spring DAO setters
077:
078: public void setEDocLiteDAO(EDocLiteDAO dao) {
079: this .dao = dao;
080: }
081:
082: public void setStyleService(StyleService styleService) {
083: this .styleService = styleService;
084: }
085:
086: public EDLController getEDLController(String edlName) {
087: EDocLiteAssociation edlAssociation = this .dao
088: .getEDocLiteAssociation(edlName);
089: if (edlAssociation == null) {
090: throw new WorkflowRuntimeException(
091: "No document association active for EDL: "
092: + edlName);
093: }
094: if (edlGlobalConfig == null) {
095: initEDLGlobalConfig();
096: }
097: return EDLControllerFactory.createEDLController(edlAssociation,
098: edlGlobalConfig);
099: }
100:
101: public EDLController getEDLController(Long documentId) {
102: DocumentRouteHeaderValue document = KEWServiceLocator
103: .getRouteHeaderService().getRouteHeader(documentId);
104: String edlName = document.getAppDocId();//components working with workflow docs will need to know this, perhaps through a document utils.
105: if (edlName == null) {
106: edlName = document.getDocumentType().getName();
107: }
108: EDocLiteAssociation edlAssociation = this .dao
109: .getEDocLiteAssociation(edlName);
110: if (edlAssociation == null) {
111: throw new WorkflowRuntimeException(
112: "No document association active for EDL: "
113: + edlName);
114: }
115: if (edlGlobalConfig == null) {
116: initEDLGlobalConfig();
117: }
118: return EDLControllerFactory.createEDLController(edlAssociation,
119: edlGlobalConfig, document);
120: }
121:
122: public void initEDLGlobalConfig() {
123: try {
124: this .edlGlobalConfig = EDLGlobalConfigFactory
125: .createEDLGlobalConfig(Core
126: .getCurrentContextConfig()
127: .getEDLConfigLocation());
128: } catch (Exception e) {
129: throw new WorkflowRuntimeException(e);
130: }
131: }
132:
133: public Document getDefinitionXml(EDocLiteAssociation edlAssociation) {
134: try {
135: Document def = DocumentBuilderFactory.newInstance()
136: .newDocumentBuilder().parse(
137: new InputSource(new StringReader(
138: getEDocLiteDefinition(
139: edlAssociation
140: .getDefinition())
141: .getXmlContent())));
142: return def;
143: } catch (Exception e) {
144: throw new WorkflowRuntimeException(
145: "Caught exception parsing EDL definition "
146: + edlAssociation.getDefinition(), e);
147: }
148: }
149:
150: private static WorkflowServiceErrorException generateException(
151: String error, Throwable cause) {
152: WorkflowServiceErrorException wsee = new WorkflowServiceErrorException(
153: error, new WorkflowServiceErrorImpl(error,
154: EdenConstants.XML_FILE_PARSE_ERROR));
155: if (cause != null) {
156: wsee.initCause(cause);
157: }
158: return wsee;
159: }
160:
161: private static WorkflowServiceErrorException generateMissingAttribException(
162: String element, String attrib) {
163: return generateException(
164: "EDocLite '" + element + "' element must contain a '"
165: + attrib + "' attribute", null);
166: }
167:
168: private static WorkflowServiceErrorException generateMissingChildException(
169: String element, String child) {
170: return generateException("EDocLite '" + element
171: + "' element must contain a '" + child
172: + "' child element", null);
173: }
174:
175: private static WorkflowServiceErrorException generateSerializationException(
176: String element, TransformerException cause) {
177: return generateException("Error serializing EDocLite '"
178: + element + "' element", cause);
179: }
180:
181: /**
182: * Parses an arbitrary XML stream
183: *
184: * @param stream
185: * stream containing XML doc content
186: * @return parsed Document object
187: */
188: private static Document parse(InputStream stream) {
189: try {
190: return DocumentBuilderFactory.newInstance()
191: .newDocumentBuilder().parse(stream);
192: } catch (Exception e) {
193: WorkflowServiceErrorException wsee = new WorkflowServiceErrorException(
194: "Error parsing EDocLite XML file",
195: new WorkflowServiceErrorImpl(
196: "Error parsing XML file.",
197: EdenConstants.XML_FILE_PARSE_ERROR));
198: wsee.initCause(e);
199: throw wsee;
200: }
201: }
202:
203: /**
204: * Parses an EDocLiteAssocation
205: *
206: * @param e
207: * element to parse
208: * @return an EDocLiteAssocation
209: */
210: private static EDocLiteAssociation parseEDocLiteAssociation(
211: Element e) {
212: String docType = EDLXmlUtils.getChildElementTextValue(e,
213: "docType");
214: if (docType == null) {
215: throw generateMissingChildException("association",
216: "docType");
217: }
218: EDocLiteAssociation assoc = new EDocLiteAssociation();
219: assoc.setEdlName(docType);
220: assoc.setDefinition(EDLXmlUtils.getChildElementTextValue(e,
221: "definition"));
222: assoc
223: .setStyle(EDLXmlUtils.getChildElementTextValue(e,
224: "style"));
225: assoc.setActiveInd(Boolean.valueOf(EDLXmlUtils
226: .getChildElementTextValue(e, "active")));
227: return assoc;
228: }
229:
230: /**
231: * Parses an EDocLiteDefinition
232: *
233: * @param e
234: * element to parse
235: * @return an EDocLiteDefinition
236: */
237: private static EDocLiteDefinition parseEDocLiteDefinition(Element e) {
238: EDocLiteDefinition def = new EDocLiteDefinition();
239: String name = e.getAttribute("name");
240: if (name == null || name.length() == 0) {
241: throw generateMissingAttribException(EDLXmlUtils.EDL_E,
242: "name");
243: }
244: def.setName(name);
245:
246: // do some validation to ensure that any attributes referenced actually exist
247: // blow up if there is a problem
248:
249: XPath xpath = XPathFactory.newInstance().newXPath();
250: NodeList fields;
251: try {
252: fields = (NodeList) xpath.evaluate("fieldDef", e,
253: XPathConstants.NODESET);
254: } catch (XPathExpressionException xpee) {
255: throw new RuntimeException("Invalid EDocLiteDefinition",
256: xpee);
257: }
258:
259: if (fields != null) {
260: Collection invalidAttributes = new ArrayList(5);
261: for (int i = 0; i < fields.getLength(); i++) {
262: Node node = (Node) fields.item(i);
263: // they should all be Element...
264: if (node instanceof Element) {
265: Element field = (Element) node;
266: // rely on XML validation to ensure this is present
267: String fieldName = field.getAttribute("name");
268: String attribute = field
269: .getAttribute("attributeName");
270: if (attribute != null && attribute.length() > 0) {
271: RuleAttribute ruleAttrib = KEWServiceLocator
272: .getRuleAttributeService().findByName(
273: attribute);
274: if (ruleAttrib == null) {
275: LOG
276: .error("Invalid attribute referenced in EDocLite definition: "
277: + attribute);
278: invalidAttributes.add("Attribute '"
279: + attribute
280: + "' referenced in field '"
281: + fieldName + "' not found");
282: }
283: }
284: }
285: }
286: if (invalidAttributes.size() > 0) {
287: LOG
288: .error("Invalid attributes referenced in EDocLite definition");
289: StringBuffer message = new StringBuffer(
290: "EDocLite definition contains references to non-existent attributes;\n");
291: Iterator it = invalidAttributes.iterator();
292: while (it.hasNext()) {
293: message.append(it.next());
294: message.append("\n");
295: }
296: throw new RuntimeException(message.toString());
297: }
298: }
299:
300: try {
301: def.setXmlContent(XmlHelper.writeNode(e, true));
302: } catch (TransformerException te) {
303: throw generateSerializationException(EDLXmlUtils.EDL_E, te);
304: }
305: return def;
306: }
307:
308: // ---- helper methods
309:
310: public void saveEDocLiteStyle(EDocLiteStyle data) {
311: styleService.saveStyle(data);
312: }
313:
314: public void saveEDocLiteDefinition(EDocLiteDefinition data) {
315: EDocLiteDefinition existingData = getEDocLiteDefinition(data
316: .getName());
317: if (existingData != null) {
318: existingData.setActiveInd(Boolean.FALSE);
319: dao.saveEDocLiteDefinition(existingData);
320: }
321: // if not specified (from xml), mark it as active
322: if (data.getActiveInd() == null) {
323: data.setActiveInd(Boolean.TRUE);
324: }
325: dao.saveEDocLiteDefinition(data);
326: removeDefinitionFromCache(data.getName());
327: }
328:
329: public void saveEDocLiteAssociation(EDocLiteAssociation assoc) {
330: EDocLiteAssociation existingData = getEDocLiteAssociation(assoc
331: .getEdlName());
332: if (existingData != null) {
333: existingData.setActiveInd(Boolean.FALSE);
334: dao.saveEDocLiteAssociation(existingData);
335: }
336: // if not specified (from xml), mark it as active
337: if (assoc.getActiveInd() == null) {
338: assoc.setActiveInd(Boolean.TRUE);
339: }
340: dao.saveEDocLiteAssociation(assoc);
341: }
342:
343: // ---- EDocLiteService interface implementation
344:
345: public void saveEDocLiteStyle(InputStream xml) {
346: styleService.saveStyle(xml);
347: }
348:
349: public void saveEDocLiteDefinition(InputStream xml) {
350: // convert xml to EDocLiteDefinition
351: EDocLiteDefinition data = parseEDocLiteDefinition(parse(xml)
352: .getDocumentElement());
353: saveEDocLiteDefinition(data);
354: }
355:
356: public void saveEDocLiteAssociation(InputStream xml) {
357: // convert xml to EDocLiteAssociation
358: EDocLiteAssociation assoc = parseEDocLiteAssociation(parse(xml)
359: .getDocumentElement());
360: saveEDocLiteAssociation(assoc);
361: }
362:
363: public EDocLiteStyle getEDocLiteStyle(String styleName) {
364: return styleService.getStyle(styleName);
365: }
366:
367: public EDocLiteDefinition getEDocLiteDefinition(
368: String definitionName) {
369: return dao.getEDocLiteDefinition(definitionName);
370: }
371:
372: public EDocLiteAssociation getEDocLiteAssociation(String docTypeName) {
373: return dao.getEDocLiteAssociation(docTypeName);
374: }
375:
376: public List<String> getEDocLiteStyles() {
377: return styleService.getStyleNames();
378: }
379:
380: public List getEDocLiteDefinitions() {
381: return dao.getEDocLiteDefinitions();
382: }
383:
384: public List getEDocLiteAssociations() {
385: return dao.getEDocLiteAssociations();
386: }
387:
388: public Templates getStyleAsTranslet(String name)
389: throws TransformerConfigurationException {
390: if (name == null || "null".equals(name)) { //"name".equals(name) - from a null value in the lookupable
391: name = "Default";
392: }
393:
394: return styleService.getStyleAsTranslet(name);
395: }
396:
397: public void removeStyleFromCache(String styleName) {
398: styleService.removeStyleFromCache(styleName);
399: }
400:
401: public void removeDefinitionFromCache(String defName) {
402: LOG.info("Removing definition " + defName + " from cache");
403: EDLControllerFactory.flushDefinitionFromConfigCache(defName);
404: }
405:
406: public List search(EDocLiteAssociation edocLite) {
407: return this .dao.search(edocLite);
408: }
409:
410: public EDocLiteAssociation getEDocLiteAssociation(Long associationId) {
411: return dao.getEDocLiteAssociation(associationId);
412: }
413:
414: // ---- XmlLoader interface implementation
415:
416: public void loadXml(InputStream inputStream, WorkflowUser user) {
417: EDocLiteXmlParser.loadXml(inputStream, user);
418: }
419:
420: // ---- XmlExporter interface implementation
421: public org.jdom.Element export(ExportDataSet dataSet) {
422: return new EDocLiteXmlExporter().export(dataSet);
423: }
424:
425: }
|