OK. Я придумал решение.
БОЛЬШОЕ ПРЕДУПРЕЖДЕНИЕ:
непроверенный для производственного использования!
Используемые зависимости javascript: плагин JQuery, jquery datatables (http://datatables.net) и плагин fnStandingRedraw для datatables.
Зависимости Java: поиск в библиотеке Gson
У меня есть страница поиска, которая отправляется сервлету.
Сервлет начинает поиск и помещает поток поиска и BlockingQueue в сессию, а затем перенаправляет на страницу результатов:
final LinkedBlockingQueue<Integer> hits =
new LinkedBlockingQueue<Integer>();
Thread searcher = new Thread() {
@Override
public void run() {
query.search(hits);
}
};
searcher.start();
session.setAttribute("searchQueue", hits);
session.setAttribute("searchThread", searcher);
response.sendRedirect("SearchResult.jsp");
Обратите внимание, что очередь заполнена первичным ключом из базы данных, следовательно, целым числом.
Страница результатов нуждается в следующих ссылках:
<link href="css/datatables/demo_table.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="js/jquery-1.6.2.js"></script>
<script type="text/javascript" src="js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="js/datatables.fnStandingRedraw.js"></script>
и следующий код JavaScript для инициализации таблицы и добавления строк:
<script type="text/javascript">
var resultsTable;
var myTimer;
$(document).ready(function() {
resultsTable = $('#result').dataTable( {
"bDeferRender": true,
"bProcessing": true,
"sPaginationType": "full_numbers",
"iDisplayLength": 4,
"aLengthMenu": [[4, -1], [4, "All"]],
"sScrollY": "580",
"aaSorting": [],
"aoColumns": [
/* column1 */ {"bSearchable": true,
"bSortable": true,
"sWidth": "50px"},
/* column2*/{"bSearchable": false,
"bSortable": false,
"sWidth": "510px"}
]
} );
getAndAddRows();
startTimer();
} );
function startTimer() {
myTimer = window.setInterval( function() {
getAndAddRows();
}, 500);
};
function stopTimer(){
window.clearInterval(myTimer);
}
function getAndAddRows(){
$.get(
'getSearchHits',
function(data){
if(data[0] == 'searchCompleted'){
stopTimer();
$('#hitsFound').text('Hits Found: '
+ resultsTable.fnSettings().fnRecordsTotal()
+ ' Search Completed');
return;
}
$('#result').dataTable().fnAddData(data, false);
resultsTable.fnStandingRedraw();
$('#hitsFound').text('Hits Found: '
+ resultsTable.fnSettings().fnRecordsTotal()
+ ' Searching...');
},
"json"
);
}
</script>
<h1 id="hitsFound">Hits Found:</h1>
<table id="result">
<thead>
<tr>
<th>column1</th>
<th>column2</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Пожалуйста, также обратитесь к домашней странице datatables, чтобы узнать, как настроить такую таблицу.
Функция get JQuery периодически вызывается на основе таймера. Он вызывает другой сервлет, который читает из очереди и возвращает новые обращения, отформатированные как JSON:
int limit = 50; //amount of records ro return
int counter = 0;
BlockingQueue<Integer> hits = (BlockingQueue<Integer>) session.getAttribute("searchQueue");
Thread searcher = (Thread) session.getAttribute("searchThread");
Gson gson = new Gson();
ArrayList<ArrayList> rows = new ArrayList<ArrayList>();
while (hits.size() > 0 && limit > counter) {
ArrayList row = new ArrayList();
Integer key = hits.take();
// get desired data and add it to current row example:
String myData = dataccessObject.getMyData(key);
row.add(key);
row.add(myData);
rows.add(row);
counter++;
}
if (rows.size() < 1 && !searcher.isAlive()) {
out.print("[\"searchCompleted\"]");
} else {
String json = gson.toJson(rows); //==> json is [[1,"myData1"],[2,"myData2"]]
out.print(json);
}
Лимит и таймеры могут потребоваться отрегулировать в зависимости от вашей цели. Ограничение существует, поэтому новые результаты периодически добавляются. (Если получение данных происходит медленнее, чем скорость, с которой обнаруживаются новые попадания, то ничего не будет возвращено и отображено до завершения поиска).
Обратите внимание, что строка myData может быть HTML, и она отображается как HTML. В моем случае я возвращаю теги изображений. И эти теги изображений имеют другой сервлет в качестве источника, который создает изображения динамически. Обратите внимание, что опция datatables "bDeferRender": true отображает соответствующие строки (и в моем случае изображения), как только они становятся видимыми в первый раз. Например. если пользователь просматривает только первые 3 страницы, страницы 4-100 фактически не отображаются. И если пользователь нажимает на последнюю страницу, то отображается только последняя, а не все между ними.
Пожалуйста, прокомментируйте в случае вопросов или улучшений / возможных ошибок.