/*
* JCarder -- cards Java programs to keep threads disentangled
*
* Copyright (C) 2006-2007 Enea AB
* Copyright (C) 2007 Ulrik Svensson
* Copyright (C) 2007 Joel Rosdahl
*
* This program is made available under the GNU GPL version 2, with a special
* exception for linking with JUnit. See the accompanying file LICENSE.txt for
* details.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
package com.enea.jcarder.agent;
import static com.enea.jcarder.common.contexts.ContextFileReader.EVENT_DB_FILENAME;
import static com.enea.jcarder.common.contexts.ContextFileReader.CONTEXTS_DB_FILENAME;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import net.jcip.annotations.ThreadSafe;
import com.enea.jcarder.common.LockingContext;
import com.enea.jcarder.common.contexts.ContextFileWriter;
import com.enea.jcarder.common.contexts.ContextWriterIfc;
import com.enea.jcarder.common.events.EventFileWriter;
import com.enea.jcarder.common.events.LockEventListenerIfc;
import com.enea.jcarder.util.Counter;
import com.enea.jcarder.util.logging.Logger;
@ThreadSafe
final class EventListener implements EventListenerIfc {
private final ThreadLocalEnteredMonitors mEnteredMonitors;
private final LockEventListenerIfc mLockEventListener;
private final LockIdGenerator mLockIdGenerator;
private final LockingContextIdCache mContextCache;
private final Logger mLogger;
private final Counter mNumberOfEnteredMonitors;
public static EventListener create(Logger logger, File outputdir)
throws IOException {
EventFileWriter eventWriter =
new EventFileWriter(logger,
new File(outputdir, EVENT_DB_FILENAME));
ContextFileWriter contextWriter =
new ContextFileWriter(logger,
new File(outputdir, CONTEXTS_DB_FILENAME));
return new EventListener(logger, eventWriter, contextWriter);
}
public EventListener(Logger logger,
LockEventListenerIfc lockEventListener,
ContextWriterIfc contextWriter) {
mLogger = logger;
mEnteredMonitors = new ThreadLocalEnteredMonitors();
mLockEventListener = lockEventListener;
mLockIdGenerator = new LockIdGenerator(mLogger, contextWriter);
mContextCache = new LockingContextIdCache(mLogger, contextWriter);
mNumberOfEnteredMonitors =
new Counter("Entered Monitors", mLogger, 100000);
}
public void beforeMonitorEnter(Object monitor, LockingContext context)
throws Exception {
mLogger.finest("EventListener.beforeMonitorEnter");
Iterator<EnteredMonitor> iter = mEnteredMonitors.getIterator();
while (iter.hasNext()) {
Object previousEnteredMonitor = iter.next().getMonitorIfStillHeld();
if (previousEnteredMonitor == null) {
iter.remove();
} else if (previousEnteredMonitor == monitor) {
return; // Monitor already entered.
}
}
enteringNewMonitor(monitor, context);
}
private synchronized void enteringNewMonitor(Object monitor,
LockingContext context)
throws Exception {
mNumberOfEnteredMonitors.increment();
int newLockId = mLockIdGenerator.acquireLockId(monitor);
int newContextId = mContextCache.acquireContextId(context);
EnteredMonitor lastMonitor = mEnteredMonitors.getFirst();
if (lastMonitor != null) {
Thread performingThread = Thread.currentThread();
mLockEventListener.onLockEvent(newLockId,
newContextId,
lastMonitor.getLockId(),
lastMonitor.getLockingContextId(),
performingThread.getId());
}
mEnteredMonitors.addFirst(new EnteredMonitor(monitor,
newLockId,
newContextId));
}
}