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 javax.sql.DataSource;
020
021 import org.apache.camel.impl.ServiceSupport;
022 import org.apache.camel.spi.IdempotentRepository;
023 import org.springframework.jdbc.core.JdbcTemplate;
024 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
025 import org.springframework.jmx.export.annotation.ManagedAttribute;
026 import org.springframework.jmx.export.annotation.ManagedOperation;
027 import org.springframework.jmx.export.annotation.ManagedResource;
028 import org.springframework.transaction.TransactionDefinition;
029 import org.springframework.transaction.TransactionStatus;
030 import org.springframework.transaction.support.TransactionCallback;
031 import org.springframework.transaction.support.TransactionTemplate;
032
033 /**
034 * @version
035 */
036 @ManagedResource("JdbcMessageIdRepository")
037 public class JdbcMessageIdRepository extends ServiceSupport implements IdempotentRepository<String> {
038
039 protected static final String QUERY_STRING = "SELECT COUNT(*) FROM CAMEL_MESSAGEPROCESSED WHERE processorName = ? AND messageId = ?";
040 protected static final String INSERT_STRING = "INSERT INTO CAMEL_MESSAGEPROCESSED (processorName, messageId) VALUES (?, ?)";
041 protected static final String DELETE_STRING = "DELETE FROM CAMEL_MESSAGEPROCESSED WHERE processorName = ? AND messageId = ?";
042
043 private final JdbcTemplate jdbcTemplate;
044 private final String processorName;
045 private final TransactionTemplate transactionTemplate;
046
047 public JdbcMessageIdRepository(DataSource dataSource, String processorName) {
048 this(dataSource, createTransactionTemplate(dataSource), processorName);
049 }
050
051 public JdbcMessageIdRepository(DataSource dataSource, TransactionTemplate transactionTemplate, String processorName) {
052 this.jdbcTemplate = new JdbcTemplate(dataSource);
053 this.jdbcTemplate.afterPropertiesSet();
054 this.processorName = processorName;
055 this.transactionTemplate = transactionTemplate;
056 }
057
058 public static JdbcMessageIdRepository jpaMessageIdRepository(DataSource dataSource, String processorName) {
059 return new JdbcMessageIdRepository(dataSource, processorName);
060 }
061
062 private static TransactionTemplate createTransactionTemplate(DataSource dataSource) {
063 TransactionTemplate transactionTemplate = new TransactionTemplate();
064 transactionTemplate.setTransactionManager(new DataSourceTransactionManager(dataSource));
065 transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
066 return transactionTemplate;
067 }
068
069 @ManagedOperation(description = "Adds the key to the store")
070 @SuppressWarnings({ "unchecked", "rawtypes" })
071 public boolean add(final String messageId) {
072 // Run this in single transaction.
073 Boolean rc = (Boolean)transactionTemplate.execute(new TransactionCallback() {
074 public Object doInTransaction(TransactionStatus status) {
075 int count = jdbcTemplate.queryForInt(QUERY_STRING, processorName, messageId);
076 if (count == 0) {
077 jdbcTemplate.update(INSERT_STRING, processorName, messageId);
078 return Boolean.TRUE;
079 } else {
080 return Boolean.FALSE;
081 }
082 }
083 });
084 return rc.booleanValue();
085 }
086
087 @ManagedOperation(description = "Does the store contain the given key")
088 @SuppressWarnings({ "unchecked", "rawtypes" })
089 public boolean contains(final String messageId) {
090 // Run this in single transaction.
091 Boolean rc = (Boolean)transactionTemplate.execute(new TransactionCallback() {
092 public Object 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 @SuppressWarnings({ "unchecked", "rawtypes" })
106 public boolean remove(final String messageId) {
107 Boolean rc = (Boolean)transactionTemplate.execute(new TransactionCallback() {
108 public Object doInTransaction(TransactionStatus status) {
109 int updateCount = jdbcTemplate.update(DELETE_STRING, processorName, messageId);
110 if (updateCount == 0) {
111 return Boolean.FALSE;
112 } else {
113 return Boolean.TRUE;
114 }
115 }
116 });
117 return rc.booleanValue();
118 }
119
120 public boolean confirm(String s) {
121 // noop
122 return true;
123 }
124
125 @ManagedAttribute(description = "The processor name")
126 public String getProcessorName() {
127 return processorName;
128 }
129
130 @Override
131 protected void doStart() throws Exception {
132 }
133
134 @Override
135 protected void doStop() throws Exception {
136 }
137 }