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    }