package egovframework.rte.fdl.idgnr;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import egovframework.rte.fdl.cmmn.exception.FdlException;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.jdbc.JdbcTestUtils;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:/spring/context-common.xml",
"classpath*:/spring/context-transaction.xml",
"classpath*:/spring/context-tableid.xml" })
public class EgovTableIdGnrServiceWithThreadTest {
int testCount = 100;
int testThread = 10;
int[] ids = null;
@Resource(name = "dataSource")
private DataSource dataSource;
@Resource(name = "schemaProperties")
Properties schemaProperties;
@Resource(name = "Ids-TestSimpleRequestIdsSize1")
private EgovIdGnrService idsTestSimpleRequestIdsSize1;
private static final Logger LOGGER = LoggerFactory.getLogger(EgovTableIdGnrServiceWithThreadTest.class);
/**
* Test Case 시작
*
* @throws Exception fail to initialize
*/
@Before
public void onSetUp() throws Exception {
JdbcTestUtils.executeSqlScript(new JdbcTemplate(dataSource), new ClassPathResource("testdata/" + schemaProperties.getProperty("tab_sample_create")), true);
}
@After
public void onTearDown() throws Exception {
JdbcTestUtils.executeSqlScript(new JdbcTemplate(dataSource), new ClassPathResource("testdata/" + schemaProperties.getProperty("tab_sample_drop")), true);
}
@Test
public void testIdsSize1InThread() throws Exception {
// 1. Initialize the counter in the database.
initializeNextLongId("test", 0);
ids = new int[testCount * testThread];
Thread[] t = new Thread[testThread];
for (int i = 0; i < testThread; i++) {
t[i] = new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= testCount; i++) {
int id;
try {
id = idsTestSimpleRequestIdsSize1.getNextIntegerId();
LOGGER.info("Generated ID : {} (ThreadID = {})", id, Thread.currentThread().getId());
ids[id] = ids[id] + 1;
} catch (FdlException e) {
e.printStackTrace();
}
}
}
});
t[i].start();
}
for (int i = 0; i < testThread; i++) {
try {
t[i].join();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 0; i < ids.length; i++) {
assertTrue("Some id is duplicated : " + i + " => " + ids[i], ids[i] == 1);
}
// 3. get next Long id using query directly.
assertEquals("The next_id column in the database did not have the expected value.", testCount * testThread, peekNextLongId("test"));
}
@Test
@Transactional
public void testIdsSize1InThreadWithTransaction() throws Exception {
// 1. Initialize the counter in the database.
initializeNextLongId("test", 0);
ids = new int[testCount * testThread];
Thread[] t = new Thread[testThread];
for (int i = 0; i < testThread; i++) {
t[i] = new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= testCount; i++) {
int id;
try {
id = idsTestSimpleRequestIdsSize1.getNextIntegerId();
LOGGER.info("Generated ID : {} (ThreadID = {})", id, Thread.currentThread().getId());
ids[id] = ids[id] + 1;
} catch (FdlException e) {
e.printStackTrace();
}
}
}
});
t[i].start();
}
for (int i = 0; i < testThread; i++) {
try {
t[i].join();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 0; i < ids.length; i++) {
assertTrue("Some id is duplicated : " + i + " => " + ids[i], ids[i] == 1);
}
// 3. get next Long id using query directly.
assertEquals("The next_id column in the database did not have the expected value.", testCount * testThread, peekNextLongId("test"));
}
@Test
public void testIdsSize1InThreadWithSeperatedTransaction() throws Exception {
// 1. Initialize the counter in the database.
initializeNextLongId("test", 0);
ids = new int[testCount * testThread];
Thread[] t = new Thread[testThread];
for (int i = 0; i < testThread; i++) {
t[i] = new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= testCount; i++) {
transaction();
}
}
});
t[i].start();
}
for (int i = 0; i < testThread; i++) {
try {
t[i].join();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 0; i < ids.length; i++) {
assertTrue("Some id is duplicated : " + i + " => " + ids[i], ids[i] == 1);
}
// 3. get next Long id using query directly.
assertEquals("The next_id column in the database did not have the expected value.", testCount * testThread, peekNextLongId("test"));
}
@Transactional
private void transaction() {
int id;
try {
id = idsTestSimpleRequestIdsSize1.getNextIntegerId();
LOGGER.info("Generated ID : {} (ThreadID = {})", id, Thread.currentThread().getId());
ids[id] = ids[id] + 1;
} catch (FdlException e) {
e.printStackTrace();
}
}
/**
* 초기값 세팅
*
* @param tableName table name
* @param nextId next id
*/
private void initializeNextLongId(String tableName, long nextId) {
try {
Connection conn = dataSource.getConnection();
try {
Statement statement = conn.createStatement();
statement.executeUpdate("INSERT INTO idttest (table_name, next_id) VALUES ('" + tableName + "', " + nextId + ")");
} finally {
conn.close();
}
} catch (Exception e) {
fail("Unable to initialize next_id. " + e);
}
}
/**
* Query를 통해 long 타입의 ID 직접 읽기
*
* @param tableName table name
* @return next Long Id
*/
private long peekNextLongId(String tableName) {
try {
Connection conn = dataSource.getConnection();
try {
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery("SELECT next_id FROM idttest " + "WHERE table_name = '" + tableName + "'");
if (rs.next()) {
return rs.getLong(1);
} else {
fail(tableName + " row not in ids table.");
return -1; // for compiler
}
} finally {
conn.close();
}
} catch (Exception e) {
fail("Unable to peek next_id. " + e);
return -1; // for compiler
}
}
}