001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.processor.idempotent.jdbc;
018
019 import java.sql.Timestamp;
020
021 import javax.sql.DataSource;
022
023 import org.apache.camel.impl.ServiceSupport;
024 import org.apache.camel.spi.IdempotentRepository;
025 import org.springframework.jdbc.core.JdbcTemplate;
026 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
027 import org.springframework.jmx.export.annotation.ManagedAttribute;
028 import org.springframework.jmx.export.annotation.ManagedOperation;
029 import org.springframework.jmx.export.annotation.ManagedResource;
030 import org.springframework.transaction.TransactionDefinition;
031 import org.springframework.transaction.TransactionStatus;
032 import org.springframework.transaction.support.TransactionCallback;
033 import org.springframework.transaction.support.TransactionTemplate;
034
035 /**
036 * @version
037 */
038 @ManagedResource("JdbcMessageIdRepository")
039 public class JdbcMessageIdRepository extends ServiceSupport implements IdempotentRepository<String> {
040
041 protected static final String QUERY_STRING = "SELECT COUNT(*) FROM CAMEL_MESSAGEPROCESSED WHERE processorName = ? AND messageId = ?";
042 protected static final String INSERT_STRING = "INSERT INTO CAMEL_MESSAGEPROCESSED (processorName, messageId, createdAt) VALUES (?, ?, ?)";
043 protected static final String DELETE_STRING = "DELETE FROM CAMEL_MESSAGEPROCESSED WHERE processorName = ? AND messageId = ?";
044
045 private final JdbcTemplate jdbcTemplate;
046 private final String processorName;
047 private final TransactionTemplate transactionTemplate;
048
049 public JdbcMessageIdRepository(DataSource dataSource, String processorName) {
050 this(dataSource, createTransactionTemplate(dataSource), processorName);
051 }
052
053 public JdbcMessageIdRepository(DataSource dataSource, TransactionTemplate transactionTemplate, String processorName) {
054 this.jdbcTemplate = new JdbcTemplate(dataSource);
055 this.jdbcTemplate.afterPropertiesSet();
056 this.processorName = processorName;
057 this.transactionTemplate = transactionTemplate;
058 }
059
060 public static JdbcMessageIdRepository jpaMessageIdRepository(DataSource dataSource, String processorName) {
061 return new JdbcMessageIdRepository(dataSource, processorName);
062 }
063
064 private static TransactionTemplate createTransactionTemplate(DataSource dataSource) {
065 TransactionTemplate transactionTemplate = new TransactionTemplate();
066 transactionTemplate.setTransactionManager(new DataSourceTransactionManager(dataSource));
067 transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
068 return transactionTemplate;
069 }
070
071 @ManagedOperation(description = "Adds the key to the store")
072 public boolean add(final String messageId) {
073 // Run this in single transaction.
074 Boolean rc = transactionTemplate.execute(new TransactionCallback<Boolean>() {
075 public Boolean doInTransaction(TransactionStatus status) {
076 int count = jdbcTemplate.queryForInt(QUERY_STRING, processorName, messageId);
077 if (count == 0) {
078 jdbcTemplate.update(INSERT_STRING, processorName, messageId, new Timestamp(System.currentTimeMillis()));
079 return Boolean.TRUE;
080 } else {
081 return Boolean.FALSE;
082 }
083 }
084 });
085 return rc.booleanValue();
086 }
087
088 @ManagedOperation(description = "Does the store contain the given key")
089 public boolean contains(final String messageId) {
090 // Run this in single transaction.
091 Boolean rc = transactionTemplate.execute(new TransactionCallback<Boolean>() {
092 public Boolean doInTransaction(TransactionStatus status) {
093 int count = jdbcTemplate.queryForInt(QUERY_STRING, processorName, messageId);
094 if (count == 0) {
095 return Boolean.FALSE;
096 } else {
097 return Boolean.TRUE;
098 }
099 }
100 });
101 return rc.booleanValue();
102 }
103
104 @ManagedOperation(description = "Remove the key from the store")
105 public boolean remove(final String messageId) {
106 Boolean rc = transactionTemplate.execute(new TransactionCallback<Boolean>() {
107 public Boolean doInTransaction(TransactionStatus status) {
108 int updateCount = jdbcTemplate.update(DELETE_STRING, processorName, messageId);
109 if (updateCount == 0) {
110 return Boolean.FALSE;
111 } else {
112 return Boolean.TRUE;
113 }
114 }
115 });
116 return rc.booleanValue();
117 }
118
119 public boolean confirm(String s) {
120 // noop
121 return true;
122 }
123
124 @ManagedAttribute(description = "The processor name")
125 public String getProcessorName() {
126 return processorName;
127 }
128
129 @Override
130 protected void doStart() throws Exception {
131 }
132
133 @Override
134 protected void doStop() throws Exception {
135 }
136 }