/*
* codjo.net
*
* Common Apache License 2.0
*/
package net.codjo.segmentation.server.participant;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.codjo.expression.FindUses;
import net.codjo.expression.InvalidExpressionException;
import net.codjo.segmentation.common.MidAuditKey;
import net.codjo.segmentation.server.blackboard.message.BlackboardAction;
import net.codjo.segmentation.server.blackboard.message.Level;
import net.codjo.segmentation.server.blackboard.message.Todo;
import net.codjo.segmentation.server.blackboard.message.Write;
import net.codjo.segmentation.server.participant.common.Page;
import net.codjo.segmentation.server.participant.common.PageStructure;
import net.codjo.segmentation.server.participant.context.ContextManager;
import net.codjo.segmentation.server.participant.context.FamilyContext;
import net.codjo.segmentation.server.participant.context.SegmentationContext;
import net.codjo.segmentation.server.participant.context.SegmentationReport;
import net.codjo.segmentation.server.participant.context.TaskTemplate;
import net.codjo.segmentation.server.participant.context.TodoContent;
import net.codjo.segmentation.server.preference.family.Row;
import net.codjo.segmentation.server.preference.family.RowFilter;
import net.codjo.segmentation.server.preference.family.XmlFamilyPreference;
import net.codjo.segmentation.server.preference.treatment.Expression;
import net.codjo.segmentation.server.util.SegmentationUtil;
import net.codjo.sql.builder.DefaultFieldInfoList;
import net.codjo.sql.builder.FieldInfo;
import net.codjo.sql.builder.QueryBuilder;
import net.codjo.sql.builder.QueryBuilderFactory;
import net.codjo.variable.UnknownVariableException;
import net.codjo.workflow.common.message.Arguments;
import static net.codjo.segmentation.server.util.SegmentationUtil.determineFullColumnName;
/**
*
*/
public class PaginatorParticipant extends BackOfficeSegmentationParticipant {
public PaginatorParticipant(ContextManager contextManager) {
super(contextManager, TransactionType.AUTO_COMMIT, SegmentationLevels.TO_PAGINATE);
}
@Override
protected void handleTodo(Todo<TodoContent> todo, Level fromLevel, Connection connection) {
logger.info("Pagination des donn�es input : " + todo.getContent());
new Paginator().run(todo, fromLevel, connection);
}
static Set<String> findUsedFieldInExpressions(List<SegmentationContext> contexts)
throws InvalidExpressionException {
FindUses findUses = new FindUses();
for (SegmentationContext context : contexts) {
for (Expression expression : context.getSegmentationPreference().getExpressions()) {
findUses.add(expression.getExpression());
}
}
Collection usedFields = findUses.buildReport().getUsedSourceColumns();
//noinspection unchecked
return new HashSet<String>(usedFields);
}
class Paginator {
private String[] sourceColumnNames;
private Set<String> fieldsUsedInExpressions;
private Todo<TodoContent> currentTodo;
private Level currentLevel;
private List<SegmentationContext> segmentationContexts;
private FamilyContext familyContext;
private DefaultFieldInfoList fieldInfoList;
private int pageToBeComputedCount = 0;
private boolean hasNext = true;
private XmlFamilyPreference familyPreference;
public void run(final Todo<TodoContent> todo, final Level fromLevel, final Connection connection) {
SegmentationReport report = getReport(todo);
new TaskTemplate(report, getName()) {
@Override
protected void doRun() throws Exception {
initialize(todo, fromLevel);
pagineData(connection, this);
}
@Override
protected void handleException(Exception e) {
logger.fatal("Pagination en erreur : " + todo.getContent(), e);
send(informOfFailure(todo, fromLevel).dueTo(e));
}
}.run();
logger.info("Nombre de pages : " + pageToBeComputedCount);
}
void initialize(Todo<TodoContent> todo, Level level)
throws InvalidExpressionException {
currentTodo = todo;
currentLevel = level;
familyContext = contextManager.getFamilyContext(currentTodo);
segmentationContexts = familyContext.getSegmentationContexts();
fieldsUsedInExpressions = findUsedFieldInExpressions(segmentationContexts);
familyPreference = familyContext.getFamilyPreference();
}
private void pagineData(Connection connection, TaskTemplate parentTask)
throws SQLException, UnknownVariableException {
final Statement statement = connection.createStatement();
try {
final int[] pageId = {1};
final Page[] page = {new Page()};
final String selectQuery = buildSelectQuery(familyContext);
logger.info("selectQuery=" + selectQuery);
final ResultSet[] resultSet = new ResultSet[1];
new TaskTemplate(parentTask, "select") {
@Override
protected void doRun() throws Exception {
resultSet[0] = statement.executeQuery(selectQuery);
}
}.run();
try {
PageStructure pageStructure = createPageStructure(resultSet[0].getMetaData());
for (SegmentationContext context : familyContext.getSegmentationContexts()) {
context.setPageStructure(pageStructure);
}
hasNext = resultSet[0].next();
while (hasNext) {
new TaskTemplate(parentTask, "processRow") {
@Override
protected void doRun() throws Exception {
if (page[0].isFull()) {
send(createComputeTodos(pageId[0]++, page[0]));
page[0] = new Page();
}
page[0].addRow(extractRow(resultSet[0]));
hasNext = resultSet[0].next();
}
}.run();
}
}
finally {
resultSet[0].close();
}
if (page[0].getRowCount() > 0) {
send(createComputeTodos(pageId[0], page[0]).then().erase(currentTodo, currentLevel));
}
else {
send(erase(currentTodo, currentLevel));
}
}
finally {
statement.close();
}
}
private Row extractRow(ResultSet resultSet)
throws SQLException {
Object[] values = new Object[sourceColumnNames.length];
for (int i = 1; i <= sourceColumnNames.length; i++) {
values[i - 1] = resultSet.getObject(i);
}
return new Row(sourceColumnNames, values);
}
private BlackboardAction createComputeTodos(int pageId, Page page) {
Level nextLevel = nextLevel(currentLevel);
Write write = null;
for (SegmentationContext context : segmentationContexts) {
context.putPage(pageId, page);
pageToBeComputedCount++;
boolean isLast = !hasNext && isLastContext(context);
if (write == null) {
write = write(createTodoAudit(isLast), SegmentationLevels.INFORMATION)
.then()
.write(createComputeTodo(pageId, context.getSegmentationId()), nextLevel);
}
else {
write.then()
.write(createTodoAudit(isLast), SegmentationLevels.INFORMATION)
.then()
.write(createComputeTodo(pageId, context.getSegmentationId()), nextLevel);
}
}
return write;
}
private Todo<TodoContent> createComputeTodo(int pageId, int segmentationId) {
TodoContent todoContent = currentTodo.getContent();
return new Todo<TodoContent>(new TodoContent(todoContent, segmentationId, pageId));
}
String buildSelectQuery(FamilyContext context) throws UnknownVariableException {
fieldInfoList = new DefaultFieldInfoList();
QueryBuilder queryBuilder
= QueryBuilderFactory.newSelectQueryBuilder(familyPreference.getSelectConfig());
List<String> fields = new ArrayList<String>(fieldsUsedInExpressions);
if (familyPreference.hasFilter()) {
RowFilter filter = familyPreference.getFilter();
fields.add(SegmentationUtil.determineFullColumnName(filter.getTableName(),
filter.getColumnName()));
}
return SegmentationUtil.buildSelectQuery(queryBuilder,
fields,
context.getParameters(),
fieldInfoList);
}
private PageStructure createPageStructure(ResultSetMetaData metaData)
throws SQLException {
int columnCount = fieldInfoList.size();
sourceColumnNames = new String[columnCount];
Map<String, Integer> columnTypesByName = new HashMap<String, Integer>(columnCount);
for (int i = 0; i < fieldInfoList.size(); i++) {
FieldInfo info = fieldInfoList.getFieldInfo(i);
String field = determineFullColumnName(info.getDBTableName(), info.getDBFieldName());
sourceColumnNames[i] = field;
columnTypesByName.put(field, metaData.getColumnType(i + 1));
}
return new PageStructure(columnTypesByName);
}
private boolean isLastContext(SegmentationContext context) {
return context == segmentationContexts.get(segmentationContexts.size() - 1);
}
private Todo<Arguments> createTodoAudit(boolean isLast) {
Arguments arguments = PaginatorParticipant.this.createAudit(new String[][]{
{MidAuditKey.LEVEL_KEY, currentLevel.getName()},
{MidAuditKey.FAMILY_KEY, familyPreference.getFamilyId()},
{MidAuditKey.PAGE_COUNT_KEY, Integer.toString(pageToBeComputedCount)},
{MidAuditKey.IS_LAST_KEY, Boolean.toString(isLast)}
});
return new Todo<Arguments>(arguments);
}
}
}