package org.myeslib.example.jdbi.routes; import java.math.BigDecimal; import java.util.List; import java.util.Map; import java.util.UUID; import lombok.RequiredArgsConstructor; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy; import org.myeslib.core.data.Snapshot; import org.myeslib.core.storage.SnapshotReader; import org.myeslib.example.SampleDomain.InventoryItemAggregateRoot; import org.myeslib.util.hazelcast.HzJobLocker; import org.myeslib.util.jdbi.ArTablesMetadata; import org.skife.jdbi.v2.DBI; import org.skife.jdbi.v2.Handle; import org.skife.jdbi.v2.tweak.HandleCallback; import org.skife.jdbi.v2.util.BigDecimalMapper; import org.skife.jdbi.v2.util.StringMapper; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.inject.Inject; @RequiredArgsConstructor(onConstructor=@__(@Inject)) public class JdbiConsumeEventsRoute extends RouteBuilder { final static String INVENTORY_ITEM_ID = "inventoryItemId"; final static String PREVIOUS_SEQ_NUMBER = "previousSeqNumber"; final static String LATEST_SEQ_NUMBER = "latestSeqNumber"; final static String HOW_MANY_UOWS_FOUND = "howManyUowFound"; final HzJobLocker jobLocker; final DBI dbi; final ArTablesMetadata tablesMetadata; final SnapshotReader<UUID, InventoryItemAggregateRoot> snapshotReader; final Map<UUID, Snapshot<InventoryItemAggregateRoot>> lastSnapshotMap; @Override public void configure() throws Exception { from("timer://consume-uow?fixedRate=true&period=60s") .routeId("timer:consume-uow") .choice() .when(method(jobLocker, "lock")) .to("direct:collectEvents") .otherwise() .log(HzJobLocker.LOCK_FAILED_MSG) .end(); from("direct:collectEvents") .routeId("direct:collectEvents") .log("Starting new job") .process(new GetPreviousSequenceNumber()) .log("previousSeqNumber for this aggregateRoot ${header.previousSeqNumber}") .process(new GetLatestSeqNumber()) .log("latestSeqNumber for this aggregateRoot ${header.latestSeqNumber}") .process(new GetIdsFromInterval()) .choice() .when(header(HOW_MANY_UOWS_FOUND).isGreaterThan(0)) .log("found ${header.howManyUowFound} events to synchronize") .to("direct:sincronizeNewEvents") .otherwise() .log("did not found anything to synchronize") .end() ; from("direct:sincronizeNewEvents") .split(body()).shareUnitOfWork() .streaming().parallelProcessing() .process(new Processor() { @Override public void process(Exchange e) throws Exception { UUID id = e.getIn().getBody(UUID.class); Snapshot<InventoryItemAggregateRoot> snapshot = snapshotReader.get(id); e.getOut().setHeaders(e.getIn().getHeaders()); e.getOut().setHeader(INVENTORY_ITEM_ID, id); e.getOut().setBody(snapshot); } }) .wireTap("direct:reflect-last-snapshot") .wireTap("direct:reflect-query-model") .setBody(header(LATEST_SEQ_NUMBER)) .aggregate(constant(true), new UseLatestAggregationStrategy()).completionSize(header(HOW_MANY_UOWS_FOUND)) .log("now let's update the last sequence synchronized") .process(new UpdatePreviousSequenceNumber()) ; from("direct:reflect-last-snapshot") .routeId("direct:reflect-last-snapshot") //.log("updating the last snapshot map") .process(new Processor() { @Override public void process(Exchange e) throws Exception { @SuppressWarnings("unchecked") Snapshot<InventoryItemAggregateRoot> snapshot = e.getIn().getBody(Snapshot.class); lastSnapshotMap.put(header(INVENTORY_ITEM_ID).evaluate(e, UUID.class), snapshot); } }) ; from("direct:reflect-query-model") .routeId("direct:reflect-query-model") //.log("updating the query model on database") .process(new Processor() { @Override public void process(Exchange e) throws Exception { // TODO.. update query model on database } }); } class GetPreviousSequenceNumber implements Processor { @Override public void process(Exchange e) throws Exception { final BigDecimal previousSeqNumber = dbi.withHandle(new HandleCallback<BigDecimal>() { @Override public BigDecimal withHandle(Handle handle) throws Exception { String sqlGetLastSeqNumber = "select last_seq_number from query_model_sync where aggregateRootName = :aggregateRootName"; log.debug(sqlGetLastSeqNumber); return handle.createQuery(sqlGetLastSeqNumber) .bind("aggregateRootName", tablesMetadata.getAggregateRootName()) .map(BigDecimalMapper.FIRST) .first(); } }); e.getOut().setHeader(PREVIOUS_SEQ_NUMBER, previousSeqNumber == null ? 0 : previousSeqNumber); } } class GetLatestSeqNumber implements Processor { @Override public void process(Exchange e) throws Exception { final BigDecimal previousSeqNumber = e.getIn().getHeader(PREVIOUS_SEQ_NUMBER, BigDecimal.class); final BigDecimal latestSeqNumber = dbi.withHandle(new HandleCallback<BigDecimal>() { @Override public BigDecimal withHandle(Handle handle) throws Exception { String sqlGetIdsSinceLastSeqNumber = String.format("select max(seq_number) from %s where seq_number > :previous_seq_number", tablesMetadata.getUnitOfWorkTable()); log.debug(sqlGetIdsSinceLastSeqNumber); return handle.createQuery(sqlGetIdsSinceLastSeqNumber) .bind("previous_seq_number", previousSeqNumber) .map(BigDecimalMapper.FIRST) .first(); } }); e.getOut().setHeader(PREVIOUS_SEQ_NUMBER, previousSeqNumber); e.getOut().setHeader(LATEST_SEQ_NUMBER, latestSeqNumber == null ? previousSeqNumber : latestSeqNumber); } } class GetIdsFromInterval implements Processor { @Override public void process(Exchange e) throws Exception { final BigDecimal previousSeqNumber = e.getIn().getHeader(PREVIOUS_SEQ_NUMBER, BigDecimal.class); final BigDecimal latestSeqNumber = e.getIn().getHeader(LATEST_SEQ_NUMBER, BigDecimal.class); List<String> ids = dbi.withHandle(new HandleCallback<List<String>>() { @Override public List<String> withHandle(Handle handle) throws Exception { String sqlGetIdsSinceLastSeqNumber = String.format("select distinct id from %s where seq_number between :previous_seq_number +1 and :latest_seq_number", tablesMetadata.getUnitOfWorkTable()); log.debug(sqlGetIdsSinceLastSeqNumber); return handle.createQuery(sqlGetIdsSinceLastSeqNumber) .bind("previous_seq_number", previousSeqNumber) .bind("latest_seq_number", latestSeqNumber) .map(StringMapper.FIRST) .list(); } }); List<UUID> uuids = Lists.transform(ids, new Function<String, UUID>() { @Override public UUID apply(String input) { return UUID.fromString((String) input); } }); e.getOut().setBody(uuids, List.class); e.getOut().setHeader(HOW_MANY_UOWS_FOUND, ids.size()); e.getOut().setHeader(LATEST_SEQ_NUMBER, latestSeqNumber); } } class UpdatePreviousSequenceNumber implements Processor { @Override public void process(Exchange e) throws Exception { final BigDecimal lastSeqNumber = e.getIn().getHeader(LATEST_SEQ_NUMBER, BigDecimal.class); int ok = dbi.withHandle(new HandleCallback<Integer>() { @Override public Integer withHandle(Handle handle) throws Exception { String getLastUpdate = "update query_model_sync set last_seq_number = :last_seq_number where aggregateRootName = :aggregateRootName"; log.debug(getLastUpdate); return handle.createStatement(getLastUpdate) .bind("last_seq_number", lastSeqNumber) .bind("aggregateRootName", tablesMetadata.getAggregateRootName()) .execute(); } }); log.info("updated previous sequence ok = {}", ok); } } }