View Javadoc

1   /*
2    * $Id: InboundMessageLossTestCase.java 22552 2011-07-25 07:18:19Z claude.mamo $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.jdbc.reliability;
12  
13  import static org.junit.Assert.assertEquals;
14  
15  import org.mule.exception.DefaultSystemExceptionStrategy;
16  import org.mule.routing.filters.WildcardFilter;
17  import org.mule.tck.probe.PollingProber;
18  import org.mule.tck.probe.Probe;
19  import org.mule.tck.probe.Prober;
20  import org.mule.transport.jdbc.functional.AbstractJdbcFunctionalTestCase;
21  
22  import java.util.Arrays;
23  import java.util.Collection;
24  
25  import org.apache.commons.dbutils.QueryRunner;
26  import org.apache.commons.dbutils.handlers.ArrayHandler;
27  import org.junit.Test;
28  import org.junit.runners.Parameterized.Parameters;
29  
30  /**
31   * Verify that no inbound messages are lost when exceptions occur.  
32   * The message must either make it all the way to the SEDA queue (in the case of 
33   * an asynchronous inbound endpoint), or be restored/rolled back at the source.
34   * 
35   * In the case of JDBC, this will cause the ACK query to not be executed and therefore 
36   * the source data will still be present the next time the database is polled.
37   */
38  public class InboundMessageLossTestCase extends AbstractJdbcFunctionalTestCase
39  {
40      /** Polling mechanism to replace Thread.sleep() for testing a delayed result. */
41      protected Prober prober = new PollingProber(10000, 100);
42          
43      protected QueryRunner qr;
44      
45      public InboundMessageLossTestCase(ConfigVariant variant, String configResources)
46      {
47          super(variant, configResources);
48          setPopulateTestData(false);
49      }
50      
51      @Parameters
52      public static Collection<Object[]> parameters()
53      {
54          return Arrays.asList(new Object[][]{
55              {ConfigVariant.SERVICE, "reliability/jdbc-connector.xml, reliability/inbound-message-loss.xml"}            
56          });
57      }          
58  
59      @Override
60      protected void doSetUp() throws Exception
61      {
62          super.doSetUp();
63          
64          // Set SystemExceptionStrategy to redeliver messages (this can only be configured programatically for now)
65          ((DefaultSystemExceptionStrategy) muleContext.getExceptionListener()).setRollbackTxFilter(new WildcardFilter("*"));
66  
67          qr = jdbcConnector.getQueryRunner();
68      }
69  
70      @Test
71      public void testNoException() throws Exception
72      {
73          assertEquals(1, qr.update(jdbcConnector.getConnection(), 
74              "INSERT INTO TEST(TYPE, DATA, ACK, RESULT) VALUES (1, '" + TEST_MESSAGE + "', NULL, NULL)"));
75  
76          prober.check(new Probe()
77          {
78              @Override
79              public boolean isSatisfied()
80              {
81                  try
82                  {
83                      Object[] queryResult = (Object[]) qr.query(jdbcConnector.getConnection(), 
84                          "SELECT DATA FROM TEST WHERE TYPE = 1 AND ACK IS NULL", new ArrayHandler());
85                      // Delivery was successful so row should be acknowledged (marked read).
86                      return (queryResult == null);
87                  }
88                  catch (Exception e)
89                  {
90                      throw new RuntimeException(e);
91                  }
92              }
93  
94              @Override
95              public String describeFailure()
96              {
97                  return "Row should be acknowledged (marked read)";
98              }
99          });
100     }
101     
102     @Test
103     public void testTransformerException() throws Exception
104     {
105         assertEquals(1, qr.update(jdbcConnector.getConnection(), 
106             "INSERT INTO TEST(TYPE, DATA, ACK, RESULT) VALUES (2, '" + TEST_MESSAGE + "', NULL, NULL)"));
107 
108         prober.check(new Probe()
109         {
110             @Override
111             public boolean isSatisfied()
112             {
113                 try
114                 {
115                     Object[] queryResult = (Object[]) qr.query(jdbcConnector.getConnection(), 
116                         "SELECT DATA FROM TEST WHERE TYPE = 2 AND ACK IS NULL", new ArrayHandler());
117                     // Delivery failed so row should not be acknowledged (marked read).
118                     return (queryResult != null && queryResult.length == 1);
119                 }
120                 catch (Exception e)
121                 {
122                     throw new RuntimeException(e);
123                 }
124             }
125 
126             @Override
127             public String describeFailure()
128             {
129                 return "Row should not be acknowledged (marked read)";
130             }
131         });
132     }
133     
134     @Test
135     public void testRouterException() throws Exception
136     {
137         assertEquals(1, qr.update(jdbcConnector.getConnection(), 
138             "INSERT INTO TEST(TYPE, DATA, ACK, RESULT) VALUES (3, '" + TEST_MESSAGE + "', NULL, NULL)"));
139 
140         prober.check(new Probe()
141         {
142             @Override
143             public boolean isSatisfied()
144             {
145                 try
146                 {
147                     Object[] queryResult = (Object[]) qr.query(jdbcConnector.getConnection(), 
148                         "SELECT DATA FROM TEST WHERE TYPE = 3 AND ACK IS NULL", new ArrayHandler());
149                     // Delivery failed so row should not be acknowledged (marked read).
150                     return (queryResult != null && queryResult.length == 1);
151                 }
152                 catch (Exception e)
153                 {
154                     throw new RuntimeException(e);
155                 }
156             }
157 
158             @Override
159             public String describeFailure()
160             {
161                 return "Row should not be acknowledged (marked read)";
162             }
163         });
164     }
165     
166     @Test
167     public void testComponentException() throws Exception
168     {
169         assertEquals(1, qr.update(jdbcConnector.getConnection(), 
170             "INSERT INTO TEST(TYPE, DATA, ACK, RESULT) VALUES (4, '" + TEST_MESSAGE + "', NULL, NULL)"));
171 
172         prober.check(new Probe()
173         {
174             @Override
175             public boolean isSatisfied()
176             {
177                 try
178                 {
179                     Object[] queryResult = (Object[]) qr.query(jdbcConnector.getConnection(), 
180                         "SELECT DATA FROM TEST WHERE TYPE = 4 AND ACK IS NULL", new ArrayHandler());
181                     // Exception occurs after the SEDA queue for an asynchronous request, so from the client's
182                     // perspective, the message has been delivered successfully.
183                     return (queryResult == null);
184                 }
185                 catch (Exception e)
186                 {
187                     throw new RuntimeException(e);
188                 }
189             }
190 
191             @Override
192             public String describeFailure()
193             {
194                 return "Row should be acknowledged (marked read)";
195             }
196         });
197     }    
198 }