/*
* Copyright 2015 the original author or authors.
* @https://github.com/scouter-project/scouter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package scouter.client.maria.views;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;
import scouter.client.Images;
import scouter.client.model.AgentDailyListProxy;
import scouter.client.model.DigestModel;
import scouter.client.model.RefreshThread;
import scouter.client.model.RefreshThread.Refreshable;
import scouter.client.model.TextProxy;
import scouter.client.net.INetReader;
import scouter.client.net.TcpProxy;
import scouter.client.popup.DigestDetailDialog;
import scouter.client.sorter.TreeLabelSorter;
import scouter.client.util.ConsoleProxy;
import scouter.client.util.ExUtil;
import scouter.client.util.ImageUtil;
import scouter.client.util.TimeUtil;
import scouter.io.DataInputX;
import scouter.lang.DigestKey;
import scouter.lang.counters.CounterConstants;
import scouter.lang.pack.MapPack;
import scouter.lang.pack.Pack;
import scouter.lang.pack.PackEnum;
import scouter.lang.pack.StatusPack;
import scouter.lang.value.ListValue;
import scouter.lang.value.MapValue;
import scouter.net.RequestCmd;
import scouter.util.CastUtil;
import scouter.util.DateUtil;
import scouter.util.FormatUtil;
public class DigestTableView extends ViewPart implements Refreshable {
public final static String ID = DigestTableView.class.getName();
double PICO = Math.pow(10, -12);
int serverId;
Composite parent;
TreeViewer viewer;
TreeColumnLayout columnLayout;
AgentDailyListProxy agentProxy = new AgentDailyListProxy();
RefreshThread thread;
boolean isAutoRefresh = false;
String date;
long stime, etime;
HashMap<Integer, DigestModel> root = new HashMap<Integer, DigestModel>();
public void init(IViewSite site) throws PartInitException {
super.init(site);
String secId = site.getSecondaryId();
this.serverId = CastUtil.cint(secId);
}
public void createPartControl(Composite parent) {
this.parent = parent;
GridLayout gridlayout = new GridLayout(1, false);
gridlayout.marginHeight = 0;
gridlayout.horizontalSpacing = 0;
gridlayout.marginWidth = 0;
parent.setLayout(gridlayout);
columnLayout = new TreeColumnLayout();
Composite mainComp = new Composite(parent, SWT.NONE);
mainComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
mainComp.setLayout(columnLayout);
viewer = new TreeViewer(mainComp, SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
createColumns();
final Tree tree = viewer.getTree();
tree.setHeaderVisible(true);
tree.setLinesVisible(true);
viewer.setLabelProvider(new TreeLabelProvider());
viewer.setContentProvider(new TreeContentProvider());
viewer.setComparator(new TreeLabelSorter(viewer));
viewer.addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
StructuredSelection sel = (StructuredSelection) event.getSelection();
Object o = sel.getFirstElement();
if (o instanceof DigestModel) {
DigestModel model = (DigestModel) o;
new DigestDetailDialog().show(model, stime, etime, serverId);
}
}
});
viewer.setInput(root);
IToolBarManager man = getViewSite().getActionBars().getToolBarManager();
Action actAutoRefresh = new Action("Auto Refresh in 10 sec.", IAction.AS_CHECK_BOX){
public void run() {
isAutoRefresh = isChecked();
if (isAutoRefresh) {
thread.interrupt();
}
}
};
actAutoRefresh.setImageDescriptor(ImageUtil.getImageDescriptor(Images.refresh_auto));
man.add(actAutoRefresh);
long now = TimeUtil.getCurrentTime(serverId);
date = DateUtil.yyyymmdd(now);
stime = now - DateUtil.MILLIS_PER_FIVE_MINUTE;
etime = now;
loadQueryJob.schedule(2000);
thread = new RefreshThread(this, 10000);
thread.start();
}
public void refresh() {
if (isAutoRefresh) {
root.clear();
long now = TimeUtil.getCurrentTime(serverId);
date = DateUtil.yyyymmdd(now);
stime = now - DateUtil.MILLIS_PER_FIVE_MINUTE;
etime = now;
loadQueryJob.schedule();
}
}
public void setInput(long stime, long etime) {
if (loadQueryJob.getState() == Job.WAITING || loadQueryJob.getState() == Job.RUNNING) {
MessageDialog.openInformation(null, "STOP", "Previous loading is not yet finished");
return;
}
if (etime - stime < DateUtil.MILLIS_PER_MINUTE) {
stime = etime - DateUtil.MILLIS_PER_MINUTE;
}
root.clear();
this.stime = stime;
this.etime = etime;
this.date = DateUtil.yyyymmdd(stime);
loadQueryJob.schedule();
}
ArrayList<DigestSchema> columnList = new ArrayList<DigestSchema>();
private void createColumns() {
columnList.clear();
for (DigestSchema column : DigestSchema.values()) {
createTreeViewerColumn(column.getTitle(), column.getWidth(), column.getAlignment(), true, true, column.isNumber());
columnList.add(column);
}
}
private TreeViewerColumn createTreeViewerColumn(String title, int width, int alignment, boolean resizable, boolean moveable, final boolean isNumber) {
final TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
final TreeColumn column = viewerColumn.getColumn();
column.setText(title);
column.setAlignment(alignment);
column.setMoveable(moveable);
columnLayout.setColumnData(column, new ColumnPixelData(width, resizable));
column.setData("isNumber", isNumber);
column.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
TreeLabelSorter sorter = (TreeLabelSorter) viewer.getComparator();
TreeColumn selectedColumn = (TreeColumn) e.widget;
sorter.setColumn(selectedColumn);
}
});
return viewerColumn;
}
public void setFocus() {
}
Job loadQueryJob = new Job("Load Digest List...") {
HashMap<DigestKey, MapPack> summaryMap = new HashMap<DigestKey, MapPack>();
HashMap<Integer, StatusPack> firstStatusMap = new HashMap<Integer, StatusPack>();
HashMap<Integer, StatusPack> lastStatusMap = new HashMap<Integer, StatusPack>();
protected IStatus run(final IProgressMonitor monitor) {
summaryMap.clear();
firstStatusMap.clear();
lastStatusMap.clear();
monitor.beginTask(DateUtil.hhmmss(stime) + " ~ " + DateUtil.hhmmss(etime), 100);
TcpProxy tcp = TcpProxy.getTcpProxy(serverId);
try {
MapPack param = new MapPack();
ListValue objHashLv = agentProxy.getObjHashLv(date, serverId, CounterConstants.MARIA_PLUGIN);
if (objHashLv.size() > 0) {
param.put("objHash", objHashLv);
param.put("date", date);
param.put("time", stime);
List<Pack> firstList = tcp.process(RequestCmd.DB_LAST_DIGEST_TABLE, param);
for (Pack p : firstList) {
StatusPack s = (StatusPack) p;
firstStatusMap.put(s.objHash, s);
}
param.put("stime", stime);
param.put("etime", etime);
tcp.process(RequestCmd.DB_DIGEST_TABLE, param, new INetReader() {
public void process(DataInputX in) throws IOException {
Pack p = in.readPack();
switch (p.getPackType()) {
case PackEnum.MAP:
MapPack m = (MapPack) p;
if (m.containsKey("percent")) {
monitor.worked(m.getInt("percent"));
} else {
int objHash = m.getInt("objHash");
int digestHash = m.getInt("digestHash");
summaryMap.put(new DigestKey(objHash, digestHash), m);
}
break;
case PackEnum.PERF_STATUS:
StatusPack sp = (StatusPack) p;
lastStatusMap.put(sp.objHash, sp);
break;
}
}
});
}
} catch (Exception e) {
ConsoleProxy.errorSafe(e.toString());
} finally {
TcpProxy.putTcpProxy(tcp);
}
Iterator<Integer> itr = lastStatusMap.keySet().iterator();
while (itr.hasNext()) {
int objHash = itr.next();
StatusPack firstStatus = firstStatusMap.get(objHash);
StatusPack lastStatus = lastStatusMap.get(objHash);
HashMap<Integer, MapValue> firstMap = new HashMap<Integer, MapValue>();
if (firstStatus == null) {
// nothing
} else {
// index first values for delta
MapValue firstData = firstStatus.data;
ListValue firstDigestLv = firstData.getList("DIGEST_TEXT");
for (int i = 0; i < firstDigestLv.size(); i++) {
int digestHash = firstDigestLv.getInt(i);
MapValue valueMap = new MapValue();
Enumeration<String> keys = firstData.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
valueMap.put(key, firstData.getList(key).get(i));
}
firstMap.put(digestHash, valueMap);
}
}
MapValue data = lastStatus.data;
ListValue digestLv = data.getList("DIGEST_TEXT");
ListValue schemaNameLv = data.getList("SCHEMA_NAME");
ListValue executionLv = data.getList("COUNT_STAR");
ListValue timerWaitLv = data.getList("SUM_TIMER_WAIT");
ListValue lockTimeLv = data.getList("SUM_LOCK_TIME");
ListValue errorsLv = data.getList("SUM_ERRORS");
ListValue warnsLv = data.getList("SUM_WARNINGS");
ListValue rowsAffectedLv = data.getList("SUM_ROWS_AFFECTED");
ListValue rowsSentLv = data.getList("SUM_ROWS_SENT");
ListValue rowsExaminedLv = data.getList("SUM_ROWS_EXAMINED");
ListValue createdTmpDiskTablesLv = data.getList("SUM_CREATED_TMP_DISK_TABLES");
ListValue createdTmpTablesLv = data.getList("SUM_CREATED_TMP_TABLES");
ListValue selectFullJoin = data.getList("SUM_SELECT_FULL_JOIN");
ListValue selectFullRangeJoin = data.getList("SUM_SELECT_FULL_RANGE_JOIN");
ListValue selectRangeLv = data.getList("SUM_SELECT_RANGE");
ListValue selectRangeCheckLv = data.getList("SUM_SELECT_RANGE_CHECK");
ListValue selectScanLv = data.getList("SUM_SELECT_SCAN");
ListValue sortMergePassesLv = data.getList("SUM_SORT_MERGE_PASSES");
ListValue sortRangeLv = data.getList("SUM_SORT_RANGE");
ListValue sortRowsLv = data.getList("SUM_SORT_ROWS");
ListValue sortScanLv = data.getList("SUM_SORT_SCAN");
ListValue noIndexUsedLv = data.getList("SUM_NO_INDEX_USED");
ListValue noGoodIndexUsedLv = data.getList("SUM_NO_GOOD_INDEX_USED");
ListValue firstSeenLv = data.getList("FIRST_SEEN");
ListValue lastSeenLv = data.getList("LAST_SEEN");
for (int i = 0; i < digestLv.size(); i++) {
if (lastSeenLv.getLong(i) < stime || lastSeenLv.getLong(i) > etime) {
continue;
}
DigestModel model = new DigestModel();
int digestHash = digestLv.getInt(i);
MapPack m = summaryMap.get(new DigestKey(objHash, digestHash));
if (m == null) continue;
long maxTimerWait = m.getLong("MAX_TIMER_WAIT");
long minTimerWait = m.getLong("MIN_TIMER_WAIT");
long avgTimerWait = m.getLong("AVG_TIMER_WAIT");
int count = m.getInt("count");
model.objHash = objHash;
model.digestHash = digestHash;
model.name = TextProxy.object.getLoadText(date, objHash, serverId);
model.database = TextProxy.maria.getLoadText(date, schemaNameLv.getInt(i), serverId);
model.firstSeen = firstSeenLv.getLong(i);
model.lastSeen = lastSeenLv.getLong(i);
model.avgResponseTime = avgTimerWait / (double) count;
model.minResponseTime = minTimerWait;
model.maxResponseTime = maxTimerWait;
MapValue firstValue = firstMap.get(digestHash);
if (firstValue == null) {
firstValue = new MapValue();
}
model.execution = executionLv.getInt(i) - firstValue.getInt("COUNT_STAR");
if (model.execution < 1) {
System.out.println("first=>" + firstStatus);
System.out.println("last =>" + lastStatus);
}
model.errorCnt = errorsLv.getInt(i) - firstValue.getInt("SUM_ERRORS");
model.warnCnt = warnsLv.getInt(i) - firstValue.getInt("SUM_WARNINGS");
model.sumResponseTime = timerWaitLv.getLong(i) - firstValue.getLong("SUM_TIMER_WAIT");
model.lockTime = lockTimeLv.getLong(i) - firstValue.getLong("SUM_LOCK_TIME");
model.rowsAffected = rowsAffectedLv.getLong(i) - firstValue.getLong("SUM_ROWS_AFFECTED");
model.rowsSent = rowsSentLv.getLong(i) - firstValue.getLong("SUM_ROWS_SENT");
model.rowsExamined = rowsExaminedLv.getLong(i) - firstValue.getLong("SUM_ROWS_EXAMINED");
model.createdTmpDiskTables = createdTmpDiskTablesLv.getLong(i) - firstValue.getLong("SUM_CREATED_TMP_DISK_TABLES");
model.createdTmpTables = createdTmpTablesLv.getLong(i) - firstValue.getLong("SUM_CREATED_TMP_TABLES");
model.selectFullJoin = selectFullJoin.getLong(i) - firstValue.getLong("SUM_SELECT_FULL_JOIN");
model.selectFullRangeJoin = selectFullRangeJoin.getLong(i) - firstValue.getLong("SUM_SELECT_FULL_RANGE_JOIN");
model.selectRange = selectRangeLv.getLong(i) - firstValue.getLong("SUM_SELECT_RANGE");
model.selectRangeCheck = selectRangeCheckLv.getLong(i) - firstValue.getLong("SUM_SELECT_RANGE_CHECK");
model.selectScan = selectScanLv.getLong(i) - firstValue.getLong("SUM_SELECT_SCAN");
model.sortMergePasses = sortMergePassesLv.getLong(i) - firstValue.getLong("SUM_SORT_MERGE_PASSES");
model.sortRange =sortRangeLv.getLong(i) - firstValue.getLong("SUM_SORT_RANGE");
model.sortRows = sortRowsLv.getLong(i) - firstValue.getLong("SUM_SORT_ROWS");
model.sortScan = sortScanLv.getLong(i) - firstValue.getLong("SUM_SORT_SCAN");
model.noIndexUsed = noIndexUsedLv.getLong(i) - firstValue.getLong("SUM_NO_INDEX_USED");
model.noGoodIndexUsed = noGoodIndexUsedLv.getLong(i) - firstValue.getLong("SUM_NO_GOOD_INDEX_USED");
DigestModel parent = root.get(digestHash);
if (parent == null) {
parent = new DigestModel();
parent.digestHash = digestHash;
String digestTxt = TextProxy.maria.getLoadText(date, digestHash, serverId);
parent.name = digestTxt == null ? "unknown hash" : digestTxt;
root.put(digestHash, parent);
}
model.parent = parent;
parent.addChild(model);
}
}
Iterator<DigestModel> parents = root.values().iterator();
while (parents.hasNext()) {
DigestModel parent = parents.next();
DigestModel[] childs = parent.getChildArray();
if (childs != null) {
double sumAvg = 0.0d;
for (DigestModel child : childs) {
sumAvg += child.avgResponseTime;
}
parent.avgResponseTime = sumAvg / childs.length;
}
}
monitor.done();
ExUtil.exec(viewer.getTree(), new Runnable() {
public void run() {
DigestTableView.this.setContentDescription(DateUtil.format(stime, "MM-dd HH:mm:ss") + " ~ " + DateUtil.format(etime, "MM-dd HH:mm:ss") + " (" + root.size() + ")");
viewer.refresh();
}
});
return Status.OK_STATUS;
}
};
class TreeContentProvider implements ITreeContentProvider {
public void dispose() {
}
public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
}
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof DigestModel) {
DigestModel parent = (DigestModel) parentElement;
Object[] array = parent.getChildArray();
if(array != null) return array;
}
return new Object[0];
}
public Object[] getElements(Object inputElement) {
if (inputElement instanceof HashMap) {
HashMap map = (HashMap) inputElement;
Object[] objArray = new Object[map.size()];
Iterator itr = map.values().iterator();
int cnt = 0;
while (itr.hasNext()) {
objArray[cnt] = itr.next();
cnt++;
}
return objArray;
}
return new Object[0];
}
public Object getParent(Object element) {
if (element instanceof DigestModel) {
return ((DigestModel) element).parent;
}
return null;
}
public boolean hasChildren(Object element) {
if (element instanceof DigestModel) {
return ((DigestModel) element).getChildArray() != null;
}
return false;
}
}
class TreeLabelProvider implements ITableLabelProvider {
public void addListener(ILabelProviderListener listener) {
}
public void dispose() {
}
public boolean isLabelProperty(Object element, String property) {
return false;
}
public void removeListener(ILabelProviderListener listener) {
}
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
public String getColumnText(Object element, int columnIndex) {
if (element instanceof DigestModel) {
DigestModel model = (DigestModel) element;
DigestSchema column = columnList.get(columnIndex);
switch (column) {
case DIGEST_TEXT:
return model.name;
case SCHEMA_NAME:
return model.database;
case COUNT_STAR:
return FormatUtil.print(model.execution, "#,##0");
case SUM_ERRORS:
return FormatUtil.print(model.errorCnt, "#,##0");
case SUM_WARNINGS:
return FormatUtil.print(model.warnCnt, "#,##0");
case SUM_TIMER_WAIT:
return FormatUtil.print(model.sumResponseTime * PICO, "#,##0.00#");
case AVG_TIMER_WAIT:
return FormatUtil.print(model.avgResponseTime * PICO, "#,##0.00#");
case MIN_TIMER_WAIT:
return FormatUtil.print(model.minResponseTime * PICO, "#,##0.00#");
case MAX_TIMER_WAIT:
return FormatUtil.print(model.maxResponseTime * PICO, "#,##0.00#");
case SUM_LOCK_TIME:
return FormatUtil.print(model.lockTime * PICO, "#,##0.00#");
case SUM_ROWS_AFFECTED:
return FormatUtil.print(model.rowsAffected, "#,##0");
case SUM_ROWS_SENT:
return FormatUtil.print(model.rowsSent, "#,##0");
case SUM_ROWS_EXAMINED:
return FormatUtil.print(model.rowsExamined, "#,##0");
case SUM_CREATED_TMP_DISK_TABLES:
return FormatUtil.print(model.createdTmpDiskTables, "#,##0");
case SUM_CREATED_TMP_TABLES:
return FormatUtil.print(model.createdTmpTables, "#,##0");
case SUM_SELECT_FULL_JOIN:
return FormatUtil.print(model.selectFullJoin, "#,##0");
case SUM_SELECT_FULL_RANGE_JOIN:
return FormatUtil.print(model.selectFullRangeJoin, "#,##0");
case SUM_SELECT_RANGE:
return FormatUtil.print(model.selectRange, "#,##0");
case SUM_SELECT_RANGE_CHECK:
return FormatUtil.print(model.selectRangeCheck, "#,##0");
case SUM_SELECT_SCAN:
return FormatUtil.print(model.selectScan, "#,##0");
case SUM_SORT_MERGE_PASSES:
return FormatUtil.print(model.sortMergePasses, "#,##0");
case SUM_SORT_RANGE:
return FormatUtil.print(model.sortRange, "#,##0");
case SUM_SORT_ROWS:
return FormatUtil.print(model.sortRows, "#,##0");
case SUM_SORT_SCAN:
return FormatUtil.print(model.sortScan, "#,##0");
case SUM_NO_INDEX_USED:
return FormatUtil.print(model.noIndexUsed, "#,##0");
case SUM_NO_GOOD_INDEX_USED:
return FormatUtil.print(model.noGoodIndexUsed, "#,##0");
case FIRST_SEEN:
return DateUtil.timestamp(model.firstSeen);
case LAST_SEEN:
return DateUtil.timestamp(model.lastSeen);
}
}
return null;
}
}
public void dispose() {
super.dispose();
if (loadQueryJob != null && (loadQueryJob.getState() == Job.WAITING || loadQueryJob.getState() == Job.RUNNING)) {
loadQueryJob.cancel();
}
}
public static void main(String[] args) {
System.out.println(139871834183L * Math.pow(10, -12));
}
}