*/
@Override
public synchronized void replay(final ReplayListener listener, final ReplayOption... options) {
// Create a barrier that allows us to wait for synchronous replay
final CyclicBarrier barrier = new CyclicBarrier(2);
try {
InputStream input = null;
// First check if we have a .zip ...
if (this.file.getAbsolutePath().endsWith(".xstream")) {
input = new FileInputStream(this.file);
}
// ... and check if we have .xstream file
if (this.file.getAbsolutePath().endsWith(".zip")) {
this.loader = new ZIPLoader(this.file);
input = this.loader.getSessionInputStream();
}
// FIXED: #26
this.in = this.xstream.createObjectInputStream(new BufferedReader(new InputStreamReader(input, "UTF-8")));
} catch (final FileNotFoundException e) {
e.printStackTrace();
} catch (final IOException e) {
e.printStackTrace();
}
// Sanity check
if (this.in == null) {
this.logger.warning("Unable to load replay for file " + this.file);
return;
}
// Options
final AtomicBoolean gettingMetaInfo = new AtomicBoolean(false);
final AtomicBoolean realtimeReplay = new AtomicBoolean(false);
final AtomicBoolean loadImages = new AtomicBoolean(false);
// Some variables
final AtomicLong slowdownFactor = new AtomicLong(1);
final AtomicLong currentEvenTime = new AtomicLong();
final AtomicLong firstEventTime = new AtomicLong();
final AtomicLong realtimeDuration = new AtomicLong();
// Process options
final OptionUtils<ReplayOption> ou = new OptionUtils<ReplayOption>(options);
if (ou.contains(OptionGetMetaInfo.class)) gettingMetaInfo.set(true);
if (ou.contains(OptionRealtime.class)) realtimeReplay.set(true);
if (ou.contains(OptionLoadImages.class)) loadImages.set(true);
if (ou.contains(OptionSlowMotion.class)) {
realtimeReplay.set(true);
slowdownFactor.set(ou.get(OptionSlowMotion.class).getFactor());
}
// Create the actual replay thread
final Thread t = new Thread(new Runnable() {
private boolean hasMore = true;
@Override
public void run() {
try {
// Lock until we finished
SessionReplayImpl.this.finishedLock.lock();
// Synchronize with exit of the function
try {
barrier.await();
} catch (final InterruptedException e) {
e.printStackTrace();
} catch (final BrokenBarrierException e) {
e.printStackTrace();
}
AbstractSessionEvent previousEvent = null;
// As long as we have more events
while (this.hasMore) {
AbstractSessionEvent event = null;
try {
// Load the next event
event = (AbstractSessionEvent) loadFromStream(SessionReplayImpl.this.in);
// In case we have no previous event, save the first event time
if (previousEvent == null) {
firstEventTime.set(event.originalEventTime);
previousEvent = event;
}
// Store current event time
currentEvenTime.set(event.originalEventTime);
// Dont process if filtered
if (SessionReplayImpl.this.toFilter.contains(event.getClass())) {
continue;
}
// Check if we only get meta events ...
if (gettingMetaInfo.get()) {
if (event instanceof ScreenSizeEvent)
SessionReplayImpl.this.screenSize = ((ScreenSizeEvent) event).screenSize;
if (event instanceof PropertyEvent) {
SessionReplayImpl.this.propertyMap.put(((PropertyEvent) event).key, ((PropertyEvent) event).value);
}
continue;
}
// Can be switched off, to make replay as fast as possible.
if (realtimeReplay.get()) {
long delta = event.originalEventTime - previousEvent.originalEventTime;
// TODO: When does this happen?
if (delta < 0) {
SessionReplayImpl.this.logger.fine("Event times are mixed up " + event.originalEventTime + " < " + previousEvent.originalEventTime);
delta = 0;
}
// And now wait for the given time
try {
Thread.sleep(delta * slowdownFactor.get());
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
// Check what kind of event it is and if we have some special rules
if (event instanceof ImageEvent && loadImages.get()) {
final ImageEvent e = (ImageEvent) event;
final InputStream is = SessionReplayImpl.this.loader.getFile(e.associatedFilename);
final BufferedImage read = ImageIO.read(is);
event = new PseudoImageEvent(e, read);
}
// Now we are permitted to fire the event.
try {
listener.nextEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
previousEvent = event;
} catch (EOFException e) {
e.printStackTrace();
this.hasMore = false;
if (gettingMetaInfo.get()) {
realtimeDuration.set(currentEvenTime.get() - firstEventTime.get());
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
} finally {
SessionReplayImpl.this.finishedLock.unlock();
try {
barrier.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
t.setDaemon(true);
t.start();
// Synchronize with starting of thread.
try {
barrier.await();
} catch (final InterruptedException e) {
e.printStackTrace();
} catch (final BrokenBarrierException e) {
e.printStackTrace();
}