Coverage Report - org.kuali.rice.kew.edl.EDocLitePostProcessor
 
Classes in this File Line Coverage Branch Coverage Complexity
EDocLitePostProcessor
0%
0/138
0%
0/24
3.077
EDocLitePostProcessor$1
0%
0/8
N/A
3.077
EDocLitePostProcessor$2
0%
0/3
N/A
3.077
 
 1  
 /*
 2  
  * Copyright 2005-2007 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  
 package org.kuali.rice.kew.edl;
 18  
 
 19  
 import java.io.ByteArrayOutputStream;
 20  
 import java.io.IOException;
 21  
 import java.io.InputStream;
 22  
 import java.io.InterruptedIOException;
 23  
 import java.io.OutputStream;
 24  
 import java.io.StringReader;
 25  
 import java.lang.reflect.Method;
 26  
 import java.net.Socket;
 27  
 import java.net.URL;
 28  
 import java.util.Timer;
 29  
 import java.util.TimerTask;
 30  
 
 31  
 import javax.xml.parsers.DocumentBuilder;
 32  
 import javax.xml.parsers.DocumentBuilderFactory;
 33  
 import javax.xml.transform.TransformerException;
 34  
 import javax.xml.xpath.XPath;
 35  
 import javax.xml.xpath.XPathConstants;
 36  
 import javax.xml.xpath.XPathExpressionException;
 37  
 import javax.xml.xpath.XPathFactory;
 38  
 
 39  
 import org.apache.log4j.Logger;
 40  
 import org.kuali.rice.kew.postprocessor.ActionTakenEvent;
 41  
 import org.kuali.rice.kew.postprocessor.DefaultPostProcessor;
 42  
 import org.kuali.rice.kew.postprocessor.DeleteEvent;
 43  
 import org.kuali.rice.kew.postprocessor.DocumentRouteLevelChange;
 44  
 import org.kuali.rice.kew.postprocessor.DocumentRouteStatusChange;
 45  
 import org.kuali.rice.kew.postprocessor.ProcessDocReport;
 46  
 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
 47  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 48  
 import org.kuali.rice.kew.util.Utilities;
 49  
 import org.kuali.rice.kew.util.XmlHelper;
 50  
 import org.w3c.dom.Document;
 51  
 import org.w3c.dom.Element;
 52  
 import org.xml.sax.InputSource;
 53  
 
 54  
 
 55  
 /**
 56  
  * PostProcessor responsible for posting events to a url defined in the EDL doc definition.
 57  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 58  
  */
 59  0
 public class EDocLitePostProcessor extends DefaultPostProcessor {
 60  0
     private static final Logger LOG = Logger.getLogger(EDocLitePostProcessor.class);
 61  0
     private static final Timer TIMER = new Timer();
 62  
     public static final int SUBMIT_URL_MILLISECONDS_WAIT = 60000;
 63  
     public static final String EVENT_TYPE_ACTION_TAKEN = "actionTaken";
 64  
     public static final String EVENT_TYPE_DELETE_ROUTE_HEADER = "deleteRouteHeader";
 65  
     public static final String EVENT_TYPE_ROUTE_LEVEL_CHANGE = "routeLevelChange";
 66  
     public static final String EVENT_TYPE_ROUTE_STATUS_CHANGE = "statusChange";
 67  
 
 68  
     private static String getURL(Document edlDoc) throws XPathExpressionException {
 69  0
         XPath xpath = XPathFactory.newInstance().newXPath();
 70  0
         return (String) xpath.evaluate("//edlContent/edl/eventNotificationURL", edlDoc, XPathConstants.STRING);
 71  
     }
 72  
 
 73  
     /**
 74  
      * @param urlstring
 75  
      * @param eventDoc
 76  
      */
 77  
     private static void submitURL(String urlstring, Document eventDoc) throws IOException, TransformerException {
 78  
         String content;
 79  
         try {
 80  0
             content = XmlHelper.writeNode(eventDoc, true);
 81  0
         } catch (TransformerException te) {
 82  0
             LOG.error("Error writing serializing event doc: " + eventDoc);
 83  0
             throw te;
 84  0
         }
 85  0
         byte[] contentBytes = content.getBytes("UTF-8");
 86  
 
 87  0
         LOG.debug("submitURL: " + urlstring);
 88  0
         URL url = new URL(urlstring);
 89  
 
 90  0
         String message = "POST " + url.getFile() + " HTTP/1.0\r\n" +
 91  
                          "Content-Length: " + contentBytes.length + "\r\n" +
 92  
                          "Cache-Control: no-cache\r\n" +
 93  
                          "Pragma: no-cache\r\n" +
 94  
                          "User-Agent: Java/1.4.2; EDocLitePostProcessor\r\n" +
 95  
                          "Host: " + url.getHost() + "\r\n" +
 96  
                          "Connection: close\r\n" +
 97  
                          "Content-Type: application/x-www-form-urlencoded\r\n\r\n" +
 98  
                          content;
 99  
 
 100  0
         byte[] buf = message.getBytes("UTF-8");
 101  0
         Socket s = new Socket(url.getHost(), url.getPort());
 102  
 
 103  
         /*URLConnection con = url.openConnection();
 104  
         LOG.debug("got connection: " + con);
 105  
         con.setDoOutput(true);
 106  
         con.setDoInput(true);
 107  
         LOG.debug("setDoOutput(true)");
 108  
 
 109  
         con.setRequestProperty("Connection", "close");
 110  
         con.setRequestProperty("Content-Length", String.valueOf(buf.length));*/
 111  
 
 112  0
         OutputStream os = s.getOutputStream();
 113  
         try {
 114  
             try {
 115  0
                 os.write(buf, 0, buf.length);
 116  0
                 os.flush();
 117  0
             } catch (InterruptedIOException ioe) {
 118  0
                 LOG.error("IO was interrupted while posting event to url " + urlstring + ": " + ioe.getMessage());
 119  0
             } catch (IOException ioe) {
 120  0
                 LOG.error("Error posting EDocLite content to url " + urlstring + ioe.getMessage());
 121  0
             } finally {
 122  0
                 try {
 123  0
                     LOG.debug("Shutting down output stream");
 124  0
                     s.shutdownOutput();
 125  0
                 } catch (IOException ioe) {
 126  0
                     LOG.error("Error shutting down output stream for url " + urlstring + ": " + ioe.getMessage());
 127  0
                 }
 128  0
             }
 129  
 
 130  0
             InputStream is = s.getInputStream();
 131  
             try {
 132  
 
 133  0
                 buf = new byte[1024];
 134  0
                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 135  
                 // this is what actually forces the write on the URLConnection!
 136  0
                 int read = is.read(buf);
 137  0
                 if (read != -1) {
 138  0
                     baos.write(buf, 0, read);
 139  
                 }
 140  0
                 LOG.debug("EDocLite post processor response:\n" + new String(baos.toByteArray()));
 141  0
             } catch (InterruptedIOException ioe) {
 142  0
                 LOG.error("IO was interrupted while reading response from url " + urlstring + ": " + ioe.getMessage());
 143  0
             } catch (IOException ioe) {
 144  0
                 LOG.error("Error reading response from EDocLite handler url " + urlstring + ioe.getMessage());
 145  0
             } finally {
 146  0
                 try {
 147  0
                     LOG.debug("Shutting down input stream");
 148  0
                     s.shutdownInput();
 149  0
                 } catch (IOException ioe) {
 150  0
                     LOG.error("Error shutting down input stream for url " + urlstring + ": " + ioe.getMessage());
 151  0
                 }
 152  0
             }
 153  0
         } finally {
 154  0
             try {
 155  0
                 s.close();
 156  0
             } catch (IOException ioe) {
 157  0
                 LOG.error("Error closing socket", ioe);
 158  0
             }
 159  0
         }
 160  0
     }
 161  
 
 162  
     protected static void postEvent(Long docId, Object event, String eventName) throws Exception {
 163  0
         DocumentRouteHeaderValue val = KEWServiceLocator.getRouteHeaderService().getRouteHeader(docId);
 164  0
         Document doc = getEDLContent(val);
 165  0
         if(LOG.isDebugEnabled()){
 166  0
                 LOG.debug("Submitting doc: " + XmlHelper.jotNode(doc));
 167  
         }
 168  
 
 169  0
         String urlstring = getURL(doc);
 170  0
         if (Utilities.isEmpty(urlstring)) {
 171  0
             LOG.warn("No eventNotificationURL defined in EDLContent");
 172  0
             return;
 173  
         }
 174  
 
 175  0
         Document eventDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
 176  0
         Element eventE = eventDoc.createElement("event");
 177  0
         eventE.setAttribute("type", eventName);
 178  0
         eventDoc.appendChild(eventE);
 179  
 
 180  0
         Element infoE = (Element) eventDoc.importNode(propertiesToXml(event, "info"), true);
 181  0
         Element docIdE = eventDoc.createElement("docId");
 182  0
         docIdE.appendChild(eventDoc.createTextNode(String.valueOf(docId)));
 183  0
         infoE.appendChild(docIdE);
 184  
 
 185  0
         eventE.appendChild(infoE);
 186  0
         eventE.appendChild(eventDoc.importNode(doc.getDocumentElement(), true));
 187  
 
 188  0
         String query = "docId=" + docId;
 189  0
         if (urlstring.indexOf('?') != -1) {
 190  0
             urlstring += "&" + query;
 191  
         } else {
 192  0
             urlstring += "?" + query;
 193  
         }
 194  
 
 195  0
         final String _urlstring = urlstring;
 196  0
         final Document _eventDoc = eventDoc;
 197  
         // a super cheesy way to enforce asynchronicity/timeout follows:
 198  0
         final Thread t = new Thread(new Runnable() {
 199  
             public void run() {
 200  
                 try {
 201  0
                     LOG.debug("Post Event calling url: " + _urlstring);
 202  0
                     submitURL(_urlstring, _eventDoc);
 203  0
                     LOG.debug("Post Event done calling url: " + _urlstring);
 204  0
                 } catch (Exception e) {
 205  0
                     LOG.error(e);
 206  0
                 }
 207  0
             }
 208  
         });
 209  0
         t.setDaemon(true);
 210  0
         t.start();
 211  
 
 212  
         // kill the submission thread if it hasn't completed after 1 minute
 213  0
         TIMER.schedule(new TimerTask() {
 214  
             public void run() {
 215  0
                 t.interrupt();
 216  0
             }
 217  
         }, SUBMIT_URL_MILLISECONDS_WAIT);
 218  0
     }
 219  
 
 220  
     public ProcessDocReport doRouteStatusChange(DocumentRouteStatusChange event) throws Exception {
 221  0
         LOG.debug("doRouteStatusChange: " + event);
 222  0
         postEvent(event.getRouteHeaderId(), event, EVENT_TYPE_ROUTE_STATUS_CHANGE);
 223  0
         return super.doRouteStatusChange(event);
 224  
     }
 225  
 
 226  
     public ProcessDocReport doActionTaken(ActionTakenEvent event) throws Exception {
 227  0
         LOG.debug("doActionTaken: " + event);
 228  0
         postEvent(event.getRouteHeaderId(), event, EVENT_TYPE_ACTION_TAKEN);
 229  0
         return super.doActionTaken(event);
 230  
     }
 231  
 
 232  
     public ProcessDocReport doDeleteRouteHeader(DeleteEvent event) throws Exception {
 233  0
         LOG.debug("doDeleteRouteHeader: " + event);
 234  0
         postEvent(event.getRouteHeaderId(), event, EVENT_TYPE_DELETE_ROUTE_HEADER);
 235  0
         return super.doDeleteRouteHeader(event);
 236  
     }
 237  
 
 238  
     public ProcessDocReport doRouteLevelChange(DocumentRouteLevelChange event) throws Exception {
 239  0
         LOG.debug("doRouteLevelChange: " + event);
 240  0
         postEvent(event.getRouteHeaderId(), event, EVENT_TYPE_ROUTE_LEVEL_CHANGE);
 241  0
         return super.doRouteLevelChange(event);
 242  
     }
 243  
 
 244  
     public static Document getEDLContent(DocumentRouteHeaderValue routeHeader) throws Exception {
 245  0
         String content = routeHeader.getDocContent();
 246  0
         Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(content)));
 247  0
         return doc;
 248  
     }
 249  
 
 250  
     public static DocumentBuilder getDocumentBuilder() throws Exception {
 251  0
         return DocumentBuilderFactory.newInstance().newDocumentBuilder();
 252  
     }
 253  
 
 254  
     private static String lowerCaseFirstChar(String s) {
 255  0
         if (s.length() == 0 || Character.isLowerCase(s.charAt(0)))
 256  0
             return s;
 257  0
         StringBuffer sb = new StringBuffer(s.length());
 258  0
         sb.append(Character.toLowerCase(s.charAt(0)));
 259  0
         if (s.length() > 1) {
 260  0
             sb.append(s.substring(1));
 261  
         }
 262  0
         return sb.toString();
 263  
     }
 264  
 
 265  
     public static Element propertiesToXml(Object o, String elementName) throws Exception {
 266  0
         Class c = o.getClass();
 267  0
         Document doc = getDocumentBuilder().newDocument();
 268  0
         Element wrapper = doc.createElement(elementName);
 269  0
         Method[] methods = c.getMethods();
 270  0
         for (int i = 0; i < methods.length; i++) {
 271  0
             String name = methods[i].getName();
 272  0
             if ("getClass".equals(name))
 273  0
                 continue;
 274  0
             if (!name.startsWith("get") || methods[i].getParameterTypes().length > 0)
 275  0
                 continue;
 276  0
             name = name.substring("get".length());
 277  0
             name = lowerCaseFirstChar(name);
 278  0
             String value = null;
 279  
             try {
 280  0
                 Object result = methods[i].invoke(o, null);
 281  0
                 if (result == null) {
 282  0
                     LOG.debug("value of " + name + " method on object " + o.getClass() + " is null");
 283  0
                     value = "";
 284  
                 } else {
 285  0
                     value = result.toString();
 286  
                 }
 287  0
                 Element fieldE = doc.createElement(name);
 288  0
                 fieldE.appendChild(doc.createTextNode(value));
 289  0
                 wrapper.appendChild(fieldE);
 290  0
             } catch (RuntimeException e) {
 291  0
                 LOG.error("Error accessing method '" + methods[i].getName() + " of instance of " + c);
 292  0
                 throw e;
 293  0
             } catch (Exception e) {
 294  0
                 LOG.error("Error accessing method '" + methods[i].getName() + " of instance of " + c);
 295  0
             }
 296  
         }
 297  0
         return wrapper;
 298  
     }
 299  
 }