Coverage Report - org.kuali.rice.ken.util.Util
 
Classes in this File Line Coverage Branch Coverage Complexity
Util
0%
0/150
0%
0/38
2.217
Util$1
0%
0/7
N/A
2.217
 
 1  
 /*
 2  
  * Copyright 2007 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.ken.util;
 17  
 
 18  
 import java.io.ByteArrayOutputStream;
 19  
 import java.io.IOException;
 20  
 import java.io.InputStream;
 21  
 import java.io.StringWriter;
 22  
 import java.sql.Timestamp;
 23  
 import java.text.DateFormat;
 24  
 import java.text.ParseException;
 25  
 import java.text.SimpleDateFormat;
 26  
 import java.util.ArrayList;
 27  
 import java.util.Date;
 28  
 import java.util.HashMap;
 29  
 import java.util.Map;
 30  
 import java.util.TimeZone;
 31  
 
 32  
 import javax.xml.namespace.NamespaceContext;
 33  
 import javax.xml.parsers.DocumentBuilder;
 34  
 import javax.xml.parsers.DocumentBuilderFactory;
 35  
 import javax.xml.parsers.ParserConfigurationException;
 36  
 import javax.xml.transform.OutputKeys;
 37  
 import javax.xml.transform.Result;
 38  
 import javax.xml.transform.Source;
 39  
 import javax.xml.transform.Transformer;
 40  
 import javax.xml.transform.TransformerException;
 41  
 import javax.xml.transform.TransformerFactory;
 42  
 import javax.xml.transform.dom.DOMSource;
 43  
 import javax.xml.transform.stream.StreamResult;
 44  
 import javax.xml.transform.stream.StreamSource;
 45  
 
 46  
 import org.apache.commons.lang.StringUtils;
 47  
 import org.apache.log4j.Logger;
 48  
 import org.kuali.rice.core.config.ConfigContext;
 49  
 import org.kuali.rice.core.dao.GenericDao;
 50  
 import org.kuali.rice.ken.bo.Notification;
 51  
 import org.kuali.rice.ken.bo.NotificationChannel;
 52  
 import org.kuali.rice.ken.bo.NotificationContentType;
 53  
 import org.kuali.rice.ken.bo.NotificationPriority;
 54  
 import org.kuali.rice.ken.bo.NotificationProducer;
 55  
 import org.kuali.rice.ken.bo.NotificationRecipient;
 56  
 import org.kuali.rice.ken.bo.NotificationSender;
 57  
 import org.kuali.rice.ken.service.NotificationContentTypeService;
 58  
 import org.w3c.dom.Document;
 59  
 import org.w3c.dom.Element;
 60  
 import org.w3c.dom.Node;
 61  
 import org.w3c.dom.NodeList;
 62  
 import org.xml.sax.EntityResolver;
 63  
 import org.xml.sax.ErrorHandler;
 64  
 import org.xml.sax.InputSource;
 65  
 import org.xml.sax.SAXException;
 66  
 import org.xml.sax.SAXParseException;
 67  
 
 68  
 import com.sun.org.apache.xerces.internal.jaxp.JAXPConstants;
 69  
 
 70  
 /**
 71  
  * A general Utility class for the Notification system.
 72  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 73  
  */
 74  0
 public final class Util {
 75  0
     private static final Logger LOG = Logger.getLogger(Util.class);
 76  
 
 77  
     //public static final EntityResolver ENTITY_RESOLVER = new ClassLoaderEntityResolver("schema", "notification");
 78  
     public static final NamespaceContext NOTIFICATION_NAMESPACE_CONTEXT;
 79  
 
 80  
     //  XSD Zulu (UTC) date format necessary for XML request messages
 81  0
     private static final DateFormat DATEFORMAT_ZULU = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
 82  
     
 83  
     // Server current date time
 84  0
     private static final DateFormat DATEFORMAT_CURR_TZ = new SimpleDateFormat(
 85  
     "MM/dd/yyyy hh:mm a");
 86  
     
 87  
     static {
 88  0
         Map<String, String> prefixToNS = new HashMap<String, String>();
 89  0
         prefixToNS.put("nreq", "ns:notification/NotificationRequest");
 90  0
         NOTIFICATION_NAMESPACE_CONTEXT = new ConfiguredNamespaceContext(prefixToNS);
 91  
 
 92  
         // set the timezone to Zulu for the XML/XSD formatter
 93  0
         DATEFORMAT_ZULU.setTimeZone(TimeZone.getTimeZone("UTC"));
 94  
         // set the timezone for current time
 95  
          
 96  0
     }
 97  
 
 98  
     /**
 99  
      * @return the name of the user configured to be the Notification system user
 100  
      */
 101  
     public static String getNotificationSystemUser() {
 102  0
         String system_user = ConfigContext.getCurrentContextConfig().getProperty(NotificationConstants.KEW_CONSTANTS.NOTIFICATION_SYSTEM_USER_PARAM);
 103  0
         if (system_user == null) {
 104  0
             system_user = NotificationConstants.KEW_CONSTANTS.NOTIFICATION_SYSTEM_USER;
 105  
         }
 106  0
         return system_user;
 107  
     }
 108  
 
 109  
     /**
 110  
      * Parses a date/time string under XSD dateTime type syntax
 111  
      * @see #DATEFORMAT_ZULU
 112  
      * @param dateTimeString an XSD dateTime-formatted String
 113  
      * @return a Date representing the time value of the String parameter 
 114  
      * @throws ParseException if an error occurs during parsing 
 115  
      */
 116  
     public static Date parseXSDDateTime(String dateTimeString) throws ParseException {
 117  0
         synchronized (DATEFORMAT_ZULU) {
 118  0
             return DATEFORMAT_ZULU.parse(dateTimeString);
 119  0
         }
 120  
     }
 121  
 
 122  
     /**
 123  
      * Formats a Date into XSD dateTime format
 124  
      * @param d the date value to format
 125  
      * @return date value formatted into XSD dateTime format
 126  
      */
 127  
     public static String toXSDDateTimeString(Date d) {
 128  0
         synchronized (DATEFORMAT_ZULU) {
 129  0
             return DATEFORMAT_ZULU.format(d);
 130  0
         }
 131  
     }
 132  
     
 133  
     /**
 134  
      * Returns the current date formatted for the UI
 135  
      * @return the current date formatted for the UI
 136  
      */
 137  
     public static String getCurrentDateTime() {
 138  0
         return toUIDateTimeString(new Date());
 139  
     }
 140  
     
 141  
     /**
 142  
      * Returns the specified date formatted for the UI
 143  
      * @return the specified date formatted for the UI
 144  
      */
 145  
     public static String toUIDateTimeString(Date d) {
 146  0
         synchronized (DATEFORMAT_CURR_TZ) {
 147  0
            return DATEFORMAT_CURR_TZ.format(d);
 148  0
         }
 149  
     }
 150  
 
 151  
     /**
 152  
      * Parses the string in UI date time format
 153  
      * @return the date parsed from UI date time format
 154  
      */
 155  
     public static Date parseUIDateTime(String s) throws ParseException {
 156  0
         synchronized (DATEFORMAT_CURR_TZ) {
 157  0
            return DATEFORMAT_CURR_TZ.parse(s);
 158  0
         }
 159  
     }
 160  
 
 161  
     /**
 162  
      * Returns a compound NamespaceContext that defers to the preconfigured notification namespace context
 163  
      * first, then delegates to the document prefix/namespace definitions second.
 164  
      * @param doc the Document to use for prefix/namespace resolution
 165  
      * @return  compound NamespaceContext
 166  
      */
 167  
     public static NamespaceContext getNotificationNamespaceContext(Document doc) {
 168  0
         return new CompoundNamespaceContext(NOTIFICATION_NAMESPACE_CONTEXT, new DocumentNamespaceContext(doc));
 169  
     }
 170  
 
 171  
     /**
 172  
      * Returns an EntityResolver to resolve XML entities (namely schema resources) in the notification system
 173  
      * @param notificationContentTypeService the NotificationContentTypeService
 174  
      * @return an EntityResolver to resolve XML entities (namely schema resources) in the notification system
 175  
      */
 176  
     public static EntityResolver getNotificationEntityResolver(NotificationContentTypeService notificationContentTypeService) {
 177  0
         return new CompoundEntityResolver(new ClassLoaderEntityResolver("schema", "notification"),
 178  
                                           new ContentTypeEntityResolver(notificationContentTypeService));
 179  
     }
 180  
 
 181  
     /**
 182  
      * transformContent - transforms xml content in notification to a string
 183  
      * using the xsl in the datastore for a given documentType
 184  
      * @param notification
 185  
      * @return
 186  
      */
 187  
     public static String transformContent(Notification notification) {
 188  0
         NotificationContentType contentType = notification.getContentType();
 189  0
         String xsl = contentType.getXsl();
 190  
         
 191  0
         LOG.debug("xsl: "+xsl);
 192  
         
 193  0
         XslSourceResolver xslresolver = new XslSourceResolver();
 194  
         //StreamSource xslsource = xslresolver.resolveXslFromFile(xslpath);
 195  0
         StreamSource xslsource = xslresolver.resolveXslFromString(xsl);
 196  0
         String content = notification.getContent();
 197  0
         LOG.debug("xslsource:"+xslsource.toString());
 198  
         
 199  0
         String contenthtml = new String();
 200  
         try {
 201  0
           ContentTransformer transformer = new ContentTransformer(xslsource);
 202  0
           contenthtml = transformer.transform(content);
 203  0
           LOG.debug("html: "+contenthtml);
 204  0
         } catch (IOException ex) {
 205  0
             LOG.error("IOException transforming document",ex);
 206  0
         } catch (Exception ex) {
 207  0
             LOG.error("Exception transforming document",ex);
 208  0
         } 
 209  0
         return contenthtml;
 210  
     }
 211  
 
 212  
     /**
 213  
      * This method uses DOM to parse the input source of XML.
 214  
      * @param source the input source
 215  
      * @param validate whether to turn on validation
 216  
      * @param namespaceAware whether to turn on namespace awareness
 217  
      * @return Document the parsed (possibly validated) document
 218  
      * @throws ParserConfigurationException
 219  
      * @throws IOException
 220  
      * @throws SAXException
 221  
      */
 222  
     public static Document parse(final InputSource source, boolean validate, boolean namespaceAware, EntityResolver entityResolver) throws ParserConfigurationException, IOException, SAXException {
 223  
         // TODO: optimize this
 224  0
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 225  0
         dbf.setValidating(validate);
 226  0
         dbf.setNamespaceAware(namespaceAware);
 227  0
         dbf.setAttribute(JAXPConstants.JAXP_SCHEMA_LANGUAGE, JAXPConstants.W3C_XML_SCHEMA);
 228  0
         DocumentBuilder db = dbf.newDocumentBuilder();
 229  0
         if (entityResolver != null) {
 230  0
             db.setEntityResolver(entityResolver);
 231  
         }
 232  0
         db.setErrorHandler(new ErrorHandler() {
 233  
             public void warning(SAXParseException se) {
 234  0
                 LOG.warn("Warning parsing xml doc " + source, se);
 235  0
             }
 236  
             public void error(SAXParseException se) throws SAXException {
 237  0
                 LOG.error("Error parsing xml doc " + source, se);
 238  0
                 throw se;
 239  
             }
 240  
             public void fatalError(SAXParseException se) throws SAXException {
 241  0
                 LOG.error("Fatal error parsing xml doc " + source, se);
 242  0
                 throw se;
 243  
             }
 244  
         });
 245  0
         return db.parse(source);
 246  
     }
 247  
 
 248  
     /**
 249  
      * This method uses DOM to parse the input source of XML, supplying a notification-system-specific
 250  
      * entity resolver.
 251  
      * @param source the input source
 252  
      * @param validate whether to turn on validation
 253  
      * @param namespaceAware whether to turn on namespace awareness
 254  
      * @return Document the parsed (possibly validated) document
 255  
      * @throws ParserConfigurationException
 256  
      * @throws IOException
 257  
      * @throws SAXException
 258  
      */
 259  
     public static Document parseWithNotificationEntityResolver(final InputSource source, boolean validate, boolean namespaceAware, NotificationContentTypeService notificationContentTypeService) throws ParserConfigurationException, IOException, SAXException {
 260  0
         return parse(source, validate, namespaceAware, getNotificationEntityResolver(notificationContentTypeService));
 261  
     }
 262  
 
 263  
     /**
 264  
      * This method returns the value of the first chile of the element node.
 265  
      * @param element
 266  
      * @return String
 267  
      */
 268  
     public static String getTextContent(org.w3c.dom.Element element) {
 269  0
         NodeList children = element.getChildNodes();
 270  0
         Node node = children.item(0);
 271  0
         return node.getNodeValue();
 272  
     }
 273  
 
 274  
     /**
 275  
      * Returns a node child with the specified tag name of the specified parent node,
 276  
      * or null if no such child node is found. 
 277  
      * @param parent the parent node
 278  
      * @param name the name of the child node
 279  
      * @return child node if found, null otherwise
 280  
      */
 281  
     public static Element getChildElement(Node parent, String name) {
 282  0
         NodeList childList = parent.getChildNodes();
 283  0
         for (int i = 0; i < childList.getLength(); i++) {
 284  0
             Node node = childList.item(i);
 285  
             // we must test against NodeName, not just LocalName
 286  
             // LocalName seems to be null - I am guessing this is because
 287  
             // the DocumentBuilderFactory is not "namespace aware"
 288  
             // although I would have expected LocalName to default to
 289  
             // NodeName
 290  0
             if (node.getNodeType() == Node.ELEMENT_NODE
 291  
                 && (name.equals(node.getLocalName())
 292  
                    || name.equals(node.getNodeName()))) {
 293  0
                 return (Element) node;
 294  
             }
 295  
         }
 296  0
         return null;
 297  
     }
 298  
     
 299  
     /**
 300  
      * Returns the text value of a child element with the given name, of the given parent element,
 301  
      * or null if the child does not exist or does not have a child text node
 302  
      * @param parent parent element
 303  
      * @param name name of child element
 304  
      * @return the text value of a child element with the given name, of the given parent element,
 305  
      * or null if the child does not exist or does not have a child text node
 306  
      */
 307  
     public static String getChildElementTextValue(Node parent, String name) {
 308  0
         Element child = getChildElement(parent, name);
 309  0
         if (child == null) {
 310  0
             return null;
 311  
         }
 312  0
         return child.getTextContent();
 313  
     }
 314  
 
 315  
     /**
 316  
      * Reads the entire contents of a stream and returns a byte array
 317  
      * 
 318  
      * @param stream
 319  
      *            the stream to read fully
 320  
      * @return a byte array containing the contents of the stream
 321  
      * @throws IOException
 322  
      */
 323  
     public static byte[] readFully(InputStream stream) throws IOException {
 324  0
         byte[] buf = new byte[1024];
 325  0
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 326  
         int read;
 327  0
         while ((read = stream.read(buf)) != -1) {
 328  0
             baos.write(buf, 0, read);
 329  
         }
 330  0
         return baos.toByteArray();
 331  
     }
 332  
 
 333  
     /**
 334  
      * Serializes a node to XML (without indentation)
 335  
      * @param node the node to serialize
 336  
      * @return the serialized node
 337  
      * @throws TransformerException if transformation fails
 338  
      */
 339  
     public static String writeNode(org.w3c.dom.Node node) throws TransformerException {
 340  0
         return writeNode(node, false);
 341  
     }
 342  
 
 343  
     /**
 344  
      * Serializes a node to XML
 345  
      * @param node the node to serialize
 346  
      * @param indent whether to apply indentation to the output
 347  
      * @return the serialized node
 348  
      * @throws TransformerException if transformation fails
 349  
      */
 350  
     public static String writeNode(org.w3c.dom.Node node, boolean indent) throws TransformerException {
 351  0
         Source source = new DOMSource(node);
 352  0
         StringWriter writer = new StringWriter();
 353  0
         Result result = new StreamResult(writer);
 354  0
         Transformer transformer = TransformerFactory.newInstance().newTransformer();
 355  0
         transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
 356  0
         if (indent) {
 357  0
             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
 358  
         }
 359  0
         transformer.transform(source, result);
 360  0
         return writer.toString();
 361  
     }
 362  
     
 363  
     /**
 364  
      * This method will clone a given Notification object, one level deep, returning a fresh new instance 
 365  
      * without any references.
 366  
      * @param notification the object to clone
 367  
      * @return Notification a fresh instance
 368  
      */
 369  
     public static final Notification cloneNotificationWithoutObjectReferences(Notification notification) {
 370  0
         Notification clone = new Notification();
 371  
         
 372  
         // handle simple data types first
 373  0
         if(notification.getCreationDateTime() != null) {
 374  0
             clone.setCreationDateTime(new Timestamp(notification.getCreationDateTime().getTime()));
 375  
         }
 376  0
         if(notification.getAutoRemoveDateTime() != null) {
 377  0
             clone.setAutoRemoveDateTime(new Timestamp(notification.getAutoRemoveDateTime().getTime()));
 378  
         }
 379  0
         clone.setContent(new String(notification.getContent()));
 380  0
         clone.setDeliveryType(new String(notification.getDeliveryType()));
 381  0
         if(notification.getId() != null) {
 382  0
             clone.setId(new Long(notification.getId()));
 383  
         }
 384  0
         clone.setProcessingFlag(new String(notification.getProcessingFlag()));
 385  0
         if(notification.getSendDateTime() != null) {
 386  0
             clone.setSendDateTime(new Timestamp(notification.getSendDateTime().getTime()));
 387  
         }
 388  
         
 389  0
         clone.setTitle(notification.getTitle());
 390  
         
 391  
         // now take care of the channel
 392  0
         NotificationChannel channel = new NotificationChannel();
 393  0
         channel.setId(new Long(notification.getChannel().getId()));
 394  0
         channel.setName(new String(notification.getChannel().getName()));
 395  0
         channel.setDescription(new String(notification.getChannel().getDescription()));
 396  0
         channel.setSubscribable(new Boolean(notification.getChannel().isSubscribable()).booleanValue());
 397  0
         clone.setChannel(channel);
 398  
         
 399  
         // handle the content type
 400  0
         NotificationContentType contentType = new NotificationContentType();
 401  0
         contentType.setId(new Long(notification.getContentType().getId()));
 402  0
         contentType.setDescription(new String(notification.getContentType().getDescription()));
 403  0
         contentType.setName(new String(notification.getContentType().getName()));
 404  0
         contentType.setNamespace(new String(notification.getContentType().getNamespace()));
 405  0
         clone.setContentType(contentType);
 406  
         
 407  
         // take care of the prioirity
 408  0
         NotificationPriority priority = new NotificationPriority();
 409  0
         priority.setDescription(new String(notification.getPriority().getDescription()));
 410  0
         priority.setId(new Long(notification.getPriority().getId()));
 411  0
         priority.setName(new String(notification.getPriority().getName()));
 412  0
         priority.setOrder(new Integer(notification.getPriority().getOrder()));
 413  0
         clone.setPriority(priority);
 414  
         
 415  
         // take care of the producer
 416  0
         NotificationProducer producer = new NotificationProducer();
 417  0
         producer.setDescription(new String(notification.getProducer().getDescription()));
 418  0
         producer.setId(new Long(notification.getProducer().getId()));
 419  0
         producer.setName(new String(notification.getProducer().getName()));
 420  0
         producer.setContactInfo(new String(notification.getProducer().getContactInfo()));
 421  0
         clone.setProducer(producer);
 422  
         
 423  
         // process the list of recipients now
 424  0
         ArrayList<NotificationRecipient> recipients = new ArrayList<NotificationRecipient>();
 425  0
         for(int i = 0; i < notification.getRecipients().size(); i++) {
 426  0
             NotificationRecipient recipient = notification.getRecipient(i);
 427  0
             NotificationRecipient cloneRecipient = new NotificationRecipient();
 428  0
             cloneRecipient.setRecipientId(new String(recipient.getRecipientId()));
 429  0
             cloneRecipient.setRecipientType(new String(recipient.getRecipientType()));
 430  
             
 431  0
             recipients.add(cloneRecipient);
 432  
         }
 433  0
         clone.setRecipients(recipients);
 434  
         
 435  
         // process the list of senders now
 436  0
         ArrayList<NotificationSender> senders = new ArrayList<NotificationSender>();
 437  0
         for(int i = 0; i < notification.getSenders().size(); i++) {
 438  0
             NotificationSender sender = notification.getSender(i);
 439  0
             NotificationSender cloneSender = new NotificationSender();
 440  0
             cloneSender.setSenderName(new String(sender.getSenderName()));
 441  
             
 442  0
             senders.add(cloneSender);
 443  
         }
 444  0
         clone.setSenders(senders);
 445  
         
 446  0
         return clone;
 447  
     }
 448  
     
 449  
     /**
 450  
      * This method generically retrieves a reference to foreign key objects that are part of the content, to get 
 451  
      * at the reference objects' pk fields so that those values can be used to store the notification with proper 
 452  
      * foreign key relationships in the database.
 453  
      * @param <T>
 454  
      * @param fieldName
 455  
      * @param keyName
 456  
      * @param keyValue
 457  
      * @param clazz
 458  
      * @param boDao
 459  
      * @return T
 460  
      * @throws IllegalArgumentException
 461  
      */
 462  
     public static <T> T retrieveFieldReference(String fieldName, String keyName, String keyValue, Class clazz, GenericDao boDao) throws IllegalArgumentException {
 463  0
         LOG.debug(fieldName + " key value: " + keyValue);
 464  0
         if (StringUtils.isBlank(keyValue)) {
 465  0
             throw new IllegalArgumentException(fieldName + " must be specified in notification");
 466  
         }
 467  0
         Map<String, Object> keys = new HashMap<String, Object>(1);
 468  0
         keys.put(keyName, keyValue);
 469  0
         T reference = (T) boDao.findByPrimaryKey(clazz, keys);
 470  0
         if (reference == null) {
 471  0
             throw new IllegalArgumentException(fieldName + " '" + keyValue + "' not found");
 472  
         }
 473  0
         return reference;
 474  
     }
 475  
     
 476  
     /**
 477  
      * This method searches for an exception of the specified type in the stack trace of the given
 478  
      * exception.
 479  
      * @param topLevelException the exception whose stack to traverse
 480  
      * @param exceptionClass the exception class to look for
 481  
      * @return the first instance of an exception of the specified class if found, or null otherwise
 482  
      */
 483  
     public static <T extends Throwable> T findExceptionInStack(Throwable topLevelException, Class<T> exceptionClass) {
 484  0
         Throwable t = topLevelException;
 485  0
         while (t != null) {
 486  0
             if (exceptionClass.isAssignableFrom(t.getClass())) return (T) t;
 487  0
             t = t.getCause();
 488  
         }
 489  0
         return null;
 490  
     }
 491  
 }