View Javadoc

1   package com.atlassian.scheduler.quartz1;
2   
3   import org.junit.Before;
4   import org.junit.Rule;
5   import org.junit.Test;
6   import org.mockito.ArgumentCaptor;
7   import org.mockito.Captor;
8   import org.mockito.Mock;
9   import org.mockito.invocation.InvocationOnMock;
10  import org.mockito.junit.MockitoJUnit;
11  import org.mockito.junit.MockitoRule;
12  import org.mockito.stubbing.Answer;
13  import org.quartz.JobDetail;
14  import org.quartz.JobPersistenceException;
15  import org.quartz.Trigger;
16  import org.quartz.core.SchedulingContext;
17  import org.quartz.impl.jdbcjobstore.DriverDelegate;
18  import org.quartz.impl.jdbcjobstore.NoSuchDelegateException;
19  import org.quartz.spi.ClassLoadHelper;
20  import org.quartz.utils.Key;
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.List;
27  import java.util.concurrent.atomic.AtomicInteger;
28  
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.anyInt;
35  import static org.mockito.Matchers.anyLong;
36  import static org.mockito.Matchers.eq;
37  import static org.mockito.Mockito.when;
38  import static org.quartz.impl.jdbcjobstore.Constants.STATE_MISFIRED;
39  import static org.quartz.impl.jdbcjobstore.Constants.STATE_WAITING;
40  
41  public class Quartz1HardenedJobStoreTest {
42      static final Logger LOG = LoggerFactory.getLogger(Quartz1HardenedJobStoreTest.class);
43  
44      final AtomicInteger callCount = new AtomicInteger();
45      final AtomicInteger throwCount = new AtomicInteger();
46      final AtomicInteger escapeCount = new AtomicInteger();
47  
48      @Rule
49      public MockitoRule mockitoRule = MockitoJUnit.rule();
50  
51      @Mock
52      Connection conn;
53      @Mock
54      DriverDelegate delegate;
55      @Mock
56      Key key1;
57      @Mock
58      Key key2;
59      @Mock
60      Trigger trigger1;
61      @Mock
62      Trigger trigger2;
63      @Mock
64      JobDetail job1;
65      @Mock
66      JobDetail job2;
67  
68      @Captor
69      ArgumentCaptor<List<Key>> misfired;
70  
71      @Before
72      public void setUp() throws Exception {
73          when(key1.getGroup()).thenReturn("key1group");
74          when(key1.getName()).thenReturn("key1name");
75          when(key2.getGroup()).thenReturn("key2group");
76          when(key2.getName()).thenReturn("key2name");
77          when(trigger1.getJobGroup()).thenReturn("job1group");
78          when(trigger1.getJobName()).thenReturn("job1name");
79          when(trigger2.getJobGroup()).thenReturn("job2group");
80          when(trigger2.getJobName()).thenReturn("job2name");
81  
82          when(delegate.selectTriggersForRecoveringJobs(conn)).thenReturn(new Trigger[]{trigger1, trigger2});
83          when(delegate.selectJobDetail(eq(conn), eq("job1name"), eq("job1group"), any(ClassLoadHelper.class)))
84                  .thenAnswer(new Answer<JobDetail>() {
85                      @Override
86                      public JobDetail answer(InvocationOnMock invocation) throws Throwable {
87                          throwCount.incrementAndGet();
88                          throw new ClassNotFoundException("He's DEAD, Jim!");
89                      }
90                  });
91          when(delegate.selectJobDetail(eq(conn), eq("job2name"), eq("job2group"), any(ClassLoadHelper.class)))
92                  .thenReturn(job2);
93      }
94  
95      @Test
96      public void testStoreTriggerThrowsExceptionOutsideOfRecovery() throws JobPersistenceException {
97          final Fixture fixture = new Fixture();
98          try {
99              fixture.storeTrigger(conn, new SchedulingContext(), trigger1, null, true, STATE_MISFIRED, true, true);
100             fail("Should have thrown JobPersistenceException");
101         } catch (JobPersistenceException jpe) {
102             assertThat(jpe.getMessage(), containsString("He's DEAD, Jim!"));
103             assertEquals("callCount", 1, callCount.get());
104             assertEquals("throwCount", 1, throwCount.get());
105             assertEquals("escapeCount", 1, escapeCount.get());
106         }
107     }
108 
109     @Test
110     public void testStoreTriggerDoesNotThrowExceptionFromRecovery() throws Exception {
111         when(delegate.selectMisfiredTriggersInStates(
112                 eq(conn), eq(STATE_MISFIRED), eq(STATE_WAITING), anyLong(),
113                 anyInt(), misfired.capture())).thenAnswer(new Answer<Boolean>() {
114             @Override
115             public Boolean answer(InvocationOnMock invocation) throws Throwable {
116                 final List<Key> keys = misfired.getValue();
117                 keys.add(key1);
118                 keys.add(key2);
119                 return true;
120             }
121         });
122 
123         final Fixture fixture = new Fixture();
124         fixture.recoverJobs();
125         assertEquals("callCount", 2, callCount.get());
126         assertEquals("throwCount", 1, throwCount.get());
127         assertEquals("escapeCount", 0, escapeCount.get());
128     }
129 
130 
131     class Fixture extends Quartz1HardenedJobStore {
132 
133         @Override
134         protected DriverDelegate getDelegate() throws NoSuchDelegateException {
135             return delegate;
136         }
137 
138         @Override
139         protected Object executeInLock(String lockName, TransactionCallback txCallback) throws JobPersistenceException {
140             return txCallback.execute(conn);
141         }
142 
143         @Override
144         protected void executeInNonManagedTXLock(
145                 final String lockName,
146                 final VoidTransactionCallback txCallback) throws JobPersistenceException {
147             txCallback.execute(conn);
148         }
149 
150         @Override
151         protected Logger getLog() {
152             return LOG;
153         }
154 
155         @Override
156         protected boolean jobExists(Connection conn, String jobName, String groupName) throws JobPersistenceException {
157             return true;
158         }
159 
160         @Override
161         protected void storeTrigger(Connection conn, SchedulingContext ctxt, Trigger newTrigger,
162                                     @Nullable JobDetail job, boolean replaceExisting, String state, boolean forceState,
163                                     boolean recovering) throws JobPersistenceException {
164             callCount.incrementAndGet();
165             boolean ok = false;
166             try {
167                 super.storeTrigger(conn, ctxt, newTrigger, job, replaceExisting, state, forceState, recovering);
168                 ok = true;
169             } finally {
170                 if (!ok) {
171                     escapeCount.incrementAndGet();
172                 }
173             }
174         }
175     }
176 }