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 }