/*******************************************************************************
* Copyright (c) 2012-2015 INRIA.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Generoso Pagano - initial API and implementation
******************************************************************************/
package fr.inria.soctrace.framesoc.ui.eventtable.loader;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.inria.soctrace.framesoc.ui.loaders.LoaderUtils;
import fr.inria.soctrace.framesoc.ui.model.LoaderQueue;
import fr.inria.soctrace.framesoc.ui.model.TimeInterval;
import fr.inria.soctrace.lib.model.Event;
import fr.inria.soctrace.lib.model.Trace;
import fr.inria.soctrace.lib.model.utils.SoCTraceException;
import fr.inria.soctrace.lib.query.EventQuery;
import fr.inria.soctrace.lib.query.conditions.ConditionsConstants.ComparisonOperation;
import fr.inria.soctrace.lib.query.conditions.ConditionsConstants.LogicalOperation;
import fr.inria.soctrace.lib.query.conditions.ConditionsConstants.OrderBy;
import fr.inria.soctrace.lib.query.conditions.LogicalCondition;
import fr.inria.soctrace.lib.query.conditions.SimpleCondition;
import fr.inria.soctrace.lib.storage.DBObject;
import fr.inria.soctrace.lib.storage.TraceDBObject;
import fr.inria.soctrace.lib.utils.DeltaManager;
/**
* Default event loader for the Gantt Chart.
*
* @author "Generoso Pagano <generoso.pagano@inria.fr>"
*/
public class EventLoader implements IEventLoader {
// logger
private static final Logger logger = LoggerFactory.getLogger(EventLoader.class);
// constants
private final int EVENTS_PER_QUERY = 100000;
// set by the user
private Trace fTrace = null;
private LoaderQueue<Event> fQueue = null;
// current visualized trace data
private TraceDBObject fTraceDB = null;
private EventQuery fQuery = null;
private TimeInterval fTimeInterval;
@Override
public void setTrace(Trace trace) {
if (fTrace != trace) {
clean();
fTrace = trace;
}
}
@Override
public void setQueue(LoaderQueue<Event> queue) {
fQueue = queue;
}
@Override
public void loadWindow(long start, long end, IProgressMonitor monitor) {
try {
Assert.isNotNull(fTrace, "Null trace in event loader");
Assert.isNotNull(fQueue, "Null queue in event loader");
start = Math.max(fTrace.getMinTimestamp(), start);
end = Math.min(fTrace.getMaxTimestamp(), end);
fTimeInterval = new TimeInterval(Long.MAX_VALUE, Long.MIN_VALUE);
// compute interval duration
long traceDuration = fTrace.getMaxTimestamp() - fTrace.getMinTimestamp();
long intervalDuration = LoaderUtils.getIntervalDuration(fTrace, EVENTS_PER_QUERY);
int totalWork = (int) ((double) traceDuration / intervalDuration);
// read the time window, interval by interval
monitor.beginTask("Loading Event Table", totalWork);
int oldWorked = 0;
/*
* XXX Current solution (may change). The table only loads the events with timestamp
* contained in the interval. So there is no difference between the first interval and
* the other.
*/
// boolean first = true;
long t0 = start;
while (t0 < end) {
// check if cancelled
if (checkCancel(monitor)) {
return;
}
// load interval
long t1 = Math.min(end, t0 + intervalDuration);
// List<Event> events = loadInterval(first, (t1 >= end), t0, t1, monitor);
boolean last = (t1 >= end);
List<Event> events = loadInterval(false, last, t0, t1, monitor);
debug(events);
if (checkCancel(monitor)) {
return;
}
// check for empty regions
if (events.size() == 0 && !last) {
t0 = getNextTimestampStartingFrom(t1);
logger.debug("saved " + ((t0 - t1) / intervalDuration) + " queries.");
continue;
}
fTimeInterval.startTimestamp = Math.min(fTimeInterval.startTimestamp, t0);
fTimeInterval.endTimestamp = Math.max(fTimeInterval.endTimestamp, t1);
fQueue.push(events, new TimeInterval(fTimeInterval));
// update progress monitor
int worked = (int) ((double) (t0 - start) / intervalDuration);
monitor.worked(Math.max(0, worked - oldWorked));
oldWorked = worked;
t0 = t1;
// first = false;
}
fQueue.setComplete();
} finally {
if (!fQueue.isStop() && !fQueue.isComplete()) {
// something went wrong, respect the queue contract anyway
fQueue.setStop();
}
monitor.done();
clean();
}
}
private List<Event> loadInterval(boolean first, boolean last, long t0, long t1,
IProgressMonitor monitor) {
ComparisonOperation endComp = (last) ? ComparisonOperation.LE : ComparisonOperation.LT;
try {
EventQuery query = getQueryObject();
query.clear();
if (first && t0 != fTrace.getMinTimestamp()) {
// first interval
LogicalCondition or = new LogicalCondition(LogicalOperation.OR);
// punctual events and variables: t0 <= t < t1 (last interval: t0 <= t <= t1)
LogicalCondition andPunct = new LogicalCondition(LogicalOperation.AND);
andPunct.addCondition(new SimpleCondition("CATEGORY", ComparisonOperation.IN,
"(0, 3)"));
andPunct.addCondition(new SimpleCondition("TIMESTAMP", ComparisonOperation.GE,
String.valueOf(t0)));
andPunct.addCondition(new SimpleCondition("TIMESTAMP", endComp, String.valueOf(t1)));
// states and links: start < t1 and end >= t0 (last interval: start <= t1 and end >=
// t0)
LogicalCondition andDuration = new LogicalCondition(LogicalOperation.AND);
andDuration.addCondition(new SimpleCondition("CATEGORY", ComparisonOperation.IN,
"(1, 2)"));
andDuration.addCondition(new SimpleCondition("TIMESTAMP", endComp, String
.valueOf(t1)));
andDuration.addCondition(new SimpleCondition("LPAR", ComparisonOperation.GE, String
.valueOf(t0)));
or.addCondition(andPunct);
or.addCondition(andDuration);
query.setElementWhere(or);
} else {
// other intervals
// all events: t0 <= t < t1 (last interval: t0 <= t <= t1)
LogicalCondition and = new LogicalCondition(LogicalOperation.AND);
and.addCondition(new SimpleCondition("TIMESTAMP", ComparisonOperation.GE, String
.valueOf(t0)));
and.addCondition(new SimpleCondition("TIMESTAMP", endComp, String.valueOf(t1)));
query.setElementWhere(and);
}
query.setOrderBy("TIMESTAMP", OrderBy.ASC);
return fQuery.getList();
} catch (SoCTraceException e) {
e.printStackTrace();
fQueue.setStop();
}
return new ArrayList<>();
}
private boolean checkCancel(IProgressMonitor monitor) {
if (monitor.isCanceled()) {
fQueue.setStop();
return true;
}
return false;
}
private long getNextTimestampStartingFrom(long end) {
long next = end + 1;
try {
Statement stm = getTraceDB().getConnection().createStatement();
DeltaManager dm = new DeltaManager();
dm.start();
ResultSet rs = stm.executeQuery("SELECT MIN(TIMESTAMP) FROM EVENT WHERE TIMESTAMP >= "
+ end);
logger.debug(dm.endMessage("exec query"));
while (rs.next()) {
next = rs.getLong(1);
}
rs.close();
stm.close();
} catch (SQLException e) {
e.printStackTrace();
} catch (SoCTraceException e) {
e.printStackTrace();
}
return Math.max(next, end + 1);
}
private void clean() {
fTrace = null;
fQueue = null;
fQuery = null;
DBObject.finalClose(fTraceDB);
}
private EventQuery getQueryObject() throws SoCTraceException {
if (fQuery == null) {
fQuery = new EventQuery(getTraceDB());
}
return fQuery;
}
private TraceDBObject getTraceDB() throws SoCTraceException {
if (fTraceDB == null) {
Assert.isNotNull(fTrace, "Null trace in event loader");
fTraceDB = TraceDBObject.openNewInstance(fTrace.getDbName());
}
return fTraceDB;
}
private void debug(List<Event> events) {
for (Event event : events) {
logger.trace(event.toString());
}
}
}