// ============================================================================
//
// Copyright (C) 2006-2016 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.dataquality.semantic.index;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.junit.Before;
import org.junit.Test;
public class ConcurrentDictionaryAccessTest {
private static final Logger log = Logger.getLogger(ConcurrentDictionaryAccessTest.class);
private AtomicBoolean errorOccurred = new AtomicBoolean();
@Before
public void setUp() throws Exception {
errorOccurred.set(false);
}
private DictionarySearcher newSemanticDictionarySearcher() {
try {
final URI ddPath = this.getClass().getResource("/index/dictionary").toURI();
final DictionarySearcher searcher = new DictionarySearcher(ddPath);
searcher.setTopDocLimit(20);
searcher.setSearchMode(DictionarySearchMode.MATCH_SEMANTIC_DICTIONARY);
return searcher;
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
private class SearcherRunnable implements Runnable {
@Override
public void run() {
final DictionarySearcher searcher = newSemanticDictionarySearcher();
doConcurrentAccess(searcher, true);
}
};
@Test
public void testThreadUnsafeConcurrentAccess() throws Exception {
try {
List<Thread> workers = new ArrayList<>();
for (int i = 0; i < 200; i++) {
final Runnable r = new SearcherRunnable();
workers.add(new Thread(r));
}
for (Thread worker : workers) {
worker.start();
}
for (Thread worker : workers) {
worker.join();
}
assertEquals("ConcurrentAccess failed", false, errorOccurred.get());
} catch (Exception e) {
fail(e.getMessage());
}
}
private static final Map<String, List<String>> EXPECTED_CATEGORY = new HashMap<String, List<String>>() {
private static final long serialVersionUID = 3771932655942133797L;
{
put("Paris", Arrays.asList(new String[] { "CITY", "FIRST_NAME", "LAST_NAME", "FR_COMMUNE", "FR_DEPARTEMENT" }));
put("Talend", Arrays.asList(new String[] { "COMPANY" }));
put("CDG", Arrays.asList(new String[] { "AIRPORT_CODE" }));
put("French", Arrays.asList(new String[] { "LANGUAGE", "LAST_NAME" }));
}
};
private void doConcurrentAccess(DictionarySearcher searcher, boolean isLogEnabled) {
int datasetID = (int) Math.floor(Math.random() * 4);
String input = "";
switch (datasetID) {
case 0:
input = "Paris";
break;
case 1:
input = "Talend";
break;
case 2:
input = "CDG";
break;
case 3:
input = "French";
break;
default:
break;
}
try {
final TopDocs docs = searcher.searchDocumentBySynonym(input);
ScoreDoc[] scoreDocs = docs.scoreDocs;
for (ScoreDoc sd : scoreDocs) {
final Document doc = searcher.getDocument(sd.doc);
final String cat = doc.getField("word").stringValue();
if (!EXPECTED_CATEGORY.get(input).contains(cat)) {
errorOccurred.set(true);
if (isLogEnabled) {
log.error(input + " is expected to be a " + cat + " but actually not");
}
}
}
searcher.close();
} catch (Exception e) {
errorOccurred.set(true);
if (isLogEnabled) {
log.error(e.getMessage(), e);
}
}
}
}