/*
* codjo.net
*
* Common Apache License 2.0
*/
package net.codjo.segmentation.server.participant;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import net.codjo.segmentation.common.MidAuditKey;
import net.codjo.segmentation.common.message.SegmentationJobRequest;
import net.codjo.segmentation.server.blackboard.ErrorLogLimiter;
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.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.SessionContext;
import net.codjo.segmentation.server.participant.context.TaskTemplate;
import net.codjo.segmentation.server.participant.context.TodoContent;
import net.codjo.segmentation.server.preference.family.TableMetaData;
import net.codjo.segmentation.server.preference.family.XmlFamilyPreference;
import net.codjo.segmentation.server.preference.treatment.Expression;
import net.codjo.segmentation.server.preference.treatment.SegmentationPreference;
import net.codjo.segmentation.server.util.SegmentationUtil;
import net.codjo.workflow.common.message.Arguments;
import net.codjo.workflow.common.message.JobRequest;
/**
* Decoupe la JobRequest en todos par famille
*/
public class JobRequestAnalyzerParticipant extends SegmentationParticipant<JobRequest> {
private static final String SEGMENTATIONS = SegmentationJobRequest.SEGMENTATION_IDS;
private static final String SEGMENTATION_ID = "segmentationId";
private final long timeWindowValue;
private final TimeUnit timeWindowUnit;
public JobRequestAnalyzerParticipant(ContextManager contextManager, long timeWindowValue, TimeUnit timeWindowUnit) {
super(contextManager, TransactionType.AUTO_COMMIT, SegmentationLevels.FIRST);
this.timeWindowValue = timeWindowValue;
this.timeWindowUnit = timeWindowUnit;
}
@Override
protected void handleTodo(final Todo<JobRequest> todo, final Level fromLevel, final Connection connection) {
final SegmentationReport report = createReport();
new TaskTemplate(report, getName()) {
@Override
protected void doRun() throws Exception {
logger.info("Analyse de la requete : " + todo.getContent());
SessionContext sessionContext = new SessionContext(report, timeWindowValue, timeWindowUnit);
contextManager.put(todo.getContent().getId(), sessionContext);
final Arguments arguments = todo.getContent().getArguments();
String[] segmentationIds = extractSegmentationId(arguments);
for (final String segmentationId : segmentationIds) {
new TaskTemplate(this, "segment") {
protected void doRun() throws Exception {
Map<String, String> parameters = new TreeMap<String, String>();
parameters.putAll(arguments.toMap());
parameters.put(SEGMENTATION_ID, segmentationId);
parameters.remove(SEGMENTATIONS);
SegmentationPreference segmentationPreference =
SegmentationPreference.createPreference(connection, Integer.parseInt(segmentationId),
parameters);
FamilyContext familyContext = getFamilyContext(connection, segmentationPreference, todo);
SegmentationUtil.checkParameters(familyContext.getFamilyPreference().getArgumentNameList(),
arguments.toMap());
familyContext.putSegmentationContext(new SegmentationContext(
segmentationPreference.getSegmentationId(),
familyContext.getFamilyPreference(),
segmentationPreference,
parameters));
}
}.run();
}
Level nextLevel = nextLevel(fromLevel);
if (hasComputeConflict(sessionContext, todo, fromLevel)) {
return;
}
BlackboardAction action = erase(todo, fromLevel);
StringBuffer ids = new StringBuffer();
for (String familyId : sessionContext.getFamilyIds()) {
action.then().write(createTodo(todo, familyId), nextLevel);
appendToBuffer(ids, familyId);
}
createAudit(action, fromLevel, ids);
send(action);
}
@Override
protected void handleException(Exception e) {
logger.fatal("Analyse en erreur : " + todo.getContent(), e);
send(informOfFailure(todo, fromLevel).dueTo(e));
}
}.run();
}
private boolean hasComputeConflict(SessionContext session, Todo<JobRequest> todo, Level fromLevel) {
session.getFamilyContexts();
for (FamilyContext familyContext : session.getFamilyContexts()) {
for (SegmentationContext segmentationContext : familyContext.getSegmentationContexts()) {
SegmentationContext conflictContext = hasComputeConflict(session, segmentationContext);
if (conflictContext != null) {
send(informOfFailure(todo, fromLevel)
.dueTo("L'axe '" + conflictContext.getSegmentationPreference().getSegmentationName()
+ "' est en cours de calcul."));
return true;
}
}
}
return false;
}
private SegmentationContext hasComputeConflict(SessionContext current,
SegmentationContext currentSegmentation) {
for (SessionContext session : contextManager.getSessions()) {
if (session == current) {
continue;
}
for (FamilyContext familyContext : session.getFamilyContexts()) {
for (SegmentationContext segmentationContext : familyContext.getSegmentationContexts()) {
if (segmentationContext.getSegmentationId() == currentSegmentation.getSegmentationId()) {
return segmentationContext;
}
}
}
}
return null;
}
private void createAudit(BlackboardAction action, Level fromLevel, StringBuffer ids) {
Arguments arguments = createAudit(new String[][]{
{MidAuditKey.LEVEL_KEY, fromLevel.getName()},
{MidAuditKey.FAMILY_KEY, ids.toString()}});
action.then().write(new Todo<Arguments>(arguments), SegmentationLevels.INFORMATION);
}
private String[] extractSegmentationId(Arguments arguments) {
String segmentationIdList = arguments.get(SEGMENTATIONS);
if (segmentationIdList == null) {
throw new IllegalArgumentException("L'argument '" + SEGMENTATIONS
+ "' listant les axes est absent de la requ�te.");
}
return segmentationIdList.replaceAll(" ", "").split(",");
}
private Todo<TodoContent> createTodo(Todo<JobRequest> initialTodo, String familyId) {
return new Todo<TodoContent>(new TodoContent(initialTodo.getContent().getId(), familyId));
}
private FamilyContext getFamilyContext(Connection connection,
SegmentationPreference segmentationPreference,
Todo<JobRequest> todo)
throws SQLException {
JobRequest jobRequest = todo.getContent();
String familyId = segmentationPreference.getFamily();
SessionContext sessionContext = contextManager.get(jobRequest.getId());
FamilyContext familyContext = sessionContext.get(familyId);
if (familyContext != null) {
return familyContext;
}
XmlFamilyPreference familyPreference = contextManager.getFamilyPreference(familyId);
if (familyPreference == null) {
throw new SQLException("no familyPreference found for familyId=" + familyId);
}
familyContext = new FamilyContext(familyPreference, jobRequest.getArguments().toMap());
sessionContext.put(familyId, familyContext);
// Todo : attention ce code n'est pas Thread safe
// il est possible que le metaData soit charg� plusieurs fois
if (familyPreference.getTableMetaData() == null) {
TableMetaData tableMetaData =
TableMetaData.create(familyPreference.getDestinationTable(), connection);
List<String> usedColumnNames = getDestinationFieldsOf(segmentationPreference.getExpressions());
tableMetaData.removeUnusedColumns(usedColumnNames);
familyContext.getFamilyPreference().setTableMetaData(tableMetaData);
}
return familyContext;
}
private List<String> getDestinationFieldsOf(List<Expression> expressions) {
List<String> result = new ArrayList<String>();
for (Expression expression : expressions) {
result.add(expression.getDestinationField());
}
return result;
}
private void appendToBuffer(StringBuffer ids, String familyId) {
if (ids.length() > 0) {
ids.append(',');
}
ids.append(familyId);
}
@Override
protected final ErrorLogLimiter getErrorLogLimiter(Todo<JobRequest> todo) {
SessionContext context = contextManager.getSessionContext(todo.getContent().getId());
return (context == null) ? ErrorLogLimiter.NONE : context.getErrorLogLimiter();
}
}