/**
* Copyright 1999-2011 Alibaba Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cobar.client.support.execution;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.sql.DataSource;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.orm.ibatis.SqlMapClientCallback;
import com.alibaba.cobar.client.support.utils.CollectionUtils;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapSession;
public class DefaultConcurrentRequestProcessor implements IConcurrentRequestProcessor {
private transient final Logger logger = LoggerFactory
.getLogger(DefaultConcurrentRequestProcessor.class);
private SqlMapClient sqlMapClient;
public DefaultConcurrentRequestProcessor() {
}
public DefaultConcurrentRequestProcessor(SqlMapClient sqlMapClient) {
this.sqlMapClient = sqlMapClient;
}
public List<Object> process(List<ConcurrentRequest> requests) {
List<Object> resultList = new ArrayList<Object>();
if (CollectionUtils.isEmpty(requests))
return resultList;
List<RequestDepository> requestsDepo = fetchConnectionsAndDepositForLaterUse(requests);
final CountDownLatch latch = new CountDownLatch(requestsDepo.size());
List<Future<Object>> futures = new ArrayList<Future<Object>>();
try {
for (RequestDepository rdepo : requestsDepo) {
ConcurrentRequest request = rdepo.getOriginalRequest();
final SqlMapClientCallback action = request.getAction();
final Connection connection = rdepo.getConnectionToUse();
futures.add(request.getExecutor().submit(new Callable<Object>() {
public Object call() throws Exception {
try {
return executeWith(connection, action);
} finally {
latch.countDown();
}
}
}));
}
try {
latch.await();
} catch (InterruptedException e) {
throw new ConcurrencyFailureException(
"interrupted when processing data access request in concurrency", e);
}
} finally {
for (RequestDepository depo : requestsDepo) {
Connection springCon = depo.getConnectionToUse();
DataSource dataSource = depo.getOriginalRequest().getDataSource();
try {
if (springCon != null) {
if (depo.isTransactionAware()) {
springCon.close();
} else {
DataSourceUtils.doReleaseConnection(springCon, dataSource);
}
}
} catch (Throwable ex) {
logger.info("Could not close JDBC Connection", ex);
}
}
}
fillResultListWithFutureResults(futures, resultList);
return resultList;
}
protected Object executeWith(Connection connection, SqlMapClientCallback action) {
SqlMapSession session = getSqlMapClient().openSession();
try {
try {
session.setUserConnection(connection);
} catch (SQLException e) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", e);
}
try {
return action.doInSqlMapClient(session);
} catch (SQLException ex) {
throw new SQLErrorCodeSQLExceptionTranslator().translate("SqlMapClient operation",
null, ex);
}
} finally {
session.close();
}
}
private void fillResultListWithFutureResults(List<Future<Object>> futures,
List<Object> resultList) {
for (Future<Object> future : futures) {
try {
resultList.add(future.get());
} catch (InterruptedException e) {
throw new ConcurrencyFailureException(
"interrupted when processing data access request in concurrency", e);
} catch (ExecutionException e) {
throw new ConcurrencyFailureException("something goes wrong in processing", e);
}
}
}
private List<RequestDepository> fetchConnectionsAndDepositForLaterUse(
List<ConcurrentRequest> requests) {
List<RequestDepository> depos = new ArrayList<RequestDepository>();
for (ConcurrentRequest request : requests) {
DataSource dataSource = request.getDataSource();
Connection springCon = null;
boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);
try {
springCon = (transactionAware ? dataSource.getConnection() : DataSourceUtils
.doGetConnection(dataSource));
} catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
RequestDepository depo = new RequestDepository();
depo.setOriginalRequest(request);
depo.setConnectionToUse(springCon);
depo.setTransactionAware(transactionAware);
depos.add(depo);
}
return depos;
}
public void setSqlMapClient(SqlMapClient sqlMapClient) {
Validate.notNull(sqlMapClient);
this.sqlMapClient = sqlMapClient;
}
public SqlMapClient getSqlMapClient() {
return sqlMapClient;
}
}