View Javadoc
1   /**
2    * Copyright 2005-2014 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.kew.routeheader;
17  
18  import org.junit.Test;
19  import org.kuali.rice.kew.api.KewApiConstants;
20  import org.kuali.rice.kew.api.WorkflowDocument;
21  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
22  import org.kuali.rice.kew.doctype.bo.DocumentType;
23  import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
24  import org.kuali.rice.kew.service.KEWServiceLocator;
25  import org.kuali.rice.kew.test.KEWTestCase;
26  import org.kuali.rice.test.BaselineTestCase;
27  import org.springframework.transaction.TransactionStatus;
28  import org.springframework.transaction.support.TransactionCallback;
29  
30  import java.sql.Timestamp;
31  import java.util.concurrent.CountDownLatch;
32  import java.util.concurrent.TimeUnit;
33  
34  import static org.junit.Assert.*;
35  
36  @BaselineTestCase.BaselineMode(BaselineTestCase.Mode.NONE)
37  public class RouteHeaderServiceTest extends KEWTestCase {
38  
39      private RouteHeaderService routeHeaderService;
40  
41      protected void setUpAfterDataLoad() throws Exception {
42          super.setUpAfterDataLoad();
43          routeHeaderService = KEWServiceLocator.getRouteHeaderService();
44      }
45      
46      /**
47       * Tests the saving of a document with large XML content.  This verifies that large CLOBs (> 4000 bytes)
48       * can be saved by OJB.  This can cause paticular issues with Oracle and OJB has to unwrap the native jdbc
49       * Connections and Statements from the pooled connection.  We need to make sure this is working for our
50       * pooling software of choice.
51       */
52      @Test
53      public void testLargeDocumentContent() throws Exception {
54          StringBuffer buffer = new StringBuffer();
55          buffer.append("<content>");
56          for (int index = 0; index < 10000; index++) {
57              buffer.append("abcdefghijklmnopqrstuvwxyz");
58          }
59          buffer.append("</content>");
60          DocumentRouteHeaderValue document = new DocumentRouteHeaderValue();
61          document.setDocContent(buffer.toString());
62          document.setDocRouteStatus(KewApiConstants.ROUTE_HEADER_INITIATED_CD);
63          document.setDocRouteLevel(0);
64          document.setDateModified(new Timestamp(System.currentTimeMillis()));
65          document.setCreateDate(new Timestamp(System.currentTimeMillis()));
66          document.setInitiatorWorkflowId("1");
67          DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName("TestDocumentType");
68          assertNotNull(documentType);
69          document.setDocumentTypeId(documentType.getDocumentTypeId());
70          document = routeHeaderService.saveRouteHeader(document);
71          assertNotNull("Document was saved, it should have an ID now.", document.getDocumentId());
72          
73          // now reload from database and verify it's the right size
74          document = routeHeaderService.getRouteHeader(document.getDocumentId());
75          String docContent = document.getDocContent();
76          assertEquals("Doc content should be the same size as original string buffer.", buffer.length(), docContent.length());
77          assertTrue("Should be greater than about 5000 bytes.", docContent.getBytes().length > 5000);
78      }
79  
80      @Test public void testGetApplicationIdByDocumentId() throws Exception {
81      	WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "TestDocumentType2");
82      	String documentId = document.getDocumentId();
83      	String applicationId = routeHeaderService.getApplicationIdByDocumentId(documentId);
84      	assertEquals("applicationId should be KEWNEW", "KEWNEW", applicationId);
85  
86      	// now check TestDocumentType
87      	document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "TestDocumentType");
88      	documentId = document.getDocumentId();
89      	applicationId = routeHeaderService.getApplicationIdByDocumentId(documentId);
90      	assertEquals("applicationId should be KUALI", "KUALI", applicationId);
91      }
92  
93      @Test public void testLockRouteHeader() throws Exception {
94  
95          long timeout = 60 * 1000;
96  
97      	WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("rkirkend"), "TestDocumentType");
98      	document.saveDocumentData();
99      	String documentId = document.getDocumentId();
100 
101         final Locker locker1 = new Locker(documentId);
102         locker1.start();
103         locker1.latch1.await(timeout, TimeUnit.MILLISECONDS);
104 
105         // the locker show now be waiting on the second latch
106         assertTrue(locker1.waiting);
107         assertFalse(locker1.completed);
108 
109         // now start a second locker thread to attempt to lock the document as well, it should end up getting blocked
110         // from locking
111         final Locker locker2 = new Locker(documentId);
112         locker2.start();
113 
114         // the thread has been started, let's give it a little bit of time to attempt to lock the doc, it should get
115         // blocked in the select ... for update
116         Thread.sleep(2000);
117 
118         // this locker should essentially be blocked on the call to lock the route header
119         assertTrue(locker2.prelock);
120         assertFalse(locker2.waiting);
121 
122         // now, release the first lock
123         locker1.latch2.countDown();
124         locker1.join(timeout);
125 
126         // at this point locker1 should have completed
127         assertTrue(locker1.completed);
128 
129         // give locker2 a little bit of time to finish it's lock on the route header and proceed to it's wait
130         Thread.sleep(2000);
131         locker2.latch2.countDown();
132         locker2.join(timeout);
133 
134         // locker 2 should be completed as well
135         assertTrue(locker2.completed);
136 
137     }
138 
139     private class Locker extends Thread {
140 
141         private static final long TIMEOUT = 60 * 1000;
142 
143         String documentId;
144         CountDownLatch latch1;
145         CountDownLatch latch2;
146 
147         volatile boolean prelock;
148         volatile boolean waiting;
149         volatile boolean completed;
150 
151         Locker(String documentId) {
152             this.documentId = documentId;
153             this.latch1 = new CountDownLatch(1);
154             this.latch2 = new CountDownLatch(1);
155         }
156 
157         public void run() {
158             getTransactionTemplate().execute(new TransactionCallback() {
159                 public Object doInTransaction(TransactionStatus status) {
160                     prelock = true;
161                     routeHeaderService.lockRouteHeader(documentId);
162                     try {
163                         waiting = true;
164                         latch1.countDown();
165                         latch2.await(TIMEOUT, TimeUnit.MILLISECONDS);
166                     } catch (InterruptedException e) {
167                          throw new RuntimeException("Shouldn't have been interrupted but was.", e);
168                     }
169                     return null;
170                 }
171             });
172             completed = true;
173         }
174     }
175 
176 }