/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Other licenses: * ----------------------------------------------------------------------------- * Commercial licenses for this work are available. These replace the above * ASL 2.0 and offer limited warranties, support, maintenance, and commercial * database integrations. * * For more information, please visit: http://www.jooq.org/licenses * * * * * * * * * * * * * */ package org.jooq.example; import static java.util.stream.Collectors.toList; import static java.util.stream.IntStream.range; import static org.jooq.example.db.oracle.sp.Queues.NEW_AUTHOR_AQ; // ... // ... import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.math.BigDecimal; import java.sql.SQLException; import java.util.List; import java.util.concurrent.CompletionException; import org.jooq.example.db.oracle.sp.udt.records.AuthorTRecord; import org.jooq.example.db.oracle.sp.udt.records.BookTRecord; import org.jooq.example.db.oracle.sp.udt.records.BooksTRecord; import org.jooq.exception.DataAccessException; import org.jooq.impl.DSL; // ... // ... // ... // ... import org.junit.Before; import org.junit.Test; /** * @author Lukas Eder */ public class OracleAQExamples extends Utils { // Generate 10 authors static final List<AuthorTRecord> authors = range(0, 10).mapToObj(i -> new AuthorTRecord(i, "F" + i, "L1" + i, i == 0 ? null : range(1, i).mapToObj(j -> new BookTRecord(j, "T" + j, "DE")).collect( () -> new BooksTRecord(), (l, b) -> l.add(b), (l1, l2) -> l1.addAll(l2) ))) .collect(toList()); @Before public void setup() throws Exception { try { // Dequeue them again DEQUEUE_OPTIONS_T deq = new DEQUEUE_OPTIONS_T().wait(NO_WAIT); // Empty the queue in case failing tests leave messages in the queue for (int i = 0; i < 99; i++) DBMS_AQ.dequeue(dsl.configuration(), NEW_AUTHOR_AQ, deq); } catch (Exception ignore) {} } @Test public void testAQSimple() throws Exception { dsl.transaction(c -> { // Enqueue all authors authors.stream().forEach(a -> { DBMS_AQ.enqueue(dsl.configuration(), NEW_AUTHOR_AQ, a); }); // Dequeue them again authors.stream().forEach(a -> { assertEquals(a, DBMS_AQ.dequeue(dsl.configuration(), NEW_AUTHOR_AQ)); }); }); } @Test public void testAQStream() throws Exception { dsl.transaction(c -> { // Enqueue all authors authors.stream().forEach(a -> { DBMS_AQ.enqueue(dsl.configuration(), NEW_AUTHOR_AQ, a); }); // Dequeue some of them again List<AuthorTRecord> l1 = DBMS_AQ.dequeueStream(dsl.configuration(), NEW_AUTHOR_AQ) .limit(2) .collect(toList()); assertEquals(authors.subList(0, 2), l1); List<AuthorTRecord> l2 = DBMS_AQ.dequeueStream(dsl.configuration(), NEW_AUTHOR_AQ) .limit(authors.size() - 2) .collect(toList()); assertEquals(authors.subList(2, authors.size()), l2); }); } @Test public void testAQIterable() throws Exception { dsl.transaction(c -> { // Enqueue all authors authors.stream().forEach(a -> { DBMS_AQ.enqueue(dsl.configuration(), NEW_AUTHOR_AQ, a); }); // Dequeue them again int i = 0; for (AuthorTRecord author : DBMS_AQ.dequeueIterable(dsl.configuration(), NEW_AUTHOR_AQ)) { assertEquals(authors.get(i++), author); if (i == authors.size()) break; } }); } @Test public void testAQAsync() throws Exception { dsl.transaction(c -> { // Enqueue all authors authors.stream().forEach(a -> { DBMS_AQ.enqueue(dsl.configuration(), NEW_AUTHOR_AQ, a); }); DEQUEUE_OPTIONS_T options = new DEQUEUE_OPTIONS_T().wait(NO_WAIT); // Dequeue some of them again assertNull( DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ) .thenCompose(a -> { assertEquals(authors.get(0), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(1), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(2), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(3), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(4), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(5), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(6), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(7), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(8), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .thenCompose(a -> { assertEquals(authors.get(9), a); return DBMS_AQ.dequeueAsync(dsl.configuration(), NEW_AUTHOR_AQ, options); }) .toCompletableFuture() .exceptionally(t -> { assertTrue(t instanceof CompletionException); assertTrue(t.getCause() instanceof DataAccessException); assertTrue(t.getCause().getCause() instanceof SQLException); assertTrue(t.getCause().getCause().getMessage().contains("ORA-25228")); return null; }) .join()); }); } @Test public void testAQWait() throws Exception { dsl.transaction(c -> { long time = System.nanoTime(); try { DBMS_AQ.dequeue(c, NEW_AUTHOR_AQ, new DEQUEUE_OPTIONS_T().wait(2)); fail(); } catch (DataAccessException expected) {} // "close enough" assertTrue(System.nanoTime() - time > 1_900_000_000L); }); } @Test public void testAQOptions() throws Exception { dsl.transaction(c -> { MESSAGE_PROPERTIES_T props = new MESSAGE_PROPERTIES_T(); ENQUEUE_OPTIONS_T enq = new ENQUEUE_OPTIONS_T().visibility(IMMEDIATE); // Enqueue two authors DBMS_AQ.enqueue(c, NEW_AUTHOR_AQ, authors.get(0), enq, props.correlation("A") .delay(BigDecimal.ZERO) ); DBMS_AQ.enqueue(c, NEW_AUTHOR_AQ, authors.get(1), enq, props.correlation("B") .delay(BigDecimal.ZERO) ); // Dequeue them again DEQUEUE_OPTIONS_T deq = new DEQUEUE_OPTIONS_T(); assertEquals(authors.get(0), DBMS_AQ.dequeue(c, NEW_AUTHOR_AQ, deq.wait(NO_WAIT), props)); assertEquals(0, (int) props.attempts); assertEquals("A", props.correlation); assertEquals(BigDecimal.ZERO, props.delay); assertEquals(authors.get(1), DBMS_AQ.dequeue(c, NEW_AUTHOR_AQ, deq.wait(NO_WAIT), props)); assertEquals(0, (int) props.attempts); assertEquals("B", props.correlation); assertEquals(BigDecimal.ZERO, props.delay); // The queue is empty, this should fail assertThrows(DataAccessException.class, () -> { DBMS_AQ.dequeue(c, NEW_AUTHOR_AQ, deq, props); }); }); } @Test public void testAQTransactions() throws Exception { dsl.transaction(c1 -> { // Enqueue an author DBMS_AQ.enqueue(c1, NEW_AUTHOR_AQ, authors.get(0)); // This nested transaction is rolled back to its savepoint assertThrows(RuntimeException.class, () -> { DSL.using(c1).transaction(c2 -> { DBMS_AQ.enqueue(c2, NEW_AUTHOR_AQ, authors.get(1)); throw new RuntimeException(); }); }); // Dequeue the first author MESSAGE_PROPERTIES_T props = new MESSAGE_PROPERTIES_T(); DEQUEUE_OPTIONS_T deq = new DEQUEUE_OPTIONS_T().wait(NO_WAIT); assertEquals(authors.get(0), DBMS_AQ.dequeue(c1, NEW_AUTHOR_AQ, deq, props)); // The queue is empty (due to the rollback), this should fail assertThrows(DataAccessException.class, () -> { DBMS_AQ.dequeue(c1, NEW_AUTHOR_AQ, deq, props); }); }); } }