Coverage Report - org.kuali.rice.kew.edl.service.impl.StyleServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
StyleServiceImpl
0%
0/123
0%
0/48
2.905
 
 1  
 /*
 2  
  * Copyright 2005-2008 The Kuali Foundation
 3  
  *
 4  
  *
 5  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.opensource.org/licenses/ecl2.php
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.kuali.rice.kew.edl.service.impl;
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.io.InputStream;
 22  
 import java.io.StringReader;
 23  
 import java.net.MalformedURLException;
 24  
 import java.util.List;
 25  
 import java.util.Properties;
 26  
 
 27  
 import javax.xml.parsers.DocumentBuilderFactory;
 28  
 import javax.xml.transform.Templates;
 29  
 import javax.xml.transform.TransformerConfigurationException;
 30  
 import javax.xml.transform.TransformerException;
 31  
 import javax.xml.transform.TransformerFactory;
 32  
 import javax.xml.transform.URIResolver;
 33  
 import javax.xml.transform.stream.StreamSource;
 34  
 
 35  
 import org.apache.log4j.Logger;
 36  
 import org.kuali.rice.core.config.ConfigContext;
 37  
 import org.kuali.rice.core.exception.RiceRuntimeException;
 38  
 import org.kuali.rice.core.util.RiceUtilities;
 39  
 import org.kuali.rice.kew.edl.WidgetURIResolver;
 40  
 import org.kuali.rice.kew.edl.bo.EDocLiteStyle;
 41  
 import org.kuali.rice.kew.edl.dao.EDocLiteDAO;
 42  
 import org.kuali.rice.kew.edl.service.StyleService;
 43  
 import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
 44  
 import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
 45  
 import org.kuali.rice.kew.export.ExportDataSet;
 46  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 47  
 import org.kuali.rice.kew.util.KEWConstants;
 48  
 import org.kuali.rice.kew.util.Utilities;
 49  
 import org.kuali.rice.kew.util.XmlHelper;
 50  
 import org.kuali.rice.kew.xml.StyleXmlParser;
 51  
 import org.kuali.rice.kew.xml.export.StyleXmlExporter;
 52  
 import org.kuali.rice.kns.util.KNSConstants;
 53  
 import org.w3c.dom.Attr;
 54  
 import org.w3c.dom.Document;
 55  
 import org.w3c.dom.Element;
 56  
 import org.w3c.dom.NamedNodeMap;
 57  
 import org.w3c.dom.Node;
 58  
 import org.w3c.dom.NodeList;
 59  
 
 60  
 
 61  
 /**
 62  
  * Implements generic StyleService via existing EDL style table
 63  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 64  
  */
 65  0
 public class StyleServiceImpl implements StyleService {
 66  0
     private static final Logger LOG = Logger.getLogger(StyleServiceImpl.class);
 67  
 
 68  
     private static final String TEMPLATES_CACHE_GROUP_NAME = "Templates";
 69  
     private static final String STYLE_CONFIG_PREFIX = "edl.style";
 70  
 
 71  
     /**
 72  
      * The Spring-wired DAO bean
 73  
      */
 74  
     private EDocLiteDAO dao;
 75  
 
 76  
     // ---- Spring DAO setter
 77  
 
 78  
     public void setStyleDAO(EDocLiteDAO dao) {
 79  0
         this.dao = dao;
 80  0
     }
 81  
 
 82  
     /**
 83  
      * Loads the named style from the database, or (if configured) imports it from a file
 84  
      * specified via a configuration parameter with a name of the format edl.style.<styleName>
 85  
      * {@inheritDoc}
 86  
      * @see org.kuali.rice.kew.edl.service.StyleService#getStyle(java.lang.String)
 87  
      */
 88  
     public EDocLiteStyle getStyle(String styleName) {
 89  0
         EDocLiteStyle result = null;
 90  
         // try to fetch the style from the database
 91  0
         result = dao.getEDocLiteStyle(styleName);
 92  
         // if it's null, look for a config param specifiying a file to load
 93  0
         if (result == null) {
 94  0
             String propertyName = STYLE_CONFIG_PREFIX + "." + styleName;
 95  0
             String location = ConfigContext.getCurrentContextConfig().getProperty(propertyName);
 96  0
             if (location != null) {
 97  
 
 98  0
                 InputStream xml = null;
 99  
 
 100  
                 try {
 101  0
                     xml = RiceUtilities.getResourceAsStream(location);
 102  0
                 } catch (MalformedURLException e) {
 103  0
                     throw new RiceRuntimeException(getUnableToLoadMessage(propertyName, location), e);
 104  0
                 } catch (IOException e) {
 105  0
                     throw new RiceRuntimeException(getUnableToLoadMessage(propertyName, location), e);
 106  0
                 }
 107  
 
 108  0
                 if (xml == null) {
 109  0
                     throw new RiceRuntimeException(getUnableToLoadMessage(propertyName, location) + ", no such file");
 110  
                 }
 111  
 
 112  0
                 Element style = findNamedStyle(styleName, xml);
 113  
 
 114  0
                 if (style == null) {
 115  0
                     throw new RiceRuntimeException(getUnableToLoadMessage(propertyName, location) +
 116  
                             ", no style named '"+ styleName +"' in that file");
 117  
                 }
 118  
 
 119  0
                 EDocLiteStyle loadedStyle = parseEDocLiteStyle(style);
 120  
                 // redundant check, but it doesn't hurt
 121  0
                 if (!styleName.equals(loadedStyle.getName())) {
 122  0
                     throw new RiceRuntimeException("EDocLiteStyle loaded from " + location +
 123  
                             " does not contain style named " + styleName);
 124  
                 } else {
 125  0
                     LOG.info("importing style '" + styleName + "' from '" + location + "' as configured by "+ propertyName);
 126  0
                     saveStyle(loadedStyle);
 127  0
                     result = loadedStyle;
 128  
                 }
 129  
             }
 130  
         }
 131  0
         return result;
 132  
     }
 133  
 
 134  
     /**
 135  
      * returns the first <style> element it encounters with the
 136  
      * given name attribute from an xml document
 137  
      */
 138  
     private Element findNamedStyle(String styleName, InputStream xml) {
 139  0
         Element style = null;
 140  
 
 141  0
         Document xmlDoc = parse(xml);
 142  0
         NodeList nodes = xmlDoc.getElementsByTagName("style");
 143  0
         if (nodes != null) for (int i=0; i<nodes.getLength(); i++) {
 144  0
             Element element = (Element)nodes.item(i);
 145  0
             NamedNodeMap attrs = element.getAttributes();
 146  0
             if (attrs != null) {
 147  0
                 Attr attr = (Attr)attrs.getNamedItem("name");
 148  0
                 if (styleName.equals(attr.getValue())) {
 149  0
                     style = element;
 150  0
                     break;
 151  
                 }
 152  
             }
 153  
         }
 154  0
         return style;
 155  
     }
 156  
 
 157  
     /**
 158  
      * This method ...
 159  
      *
 160  
      * @param propertyName
 161  
      * @param location
 162  
      * @return
 163  
      */
 164  
     private String getUnableToLoadMessage(String propertyName, String location) {
 165  0
         return "unable to load resource at '" + location +
 166  
                 "' specified by configuration parameter '" + propertyName + "'";
 167  
     }
 168  
 
 169  
     public Templates getStyleAsTranslet(String name) throws TransformerConfigurationException {
 170  0
         if (name == null) return null;
 171  0
         Templates translet = fetchTemplatesFromCache(name);
 172  0
         if (translet == null) {
 173  0
             EDocLiteStyle edlStyleData = getStyle(name);
 174  0
             if (edlStyleData == null) {
 175  
                 //throw new WorkflowRuntimeException("Style " + name + " not found.");
 176  0
                 return null;
 177  
             }
 178  
 
 179  0
             boolean useXSLTC = Utilities.getKNSParameterBooleanValue(KEWConstants.KEW_NAMESPACE, KNSConstants.DetailTypes.EDOC_LITE_DETAIL_TYPE, KEWConstants.EDL_USE_XSLTC_IND);
 180  0
             if (useXSLTC) {
 181  0
                 LOG.info("using xsltc to compile stylesheet");
 182  0
                 String key = "javax.xml.transform.TransformerFactory";
 183  0
                 String value = "org.apache.xalan.xsltc.trax.TransformerFactoryImpl";
 184  0
                 Properties props = System.getProperties();
 185  0
                 props.put(key, value);
 186  0
                 System.setProperties(props);
 187  
             }
 188  
 
 189  0
             TransformerFactory factory = TransformerFactory.newInstance();
 190  0
             URIResolver resolver = new WidgetURIResolver();
 191  0
             factory.setURIResolver(resolver);
 192  
 
 193  0
             if (useXSLTC) {
 194  0
                 factory.setAttribute("translet-name",name);
 195  0
                 factory.setAttribute("generate-translet",Boolean.TRUE);
 196  0
                 String debugTransform = Utilities.getKNSParameterValue(KEWConstants.KEW_NAMESPACE, KNSConstants.DetailTypes.EDOC_LITE_DETAIL_TYPE, KEWConstants.EDL_DEBUG_TRANSFORM_IND);
 197  0
                 if (debugTransform.trim().equals("Y")) {
 198  0
                     factory.setAttribute("debug", Boolean.TRUE);
 199  
                 }
 200  
             }
 201  
 
 202  0
             translet = factory.newTemplates(new StreamSource(new StringReader(edlStyleData.getXmlContent())));
 203  0
             putTemplatesInCache(name, translet);
 204  
         }
 205  0
         return translet;
 206  
     }
 207  
 
 208  
     /**
 209  
      * Returns all styles
 210  
      */
 211  
     public List<EDocLiteStyle> getStyles() {
 212  0
         return dao.getEDocLiteStyles();
 213  
     }
 214  
 
 215  
     /**
 216  
      * Returns all style names
 217  
      */
 218  
     public List<String> getStyleNames() {
 219  0
         return dao.getEDocLiteStyleNames();
 220  
     }
 221  
 
 222  
     /**
 223  
      * Does not currently take into account style sheet dependences robustly
 224  
      */
 225  
     public void removeStyleFromCache(String styleName) {
 226  0
         LOG.info("Removing Style " + styleName + " from the style cache");
 227  
         // we don't know what styles may import other styles so we need to flush them all
 228  0
         KEWServiceLocator.getCacheAdministrator().flushGroup(TEMPLATES_CACHE_GROUP_NAME);
 229  
         //KEWServiceLocator.getCacheAdministrator().flushEntry(getTemplatesCacheKey(styleName));
 230  0
     }
 231  
 
 232  
     public void saveStyle(InputStream xml) {
 233  
         // convert xml to EDocLiteStyle
 234  0
         EDocLiteStyle data = parseEDocLiteStyle(parse(xml).getDocumentElement());
 235  0
         saveStyle(data);
 236  0
     }
 237  
 
 238  
     public void saveStyle(EDocLiteStyle data) {
 239  0
         EDocLiteStyle existingData = dao.getEDocLiteStyle(data.getName());
 240  0
         if (existingData != null) {
 241  0
             existingData.setActiveInd(Boolean.FALSE);
 242  0
             dao.saveEDocLiteStyle(existingData);
 243  
         }
 244  
         // if not specified (from xml), mark it as active
 245  0
         if (data.getActiveInd() == null) {
 246  0
             data.setActiveInd(Boolean.TRUE);
 247  
         }
 248  0
         dao.saveEDocLiteStyle(data);
 249  0
         removeStyleFromCache(data.getName());
 250  0
     }
 251  
 
 252  
 
 253  
     // ---- XmlLoader interface implementation
 254  
 
 255  
     public void loadXml(InputStream inputStream, String principalId) {
 256  0
         StyleXmlParser.loadXml(this, inputStream, principalId);
 257  0
     }
 258  
 
 259  
     // ---- XmlExporter interface implementation
 260  
     public org.jdom.Element export(ExportDataSet dataSet) {
 261  0
         return new StyleXmlExporter().export(dataSet);
 262  
     }
 263  
 
 264  
     // cache helper methods
 265  
 
 266  
     /**
 267  
      * Returns the key to be used for caching the Templates for the given style name.
 268  
      */
 269  
     protected String getTemplatesCacheKey(String styleName) {
 270  0
         return TEMPLATES_CACHE_GROUP_NAME + ":" + styleName;
 271  
     }
 272  
 
 273  
     protected Templates fetchTemplatesFromCache(String styleName) {
 274  0
         return (Templates) KEWServiceLocator.getCacheAdministrator().getFromCache(getTemplatesCacheKey(styleName));
 275  
     }
 276  
 
 277  
     protected void putTemplatesInCache(String styleName, Templates templates) {
 278  0
         KEWServiceLocator.getCacheAdministrator().putInCache(getTemplatesCacheKey(styleName), templates, TEMPLATES_CACHE_GROUP_NAME);
 279  0
     }
 280  
 
 281  
     // parsing helper methods
 282  
 
 283  
     /**
 284  
      * Parses an EDocLiteStyle
 285  
      *
 286  
      * @param e
 287  
      *            element to parse
 288  
      * @return an EDocLiteStyle
 289  
      */
 290  
     private static EDocLiteStyle parseEDocLiteStyle(Element e) {
 291  0
         String name = e.getAttribute("name");
 292  0
         if (name == null || name.length() == 0) {
 293  0
             throw generateMissingAttribException("style", "name");
 294  
         }
 295  0
         EDocLiteStyle style = new EDocLiteStyle();
 296  0
         style.setName(name);
 297  0
         Element stylesheet = null;
 298  0
         NodeList children = e.getChildNodes();
 299  0
         for (int i = 0; i < children.getLength(); i++) {
 300  0
             Node child = children.item(i);
 301  
             /*
 302  
              * LOG.debug("NodeName: " + child.getNodeName()); LOG.debug("LocalName: " + child.getLocalName()); LOG.debug("Prefix: " + child.getPrefix()); LOG.debug("NS URI: " + child.getNamespaceURI());
 303  
              */
 304  0
             if (child.getNodeType() == Node.ELEMENT_NODE && "xsl:stylesheet".equals(child.getNodeName())) {
 305  0
                 stylesheet = (Element) child;
 306  0
                 break;
 307  
             }
 308  
         }
 309  0
         if (stylesheet == null) {
 310  0
             throw generateMissingChildException("style", "xsl:stylesheet");
 311  
         }
 312  
         try {
 313  0
             style.setXmlContent(XmlHelper.writeNode(stylesheet, true));
 314  0
         } catch (TransformerException te) {
 315  0
             throw generateSerializationException("style", te);
 316  0
         }
 317  0
         return style;
 318  
     }
 319  
 
 320  
     /**
 321  
      * Parses an arbitrary XML stream
 322  
      *
 323  
      * @param stream
 324  
      *            stream containing XML doc content
 325  
      * @return parsed Document object
 326  
      */
 327  
     private static Document parse(InputStream stream) {
 328  
         try {
 329  0
             return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream);
 330  0
         } catch (Exception e) {
 331  0
             WorkflowServiceErrorException wsee = new WorkflowServiceErrorException("Error parsing Style XML file", new WorkflowServiceErrorImpl("Error parsing XML file.", KEWConstants.XML_FILE_PARSE_ERROR));
 332  0
             wsee.initCause(e);
 333  0
             throw wsee;
 334  
         }
 335  
     }
 336  
 
 337  
     private static WorkflowServiceErrorException generateMissingAttribException(String element, String attrib) {
 338  0
         return generateException("Style '" + element + "' element must contain a '" + attrib + "' attribute", null);
 339  
     }
 340  
 
 341  
     private static WorkflowServiceErrorException generateMissingChildException(String element, String child) {
 342  0
         return generateException("Style '" + element + "' element must contain a '" + child + "' child element", null);
 343  
     }
 344  
 
 345  
     private static WorkflowServiceErrorException generateSerializationException(String element, TransformerException cause) {
 346  0
         return generateException("Error serializing EDocLite '" + element + "' element", cause);
 347  
     }
 348  
 
 349  
     private static WorkflowServiceErrorException generateException(String error, Throwable cause) {
 350  0
         WorkflowServiceErrorException wsee = new WorkflowServiceErrorException(error, new WorkflowServiceErrorImpl(error, KEWConstants.XML_FILE_PARSE_ERROR));
 351  0
         if (cause != null) {
 352  0
             wsee.initCause(cause);
 353  
         }
 354  0
         return wsee;
 355  
     }
 356  
 }