View Javadoc

1   package com.atlassian.scheduler.quartz2;
2   
3   import org.junit.Before;
4   import org.junit.Rule;
5   import org.junit.Test;
6   import org.junit.runner.RunWith;
7   import org.mockito.Mock;
8   import org.mockito.invocation.InvocationOnMock;
9   import org.mockito.junit.MockitoJUnit;
10  import org.mockito.junit.MockitoRule;
11  import org.mockito.runners.MockitoJUnitRunner;
12  import org.mockito.stubbing.Answer;
13  import org.quartz.JobDetail;
14  import org.quartz.JobKey;
15  import org.quartz.JobPersistenceException;
16  import org.quartz.TriggerKey;
17  import org.quartz.impl.jdbcjobstore.DriverDelegate;
18  import org.quartz.impl.jdbcjobstore.NoSuchDelegateException;
19  import org.quartz.spi.ClassLoadHelper;
20  import org.quartz.spi.OperableTrigger;
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  
24  import javax.annotation.Nullable;
25  import java.sql.Connection;
26  import java.util.concurrent.atomic.AtomicInteger;
27  
28  import static java.util.Arrays.asList;
29  import static org.hamcrest.Matchers.containsString;
30  import static org.junit.Assert.assertEquals;
31  import static org.junit.Assert.assertThat;
32  import static org.junit.Assert.fail;
33  import static org.mockito.Matchers.any;
34  import static org.mockito.Matchers.eq;
35  import static org.mockito.Mockito.when;
36  import static org.quartz.JobKey.jobKey;
37  import static org.quartz.TriggerKey.triggerKey;
38  import static org.quartz.impl.jdbcjobstore.Constants.STATE_MISFIRED;
39  
40  @RunWith(MockitoJUnitRunner.class)
41  public class Quartz2HardenedJobStoreTest {
42      static final Logger LOG = LoggerFactory.getLogger(Quartz2HardenedJobStoreTest.class);
43  
44      static final JobKey JOB1_KEY = jobKey("job1name", "job1group");
45      static final JobKey JOB2_KEY = jobKey("job2name", "job2group");
46      static final TriggerKey TRIGGER1_KEY = triggerKey("trigger1name", "trigger1group");
47      static final TriggerKey TRIGGER2_KEY = triggerKey("trigger2name", "trigger2group");
48  
49      final AtomicInteger callCount = new AtomicInteger();
50      final AtomicInteger throwCount = new AtomicInteger();
51      final AtomicInteger escapeCount = new AtomicInteger();
52  
53      @Rule
54      public MockitoRule mockitoRule = MockitoJUnit.rule();
55  
56      @Mock
57      Connection conn;
58      @Mock
59      DriverDelegate delegate;
60      @Mock
61      OperableTrigger trigger1;
62      @Mock
63      OperableTrigger trigger2;
64      @Mock
65      JobDetail job1;
66      @Mock
67      JobDetail job2;
68  
69      @Before
70      public void setUp() throws Exception {
71          when(trigger1.getJobKey()).thenReturn(JOB1_KEY);
72          when(trigger2.getJobKey()).thenReturn(JOB2_KEY);
73          when(trigger1.getKey()).thenReturn(TRIGGER1_KEY);
74          when(trigger2.getKey()).thenReturn(TRIGGER2_KEY);
75  
76          when(delegate.selectTriggersForRecoveringJobs(conn)).thenReturn(asList(trigger1, trigger2));
77          when(delegate.selectJobDetail(eq(conn), eq(JOB1_KEY), any(ClassLoadHelper.class))).thenAnswer(new Answer<JobDetail>() {
78              @Override
79              public JobDetail answer(InvocationOnMock invocation) throws Throwable {
80                  throwCount.incrementAndGet();
81                  throw new ClassNotFoundException("He's DEAD, Jim!");
82              }
83          });
84          when(delegate.selectJobDetail(eq(conn), eq(JOB2_KEY), any(ClassLoadHelper.class)))
85                  .thenReturn(job2);
86      }
87  
88      @Test
89      public void testStoreTriggerThrowsExceptionOutsideOfRecovery() throws JobPersistenceException {
90          final Fixture fixture = new Fixture();
91          try {
92              fixture.storeTrigger(conn, trigger1, null, true, STATE_MISFIRED, true, true);
93              fail("Should have thrown JobPersistenceException");
94          } catch (JobPersistenceException jpe) {
95              assertThat(jpe.getMessage(), containsString("He's DEAD, Jim!"));
96              assertEquals("callCount", 1, callCount.get());
97              assertEquals("throwCount", 1, throwCount.get());
98              assertEquals("escapeCount", 1, escapeCount.get());
99          }
100     }
101 
102     @Test
103     public void testStoreTriggerDoesNotThrowExceptionFromRecovery() throws Exception {
104         final Fixture fixture = new Fixture();
105         fixture.recoverJobs();
106         assertEquals("callCount", 2, callCount.get());
107         assertEquals("throwCount", 1, throwCount.get());
108         assertEquals("escapeCount", 0, escapeCount.get());
109     }
110 
111 
112     class Fixture extends Quartz2HardenedJobStore {
113 
114         @Override
115         protected DriverDelegate getDelegate() throws NoSuchDelegateException {
116             return delegate;
117         }
118 
119         @Override
120         protected Object executeInLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {
121             return txCallback.execute(conn);
122         }
123 
124         @Override
125         protected void executeInNonManagedTXLock(
126                 final String lockName,
127                 final VoidTransactionCallback txCallback) throws JobPersistenceException {
128             txCallback.execute(conn);
129         }
130 
131         @Override
132         protected boolean jobExists(Connection conn, JobKey jobKey) throws JobPersistenceException {
133             return true;
134         }
135 
136         @Override
137         protected Logger getLog() {
138             return LOG;
139         }
140 
141         @Override
142         protected void storeTrigger(Connection conn, OperableTrigger newTrigger, @Nullable JobDetail job,
143                                     boolean replaceExisting, String state, boolean forceState, boolean recovering)
144                 throws JobPersistenceException {
145             callCount.incrementAndGet();
146             boolean ok = false;
147             try {
148                 super.storeTrigger(conn, newTrigger, job, replaceExisting, state, forceState, recovering);
149                 ok = true;
150             } finally {
151                 if (!ok) {
152                     escapeCount.incrementAndGet();
153                 }
154             }
155         }
156     }
157 }