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    }