package er.extensions.eof;
import java.util.LinkedList;
import org.apache.log4j.Logger;
import com.webobjects.eoaccess.EOAdaptorChannel;
import com.webobjects.eoaccess.EODatabaseChannel;
import com.webobjects.eoaccess.EODatabaseContext;
import com.webobjects.eoaccess.EOSQLExpression;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSNotification;
import com.webobjects.foundation.NSNotificationCenter;
import com.webobjects.foundation.NSSelector;
import er.extensions.foundation.ERXProperties;
import er.extensions.foundation.ERXRuntimeUtilities;
/**
* Tracks and logs the SQL that gets sent to the database. If the milliseconds
* used exceeds the time specified in the system property
* <code>er.extensions.ERXSQLExpressionTracker.trace.milliSeconds.[debug|info|warn|error]</code>,
* and the entity name matches the regular expression
* <code>er.extensions.ERXSQLExpressionTracker.trace.entityMatchPattern</code>
* then the SQL expression is logged together with the time used and the
* parameters. <br />
* NOTE: To get patched into EOF, this class registers itself for the
* <code>EODatabaseContext.DatabaseChannelNeededNotification</code>
* notification and creates a new channel. If you would like to handle creation
* of the channel yourself *and* you need the logging feature, you need to:
* <ul>
* <li>set the er.extensions.ERXAdaptorChannelDelegate.enabled=false in your
* properties, which will prevent creation of the channel here
* <li>create the channel yourself and set the delegate to
* {@link new ERXAdaptorChannelDelegate()}
* </ul>
* otherwise you just need to set
* er.extensions.ERXAdaptorChannelDelegate.enabled=true
*
* @author ak
*/
public class ERXAdaptorChannelDelegate {
private static Logger log = Logger.getLogger(ERXAdaptorChannelDelegate.class);
private long _lastMilliseconds;
private LinkedList _lastStatements;
private Boolean _collectLastStatements;
private Integer _numberOfStatementsToCollect;
public static void setupDelegate() {
NSNotificationCenter.defaultCenter().addObserver(ERXAdaptorChannelDelegate.class,
new NSSelector("dataBaseChannelNeeded", ERXConstant.NotificationClassArray),
EODatabaseContext.DatabaseChannelNeededNotification, null);
}
/**
* Implemented so the the thread checks if it should get interrupted.
* @param eoadaptorchannel
* @param nsmutabledictionary
*/
public void adaptorChannelDidFetchRow(EOAdaptorChannel eoadaptorchannel, NSMutableDictionary nsmutabledictionary) {
ERXRuntimeUtilities.checkThreadInterrupt();
}
public void adaptorChannelDidEvaluateExpression(EOAdaptorChannel channel, EOSQLExpression expression) {
if (collectLastStatements()) {
// this collects the last 10 statements executed for dumping them
if (_lastStatements == null) {
_lastStatements = new LinkedList<String>();
}
_lastStatements.addLast(ERXEOAccessUtilities.createLogString(channel, expression, System.currentTimeMillis() - _lastMilliseconds));
while (_lastStatements.size() > numberOfStatementsToCollect()) {
_lastStatements.removeFirst();
}
}
ERXEOAccessUtilities.logExpression(channel, expression, _lastMilliseconds);
}
private int numberOfStatementsToCollect () {
if (_numberOfStatementsToCollect == null) {
_numberOfStatementsToCollect = Integer.valueOf(ERXProperties.intForKeyWithDefault("er.extensions.ERXSQLExpressionTracker.numberOfStatementsToCollect", 10));
}
return _numberOfStatementsToCollect.intValue();
}
private boolean collectLastStatements () {
if (_collectLastStatements == null) {
_collectLastStatements = new Boolean (ERXProperties.booleanForKeyWithDefault("er.extensions.ERXSQLExpressionTracker.collectLastStatements", false));
}
return _collectLastStatements.booleanValue();
}
public boolean adaptorChannelShouldEvaluateExpression(EOAdaptorChannel channel, EOSQLExpression expression) {
_lastMilliseconds = System.currentTimeMillis();
return true;
}
/**
* Answers to the EODataBaseChannelNeeded notification.
* Creates a new EODatabaseChannel and sets its adaptorChannel delegate
* to a new instance of ERXAdaptorChannelDelegate.
* @param n
*/
static public void dataBaseChannelNeeded(NSNotification n) {
if (ERXProperties.booleanForKeyWithDefault("er.extensions.ERXAdaptorChannelDelegate.enabled", false)) {
EODatabaseContext context = (EODatabaseContext) n.object();
EODatabaseChannel channel = new EODatabaseChannel(context);
context.registerChannel(channel);
channel.adaptorChannel().setDelegate(new ERXAdaptorChannelDelegate());
}
}
/**
* Dump the last collected statements to the log. Use the property
* <code>er.extensions.ERXSQLExpressionTracker.collectLastStatements</code>
* set to true to collect executed statements.
*/
public synchronized void dumpLastStatements() {
log.info("******* dumping collected SQL statements *******");
if (_lastStatements != null) {
for (int i = 0; i < _lastStatements.size(); i++) {
log.info(_lastStatements.get(i));
}
}
else {
log.info("No collected statements available.");
if (!_collectLastStatements.booleanValue()) {
log.info("You have to set the property 'er.extensions.ERXSQLExpressionTracker.collectLastStatements = true'. to make this feature work.");
}
}
_lastStatements = new LinkedList<String>();
log.info("************************************************");
}
/**
* Return the last collected SQL statements
* @author cug - Jun 20, 2007
* @return The last collected SQL statements.
*/
public LinkedList<String> lastStatements() {
return _lastStatements;
}
}