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