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