blob: 3e9917bb8d38d72b44475c9998d0c1154d2eb4e5 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// dclarke - Dynamic Persistence
// http://wiki.eclipse.org/EclipseLink/Development/Dynamic
// (https://bugs.eclipse.org/bugs/show_bug.cgi?id=200045)
// - initial JPA Employee example using XML (bug 217884)
// ported from earlier Oracle TopLink examples
// mnorman - tweaks to work from Ant command-line,
// get database properties from System, etc.
//
package org.eclipse.persistence.testing.tests.dynamic;
//javase imports
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
//EclipseLink imports
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.logging.DefaultSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.logging.SessionLogEntry;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.SessionEvent;
import org.eclipse.persistence.sessions.SessionEventAdapter;
/**
*
* @author dclarke
* @since EclipseLink 1.1.2
*/
public class QuerySQLTracker extends SessionEventAdapter {
private List<QueryResult> queries;
/**
* Constructs and installs the event listener and sql tracking session log
*
*/
private QuerySQLTracker(Session session) {
session.getEventManager().addListener(this);
session.setSessionLog(new SQLTrackingSessionLog(session, this));
reset();
}
public static QuerySQLTracker install(Session session) {
if (session.getSessionLog() instanceof SQLTrackingSessionLog) {
return ((SQLTrackingSessionLog) session.getSessionLog())
.getTracker();
}
return new QuerySQLTracker(session);
}
/**
* Helper method to retrieve a tracker from a session where it was installed
* If the session exists but does not have a tracler installed then an
* exception is thrown.
*/
public static QuerySQLTracker getTracker(Session session) {
if (session == null) {
return null;
}
SessionLog sessionLog = session.getSessionLog();
if (sessionLog instanceof QuerySQLTracker.SQLTrackingSessionLog) {
return ((QuerySQLTracker.SQLTrackingSessionLog) sessionLog)
.getTracker();
}
throw new RuntimeException(
"Could not retireve QuerySQLTracke from session: " + session);
}
/**
* Reset the lists of SQL and queries being tracked
*/
public void reset() {
this.queries = new ArrayList<QueryResult>();
}
public List<QueryResult> getQueries() {
return this.queries;
}
protected QuerySQLTracker.QueryResult getCurrentResult() {
if (getQueries().size() == 0) {
getQueries().add(new QueryResult(null));
// throw new RuntimeException("Received SQL without a Query ???");
}
return getQueries().get(getQueries().size() - 1);
}
public int getTotalSQLCalls() {
int totalSQLCalls = 0;
for (QueryResult result : getQueries()) {
totalSQLCalls += result.sqlStatements.size();
}
return totalSQLCalls;
}
public int getTotalSQLCalls(String startsWith) {
int sqlCalls = 0;
for (QueryResult result : getQueries()) {
for (String sql : result.sqlStatements) {
String sub = sql.substring(0, startsWith.length());
if (sub.equalsIgnoreCase(startsWith)) {
sqlCalls++;
}
}
}
return sqlCalls;
}
public int getTotalSQLSELECTCalls() {
return getTotalSQLCalls("SELECT");
}
public int getTotalSQLINSERTCalls() {
return getTotalSQLCalls("INSERT");
}
public int getTotalSQLUPDATECalls() {
return getTotalSQLCalls("UPDATE");
}
public int getTotalSQLDELETECalls() {
return getTotalSQLCalls("DELETE");
}
@Override
public void preExecuteQuery(SessionEvent event) {
//System.err.println("*** QuerySQLTracker.preExecuteQuery(" + event.getQuery() + ")");
//Thread.dumpStack();
QueryResult result = new QueryResult(event.getQuery());
getQueries().add(result);
}
@Override
public void postExecuteQuery(SessionEvent event) {
if (getCurrentResult().query == null) {
getCurrentResult().setQuery(event.getQuery());
}
getCurrentResult().setResult(event.getResult());
}
protected class QueryResult {
private DatabaseQuery query;
private String resultString = null;
private List<String> sqlStatements = new ArrayList<String>();
QueryResult(DatabaseQuery q) {
query = q;
}
protected void setQuery(DatabaseQuery query) {
this.query = query;
}
@SuppressWarnings("unchecked")
protected void setResult(Object queryResult) {
StringWriter writer = new StringWriter();
writer.write(Helper.getShortClassName(query));
writer.write("[" + System.identityHashCode(query) + "]");
writer.write(" result = ");
Object result = queryResult;
if (queryResult instanceof Collection) {
result = ((Collection) queryResult).toArray();
}
if (result == null) {
writer.write("NONE");
} else {
if (result instanceof Object[]) {
Object[] results = (Object[]) result;
writer.write("<" + results.length + "> [");
for (int index = 0; index < results.length; index++) {
if (index > 0) {
writer.write(", ");
}
writer.write(results[index] + "");
}
writer.write("]");
resultString = writer.toString();
} else {
writer.write(result.toString());
}
}
this.resultString = writer.toString();
}
public void addSQL(String sql) {
sqlStatements.add(sql);
}
public String toString() {
if (this.resultString == null) {
setResult(null);
}
return this.resultString;
}
}
/**
* This custom SessionLog implementation wraps the existing one and redirects
* all SQL calls to the tracker. All messages are also passed to the original
* tracker.
*/
public class SQLTrackingSessionLog extends DefaultSessionLog {
private QuerySQLTracker tracker;
private SessionLog originalLog;
protected SQLTrackingSessionLog(Session session,
QuerySQLTracker aTracker) {
this.tracker = aTracker;
this.originalLog = session.getSessionLog();
setSession(session);
setWriter(this.originalLog.getWriter());
}
public QuerySQLTracker getTracker() {
return this.tracker;
}
@Override
public synchronized void log(SessionLogEntry entry) {
if (entry.getNameSpace() != null
&& entry.getNameSpace().equalsIgnoreCase(SessionLog.SQL)) {
getTracker().getCurrentResult().addSQL(entry.getMessage());
}
super.log(entry);
}
@Override
public int getLevel(String category) {
return this.originalLog.getLevel(category);
}
@Override
public void setLevel(int level, String category) {
this.originalLog.setLevel(level, category);
}
@Override
public int getLevel() {
return this.originalLog.getLevel();
}
@Override
public void setLevel(int level) {
this.originalLog.setLevel(level);
}
@Override
public boolean shouldPrintConnection() {
return this.originalLog.shouldPrintConnection();
}
@Override
public boolean shouldPrintDate() {
return this.originalLog.shouldPrintDate();
}
@Override
public boolean shouldPrintSession() {
return this.originalLog.shouldPrintSession();
}
@Override
public boolean shouldPrintThread() {
return this.originalLog.shouldPrintThread();
}
}
public void printResults(String prefix) {
System.out.println(prefix + "-QuerySQLTracker-Queries:");
int sql = 0;
for (int index = 0; index < getQueries().size(); index++) {
QueryResult result = getQueries().get(index);
System.out.println("\t" + (index + 1) + "> " + result);
for (int sqlNum = 0; sqlNum < result.sqlStatements.size(); sqlNum++) {
sql++;
System.out.println("\t\t" + (index + 1) + "." + (sqlNum + 1)
+ "-" + sql + "> " + result.sqlStatements.get(sqlNum));
}
}
System.out.println(prefix + "-QuerySQLTracker-Queries: "
+ getQueries().size());
System.out.println(prefix + "-QuerySQLTracker-INSERT: "
+ getTotalSQLINSERTCalls());
System.out.println(prefix + "-QuerySQLTracker-SELECT: "
+ getTotalSQLSELECTCalls());
System.out.println(prefix + "-QuerySQLTracker-UPDATE: "
+ getTotalSQLUPDATECalls());
System.out.println(prefix + "-QuerySQLTracker-DELETE: "
+ getTotalSQLDELETECalls());
}
}