Coverage Report - org.kuali.rice.ken.util.Util
 
Classes in this File Line Coverage Branch Coverage Complexity
Util
0%
0/116
0%
0/28
2.1
Util$1
0%
0/7
N/A
2.1
 
 1  
 /*
 2  
  * Copyright 2006-2011 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  
 
 17  
 package org.kuali.rice.ken.util;
 18  
 
 19  
 import org.apache.commons.lang.StringUtils;
 20  
 import org.apache.log4j.Logger;
 21  
 import org.kuali.rice.core.api.config.property.ConfigContext;
 22  
 import org.kuali.rice.core.framework.persistence.dao.GenericDao;
 23  
 import org.kuali.rice.ken.bo.Notification;
 24  
 import org.kuali.rice.ken.bo.NotificationChannel;
 25  
 import org.kuali.rice.ken.bo.NotificationContentType;
 26  
 import org.kuali.rice.ken.bo.NotificationPriority;
 27  
 import org.kuali.rice.ken.bo.NotificationProducer;
 28  
 import org.kuali.rice.ken.bo.NotificationRecipient;
 29  
 import org.kuali.rice.ken.bo.NotificationSender;
 30  
 import org.kuali.rice.ken.service.NotificationContentTypeService;
 31  
 import org.w3c.dom.Document;
 32  
 import org.w3c.dom.Element;
 33  
 import org.w3c.dom.Node;
 34  
 import org.w3c.dom.NodeList;
 35  
 import org.xml.sax.EntityResolver;
 36  
 import org.xml.sax.ErrorHandler;
 37  
 import org.xml.sax.InputSource;
 38  
 import org.xml.sax.SAXException;
 39  
 import org.xml.sax.SAXParseException;
 40  
 
 41  
 import javax.xml.namespace.NamespaceContext;
 42  
 import javax.xml.parsers.DocumentBuilder;
 43  
 import javax.xml.parsers.DocumentBuilderFactory;
 44  
 import javax.xml.parsers.ParserConfigurationException;
 45  
 import javax.xml.transform.stream.StreamSource;
 46  
 import java.io.IOException;
 47  
 import java.sql.Timestamp;
 48  
 import java.text.DateFormat;
 49  
 import java.text.ParseException;
 50  
 import java.text.SimpleDateFormat;
 51  
 import java.util.ArrayList;
 52  
 import java.util.Collections;
 53  
 import java.util.Date;
 54  
 import java.util.HashMap;
 55  
 import java.util.Map;
 56  
 import java.util.TimeZone;
 57  
 
 58  
 /**
 59  
  * A general Utility class for the Notification system.
 60  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 61  
  */
 62  0
 public final class Util {
 63  0
     private static final Logger LOG = Logger.getLogger(Util.class);
 64  
     
 65  
     public static final java.lang.String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
 66  
     public static final java.lang.String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
 67  
 
 68  0
     public static final NamespaceContext NOTIFICATION_NAMESPACE_CONTEXT
 69  
         = new ConfiguredNamespaceContext(Collections.singletonMap("nreq", "ns:notification/NotificationRequest"));
 70  
 
 71  
     private static final String ZULU_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
 72  0
     private static final TimeZone ZULU_TZ = TimeZone.getTimeZone("UTC");
 73  
 
 74  
     private static final String CURR_TZ_FORMAT = "MM/dd/yyyy hh:mm a";
 75  
     
 76  0
         private Util() {
 77  0
                 throw new UnsupportedOperationException("do not call");
 78  
         }
 79  
 
 80  
     /**
 81  
      * @return the name of the user configured to be the Notification system user
 82  
      */
 83  
     public static String getNotificationSystemUser() {
 84  0
         String system_user = ConfigContext.getCurrentContextConfig().getProperty(NotificationConstants.KEW_CONSTANTS.NOTIFICATION_SYSTEM_USER_PARAM);
 85  0
         if (system_user == null) {
 86  0
             system_user = NotificationConstants.KEW_CONSTANTS.NOTIFICATION_SYSTEM_USER;
 87  
         }
 88  0
         return system_user;
 89  
     }
 90  
 
 91  
     /**
 92  
      * Parses a date/time string under XSD dateTime type syntax
 93  
      * @see #ZULU_FORMAT
 94  
      * @param dateTimeString an XSD dateTime-formatted String
 95  
      * @return a Date representing the time value of the String parameter 
 96  
      * @throws ParseException if an error occurs during parsing 
 97  
      */
 98  
     public static Date parseXSDDateTime(String dateTimeString) throws ParseException {
 99  0
             return createZulu().parse(dateTimeString);
 100  
     }
 101  
 
 102  
     /**
 103  
      * Formats a Date into XSD dateTime format
 104  
      * @param d the date value to format
 105  
      * @return date value formatted into XSD dateTime format
 106  
      */
 107  
     public static String toXSDDateTimeString(Date d) {
 108  0
         return createZulu().format(d);
 109  
     }
 110  
     
 111  
     /**
 112  
      * Returns the current date formatted for the UI
 113  
      * @return the current date formatted for the UI
 114  
      */
 115  
     public static String getCurrentDateTime() {
 116  0
         return toUIDateTimeString(new Date());
 117  
     }
 118  
     
 119  
     /**
 120  
      * Returns the specified date formatted for the UI
 121  
      * @return the specified date formatted for the UI
 122  
      */
 123  
     public static String toUIDateTimeString(Date d) {
 124  0
         return createCurrTz().format(d);
 125  
     }
 126  
 
 127  
     /**
 128  
      * Parses the string in UI date time format
 129  
      * @return the date parsed from UI date time format
 130  
      */
 131  
     public static Date parseUIDateTime(String s) throws ParseException {
 132  0
         return createCurrTz().parse(s);
 133  
     }
 134  
 
 135  
     /**
 136  
      * Returns a compound NamespaceContext that defers to the preconfigured notification namespace context
 137  
      * first, then delegates to the document prefix/namespace definitions second.
 138  
      * @param doc the Document to use for prefix/namespace resolution
 139  
      * @return  compound NamespaceContext
 140  
      */
 141  
     public static NamespaceContext getNotificationNamespaceContext(Document doc) {
 142  0
         return new CompoundNamespaceContext(NOTIFICATION_NAMESPACE_CONTEXT, new DocumentNamespaceContext(doc));
 143  
     }
 144  
 
 145  
     /**
 146  
      * Returns an EntityResolver to resolve XML entities (namely schema resources) in the notification system
 147  
      * @param notificationContentTypeService the NotificationContentTypeService
 148  
      * @return an EntityResolver to resolve XML entities (namely schema resources) in the notification system
 149  
      */
 150  
     public static EntityResolver getNotificationEntityResolver(NotificationContentTypeService notificationContentTypeService) {
 151  0
         return new CompoundEntityResolver(new ClassLoaderEntityResolver("schema", "notification"),
 152  
                                           new ContentTypeEntityResolver(notificationContentTypeService));
 153  
     }
 154  
 
 155  
     /**
 156  
      * transformContent - transforms xml content in notification to a string
 157  
      * using the xsl in the datastore for a given documentType
 158  
      * @param notification
 159  
      * @return
 160  
      */
 161  
     public static String transformContent(Notification notification) {
 162  0
         NotificationContentType contentType = notification.getContentType();
 163  0
         String xsl = contentType.getXsl();
 164  
         
 165  0
         LOG.debug("xsl: "+xsl);
 166  
         
 167  0
         XslSourceResolver xslresolver = new XslSourceResolver();
 168  
         //StreamSource xslsource = xslresolver.resolveXslFromFile(xslpath);
 169  0
         StreamSource xslsource = xslresolver.resolveXslFromString(xsl);
 170  0
         String content = notification.getContent();
 171  0
         LOG.debug("xslsource:"+xslsource.toString());
 172  
         
 173  0
         String contenthtml = new String();
 174  
         try {
 175  0
           ContentTransformer transformer = new ContentTransformer(xslsource);
 176  0
           contenthtml = transformer.transform(content);
 177  0
           LOG.debug("html: "+contenthtml);
 178  0
         } catch (IOException ex) {
 179  0
             LOG.error("IOException transforming document",ex);
 180  0
         } catch (Exception ex) {
 181  0
             LOG.error("Exception transforming document",ex);
 182  0
         } 
 183  0
         return contenthtml;
 184  
     }
 185  
 
 186  
     /**
 187  
      * This method uses DOM to parse the input source of XML.
 188  
      * @param source the input source
 189  
      * @param validate whether to turn on validation
 190  
      * @param namespaceAware whether to turn on namespace awareness
 191  
      * @return Document the parsed (possibly validated) document
 192  
      * @throws ParserConfigurationException
 193  
      * @throws IOException
 194  
      * @throws SAXException
 195  
      */
 196  
     public static Document parse(final InputSource source, boolean validate, boolean namespaceAware, EntityResolver entityResolver) throws ParserConfigurationException, IOException, SAXException {
 197  
         // TODO: optimize this
 198  0
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 199  0
         dbf.setValidating(validate);
 200  0
         dbf.setNamespaceAware(namespaceAware);
 201  0
         dbf.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
 202  0
         DocumentBuilder db = dbf.newDocumentBuilder();
 203  0
         if (entityResolver != null) {
 204  0
             db.setEntityResolver(entityResolver);
 205  
         }
 206  0
         db.setErrorHandler(new ErrorHandler() {
 207  
             public void warning(SAXParseException se) {
 208  0
                 LOG.warn("Warning parsing xml doc " + source, se);
 209  0
             }
 210  
             public void error(SAXParseException se) throws SAXException {
 211  0
                 LOG.error("Error parsing xml doc " + source, se);
 212  0
                 throw se;
 213  
             }
 214  
             public void fatalError(SAXParseException se) throws SAXException {
 215  0
                 LOG.error("Fatal error parsing xml doc " + source, se);
 216  0
                 throw se;
 217  
             }
 218  
         });
 219  0
         return db.parse(source);
 220  
     }
 221  
 
 222  
     /**
 223  
      * This method uses DOM to parse the input source of XML, supplying a notification-system-specific
 224  
      * entity resolver.
 225  
      * @param source the input source
 226  
      * @param validate whether to turn on validation
 227  
      * @param namespaceAware whether to turn on namespace awareness
 228  
      * @return Document the parsed (possibly validated) document
 229  
      * @throws ParserConfigurationException
 230  
      * @throws IOException
 231  
      * @throws SAXException
 232  
      */
 233  
     public static Document parseWithNotificationEntityResolver(final InputSource source, boolean validate, boolean namespaceAware, NotificationContentTypeService notificationContentTypeService) throws ParserConfigurationException, IOException, SAXException {
 234  0
         return parse(source, validate, namespaceAware, getNotificationEntityResolver(notificationContentTypeService));
 235  
     }
 236  
 
 237  
     /**
 238  
      * Returns a node child with the specified tag name of the specified parent node,
 239  
      * or null if no such child node is found. 
 240  
      * @param parent the parent node
 241  
      * @param name the name of the child node
 242  
      * @return child node if found, null otherwise
 243  
      */
 244  
     public static Element getChildElement(Node parent, String name) {
 245  0
         NodeList childList = parent.getChildNodes();
 246  0
         for (int i = 0; i < childList.getLength(); i++) {
 247  0
             Node node = childList.item(i);
 248  
             // we must test against NodeName, not just LocalName
 249  
             // LocalName seems to be null - I am guessing this is because
 250  
             // the DocumentBuilderFactory is not "namespace aware"
 251  
             // although I would have expected LocalName to default to
 252  
             // NodeName
 253  0
             if (node.getNodeType() == Node.ELEMENT_NODE
 254  
                 && (name.equals(node.getLocalName())
 255  
                    || name.equals(node.getNodeName()))) {
 256  0
                 return (Element) node;
 257  
             }
 258  
         }
 259  0
         return null;
 260  
     }
 261  
     
 262  
     /**
 263  
      * This method will clone a given Notification object, one level deep, returning a fresh new instance 
 264  
      * without any references.
 265  
      * @param notification the object to clone
 266  
      * @return Notification a fresh instance
 267  
      */
 268  
     public static final Notification cloneNotificationWithoutObjectReferences(Notification notification) {
 269  0
         Notification clone = new Notification();
 270  
         
 271  
         // handle simple data types first
 272  0
         if(notification.getCreationDateTime() != null) {
 273  0
             clone.setCreationDateTime(new Timestamp(notification.getCreationDateTime().getTime()));
 274  
         }
 275  0
         if(notification.getAutoRemoveDateTime() != null) {
 276  0
             clone.setAutoRemoveDateTime(new Timestamp(notification.getAutoRemoveDateTime().getTime()));
 277  
         }
 278  0
         clone.setContent(new String(notification.getContent()));
 279  0
         clone.setDeliveryType(new String(notification.getDeliveryType()));
 280  0
         if(notification.getId() != null) {
 281  0
             clone.setId(new Long(notification.getId()));
 282  
         }
 283  0
         clone.setProcessingFlag(new String(notification.getProcessingFlag()));
 284  0
         if(notification.getSendDateTime() != null) {
 285  0
             clone.setSendDateTime(new Timestamp(notification.getSendDateTime().getTime()));
 286  
         }
 287  
         
 288  0
         clone.setTitle(notification.getTitle());
 289  
         
 290  
         // now take care of the channel
 291  0
         NotificationChannel channel = new NotificationChannel();
 292  0
         channel.setId(new Long(notification.getChannel().getId()));
 293  0
         channel.setName(new String(notification.getChannel().getName()));
 294  0
         channel.setDescription(new String(notification.getChannel().getDescription()));
 295  0
         channel.setSubscribable(new Boolean(notification.getChannel().isSubscribable()).booleanValue());
 296  0
         clone.setChannel(channel);
 297  
         
 298  
         // handle the content type
 299  0
         NotificationContentType contentType = new NotificationContentType();
 300  0
         contentType.setId(new Long(notification.getContentType().getId()));
 301  0
         contentType.setDescription(new String(notification.getContentType().getDescription()));
 302  0
         contentType.setName(new String(notification.getContentType().getName()));
 303  0
         contentType.setNamespace(new String(notification.getContentType().getNamespace()));
 304  0
         clone.setContentType(contentType);
 305  
         
 306  
         // take care of the prioirity
 307  0
         NotificationPriority priority = new NotificationPriority();
 308  0
         priority.setDescription(new String(notification.getPriority().getDescription()));
 309  0
         priority.setId(new Long(notification.getPriority().getId()));
 310  0
         priority.setName(new String(notification.getPriority().getName()));
 311  0
         priority.setOrder(new Integer(notification.getPriority().getOrder()));
 312  0
         clone.setPriority(priority);
 313  
         
 314  
         // take care of the producer
 315  0
         NotificationProducer producer = new NotificationProducer();
 316  0
         producer.setDescription(new String(notification.getProducer().getDescription()));
 317  0
         producer.setId(new Long(notification.getProducer().getId()));
 318  0
         producer.setName(new String(notification.getProducer().getName()));
 319  0
         producer.setContactInfo(new String(notification.getProducer().getContactInfo()));
 320  0
         clone.setProducer(producer);
 321  
         
 322  
         // process the list of recipients now
 323  0
         ArrayList<NotificationRecipient> recipients = new ArrayList<NotificationRecipient>();
 324  0
         for(int i = 0; i < notification.getRecipients().size(); i++) {
 325  0
             NotificationRecipient recipient = notification.getRecipient(i);
 326  0
             NotificationRecipient cloneRecipient = new NotificationRecipient();
 327  0
             cloneRecipient.setRecipientId(new String(recipient.getRecipientId()));
 328  0
             cloneRecipient.setRecipientType(new String(recipient.getRecipientType()));
 329  
             
 330  0
             recipients.add(cloneRecipient);
 331  
         }
 332  0
         clone.setRecipients(recipients);
 333  
         
 334  
         // process the list of senders now
 335  0
         ArrayList<NotificationSender> senders = new ArrayList<NotificationSender>();
 336  0
         for(int i = 0; i < notification.getSenders().size(); i++) {
 337  0
             NotificationSender sender = notification.getSender(i);
 338  0
             NotificationSender cloneSender = new NotificationSender();
 339  0
             cloneSender.setSenderName(new String(sender.getSenderName()));
 340  
             
 341  0
             senders.add(cloneSender);
 342  
         }
 343  0
         clone.setSenders(senders);
 344  
         
 345  0
         return clone;
 346  
     }
 347  
     
 348  
     /**
 349  
      * This method generically retrieves a reference to foreign key objects that are part of the content, to get 
 350  
      * at the reference objects' pk fields so that those values can be used to store the notification with proper 
 351  
      * foreign key relationships in the database.
 352  
      * @param <T>
 353  
      * @param fieldName
 354  
      * @param keyName
 355  
      * @param keyValue
 356  
      * @param clazz
 357  
      * @param boDao
 358  
      * @return T
 359  
      * @throws IllegalArgumentException
 360  
      */
 361  
     public static <T> T retrieveFieldReference(String fieldName, String keyName, String keyValue, Class clazz, GenericDao boDao) throws IllegalArgumentException {
 362  0
         LOG.debug(fieldName + " key value: " + keyValue);
 363  0
         if (StringUtils.isBlank(keyValue)) {
 364  0
             throw new IllegalArgumentException(fieldName + " must be specified in notification");
 365  
         }
 366  0
         Map<String, Object> keys = new HashMap<String, Object>(1);
 367  0
         keys.put(keyName, keyValue);
 368  0
         T reference = (T) boDao.findByPrimaryKey(clazz, keys);
 369  0
         if (reference == null) {
 370  0
             throw new IllegalArgumentException(fieldName + " '" + keyValue + "' not found");
 371  
         }
 372  0
         return reference;
 373  
     }
 374  
 
 375  
     /** date formats are not thread safe so creating a new one each time it is needed. */
 376  
     private static DateFormat createZulu() {
 377  0
         final DateFormat df = new SimpleDateFormat(ZULU_FORMAT);
 378  0
         df.setTimeZone(ZULU_TZ);
 379  0
         return df;
 380  
     }
 381  
 
 382  
     /** date formats are not thread safe so creating a new one each time it is needed. */
 383  
     private static DateFormat createCurrTz() {
 384  0
         return new SimpleDateFormat(CURR_TZ_FORMAT);
 385  
     }
 386  
 }