1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.rice.krad.data.platform;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.config.property.ConfigContext;
20  import org.springframework.beans.factory.InitializingBean;
21  import org.springframework.dao.DataAccessException;
22  import org.springframework.dao.DataAccessResourceFailureException;
23  import org.springframework.dao.IncorrectResultSizeDataAccessException;
24  import org.springframework.jdbc.core.ConnectionCallback;
25  import org.springframework.jdbc.core.JdbcTemplate;
26  import org.springframework.jdbc.support.JdbcUtils;
27  import org.springframework.jdbc.support.incrementer.AbstractColumnMaxValueIncrementer;
28  import org.springframework.jdbc.support.incrementer.AbstractSequenceMaxValueIncrementer;
29  import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
30  import org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer;
31  
32  import javax.sql.DataSource;
33  import java.sql.Connection;
34  import java.sql.ResultSet;
35  import java.sql.SQLException;
36  import java.sql.Statement;
37  import java.util.Collections;
38  import java.util.IdentityHashMap;
39  import java.util.Map;
40  import java.util.concurrent.ConcurrentHashMap;
41  import java.util.concurrent.ConcurrentMap;
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  public final class MaxValueIncrementerFactory {
66  
67      private static final String ID_COLUMN_NAME = "ID";
68  
69      
70  
71  
72  
73  
74  
75  
76  
77      public static final String PLATFORM_INCREMENTER_PREFIX = "rice.krad.data.platform.incrementer.";
78  
79      private static final Map<DataSource, ConcurrentMap<String, DataFieldMaxValueIncrementer>> cache
80              = Collections.synchronizedMap(new IdentityHashMap<DataSource, ConcurrentMap<String, DataFieldMaxValueIncrementer>>(8));
81  
82      
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94      public static DataFieldMaxValueIncrementer getIncrementer(DataSource dataSource, String incrementerName) {
95          if (dataSource == null) {
96              throw new IllegalArgumentException("DataSource must not be null");
97          }
98          if (StringUtils.isBlank(incrementerName)) {
99              throw new IllegalArgumentException("Incrementer name must not be null or blank");
100         }
101 
102         
103         
104         ConcurrentMap<String, DataFieldMaxValueIncrementer> incrementerCache = cache.get(dataSource);
105 
106         if (incrementerCache == null) {
107             cache.put(dataSource,
108                     new ConcurrentHashMap<String, DataFieldMaxValueIncrementer>(8, 0.9f, 1));
109             if (incrementerCache == null) {
110                 incrementerCache = cache.get(dataSource);
111             }
112         }
113 
114         
115         DataFieldMaxValueIncrementer incrementer = incrementerCache.get(incrementerName.toUpperCase());
116         if (incrementer == null) {
117             incrementer = incrementerCache.putIfAbsent(incrementerName.toUpperCase(), createIncrementer(dataSource,
118                     incrementerName));
119             if (incrementer == null) {
120                 incrementer = incrementerCache.get(incrementerName.toUpperCase());
121             }
122         }
123         return incrementer;
124 
125     }
126 
127     
128 
129 
130 
131 
132 
133 
134     private static DataFieldMaxValueIncrementer createIncrementer(DataSource dataSource, String incrementerName) {
135         DatabasePlatformInfo platformInfo = DatabasePlatforms.detectPlatform(dataSource);
136         DataFieldMaxValueIncrementer incrementer = getCustomizedIncrementer(platformInfo, dataSource,incrementerName,ID_COLUMN_NAME);
137         if(incrementer != null){
138             return incrementer;
139         }
140 
141         if (DatabasePlatforms.ORACLE.equalsIgnoreCase(platformInfo.getName())) {
142             incrementer = new OracleSequenceMaxValueIncrementer(dataSource, incrementerName);
143         } else if (DatabasePlatforms.MYSQL.equalsIgnoreCase(platformInfo.getName())) {
144             incrementer = new EnhancedMySQLMaxValueIncrementer(dataSource, incrementerName, ID_COLUMN_NAME);
145         }
146         if (incrementer == null) {
147             throw new UnsupportedDatabasePlatformException(platformInfo);
148         }
149         if (incrementer instanceof InitializingBean) {
150             try {
151                 ((InitializingBean) incrementer).afterPropertiesSet();
152             } catch (Exception e) {
153                 throw new DataAccessResourceFailureException(
154                         "Failed to initialize max value incrementer for given datasource and incrementer. dataSource="
155                                 + dataSource.toString()
156                                 + ", incrementerName = "
157                                 + incrementerName, e);
158             }
159         }
160         return incrementer;
161     }
162 
163     
164 
165 
166 
167 
168 
169 
170 
171 
172 
173 
174 
175 
176 
177     private static DataFieldMaxValueIncrementer getCustomizedIncrementer(DatabasePlatformInfo platformInfo, DataSource dataSource, String incrementerName, String columnName){
178         if(platformInfo == null){
179             throw new  IllegalArgumentException("DataSource platform must not be null");
180         }
181         if(ConfigContext.getCurrentContextConfig() == null){
182             return null;
183         }
184         Map<String,String> incrementerPropToIncrementer = ConfigContext.getCurrentContextConfig().
185                                 getPropertiesWithPrefix(PLATFORM_INCREMENTER_PREFIX, true);
186         String platformNameVersion = platformInfo.getName().toLowerCase() + "." + platformInfo.getMajorVersion();
187         String incrementerClassName = "";
188 
189          if(incrementerPropToIncrementer.containsKey(platformNameVersion)){
190             incrementerClassName = incrementerPropToIncrementer.get(platformNameVersion);
191          } else if(incrementerPropToIncrementer.containsKey(platformInfo.getName().toLowerCase())){
192              incrementerClassName = incrementerPropToIncrementer.get(platformInfo.getName().toLowerCase());
193          }
194 
195         if(StringUtils.isNotBlank(incrementerClassName)){
196             try {
197                 Class incrementerClass = Class.forName(incrementerClassName);
198                 if(AbstractSequenceMaxValueIncrementer.class.isAssignableFrom(incrementerClass)){
199                     AbstractSequenceMaxValueIncrementer abstractSequenceMaxValueIncrementer = (AbstractSequenceMaxValueIncrementer)incrementerClass.newInstance();
200                     abstractSequenceMaxValueIncrementer.setDataSource(dataSource);
201                     abstractSequenceMaxValueIncrementer.setIncrementerName(incrementerName);
202                     return abstractSequenceMaxValueIncrementer;
203 
204                 } else if(AbstractColumnMaxValueIncrementer.class.isAssignableFrom(incrementerClass)){
205                     AbstractColumnMaxValueIncrementer abstractColumnMaxValueIncrementer = (AbstractColumnMaxValueIncrementer)incrementerClass.newInstance();
206                     abstractColumnMaxValueIncrementer.setDataSource(dataSource);
207                     abstractColumnMaxValueIncrementer.setIncrementerName(incrementerName);
208                     abstractColumnMaxValueIncrementer.setColumnName(columnName);
209                     return abstractColumnMaxValueIncrementer;
210                 } else {
211                     throw new InstantiationError("Cannot create incrementer class "+incrementerClassName +" it has to extend "
212                             + "AbstractSequenceMaxValueIncrementer or AbstractColumnMaxValueIncrementer");
213                 }
214             } catch (Exception e){
215                 throw new InstantiationError("Could not instantiate custom incrementer "+incrementerClassName);
216             }
217         }
218         return null;
219     }
220 
221     
222 
223 
224 
225 
226 
227 
228 
229 
230 
231     static final class EnhancedMySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer {
232 
233         private JdbcTemplate template;
234 
235         
236 
237 
238         private EnhancedMySQLMaxValueIncrementer() {}
239 
240         
241 
242 
243 
244 
245 
246 
247         private EnhancedMySQLMaxValueIncrementer(DataSource dataSource, String incrementerName, String columnName) {
248             super(dataSource, incrementerName, columnName);
249         }
250 
251         
252 
253 
254         @Override
255         public synchronized void afterPropertiesSet() {
256             super.afterPropertiesSet();
257             template = new JdbcTemplate(getDataSource());
258         }
259 
260         
261 
262 
263         @Override
264         protected synchronized long getNextKey() throws DataAccessException {
265             return template.execute(new ConnectionCallback<Long>() {
266                 @Override
267                 public Long doInConnection(Connection con) throws SQLException, DataAccessException {
268                     Statement statement = null;
269                     ResultSet resultSet = null;
270                     try {
271                         statement = con.createStatement();
272                         String sql = "INSERT INTO " + getIncrementerName() + " VALUES (NULL)";
273                         statement.executeUpdate(sql);
274                         sql = "SELECT LAST_INSERT_ID()";
275                         resultSet = statement.executeQuery(sql);
276                         if (resultSet != null) {
277                             resultSet.first();
278                             return resultSet.getLong(1);
279                         } else {
280                             throw new IncorrectResultSizeDataAccessException("Failed to get last_insert_id() for sequence incrementer table '" + getIncrementerName() + "'", 1);
281                         }
282                     } finally {
283                         JdbcUtils.closeResultSet(resultSet);
284                         JdbcUtils.closeStatement(statement);
285                     }
286                 }
287             }).longValue();
288         }
289     }
290 
291     
292 
293 
294     private MaxValueIncrementerFactory() {}
295 
296 }