/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.druid.support.jconsole;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import javax.management.MBeanServerConnection;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.border.TitledBorder;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import com.alibaba.druid.support.jconsole.model.DruidTableCellRenderer;
import com.alibaba.druid.support.jconsole.model.DruidTableModel;
import com.alibaba.druid.support.jconsole.util.TableDataProcessor;
import com.alibaba.druid.support.jconsole.util.TableDataProcessor.ColumnData;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
// TODO: Auto-generated Javadoc
/**
* sql语句的详细信息
*
* 调用服务地址:/sql-{id}.json
* 返回信息:
* <pre>
* {
"ResultCode": 1,
"Content": {
"ExecuteCount": 15,
"ReadStringLength": 0,
"LastSlowParameters": null,
"BlobOpenCount": 0,
"InputStreamOpenCount": 0,
"DbType": "mysql",
"DataSource": null,
"RunningCount": 0,
"parsedRelationships": "[]",
"ConcurrentMax": 1,
"LastErrorClass": null,
"MaxTimespan": 125,
"InTransactionCount": 0,
"ID": 3,
"parsedFields": "[]",
"EffectedRowCount": 0,
"BatchSizeTotal": 0,
"URL": null,
"ExecuteAndResultSetHoldTime": 0,
"LastErrorMessage": null,
"FetchRowCountHistogram": [
0,
0,
0,
0,
0,
0
],
"FetchRowCountMax": 0,
"Histogram": [
0,
0,
13,
2,
0,
0,
0,
0
],
"parsedOrderbycolumns": "[]",
"ErrorCount": 0,
"SQL": "DROP TABLE t_big",
"ClobOpenCount": 0,
"LastTime": "2012-09-14 21:35:10",
"File": null,
"LastErrorStackTrace": null,
"LastError": null,
"EffectedRowCountHistogram": [
15,
0,
0,
0,
0,
0
],
"TotalTime": 742,
"formattedSql": "DROP TABLE t_big",
"Name": null,
"MaxTimespanOccurTime": "2012/09/14 09:35:00:758",
"parsedTable": "{t_big=Drop}",
"BatchSizeMax": 0,
"ReadBytesLength": 0,
"EffectedRowCountMax": 0,
"FetchRowCount": 0,
"parsedConditions": "[]",
"ResultSetHoldTime": 0,
"ReaderOpenCount": 0,
"ExecuteAndResultHoldTimeHistogram": [
0,
0,
13,
2,
0,
0,
0,
0
],
"LastErrorTime": null
}
}
* </pre>
* @author yunnysunny<yunnysunny@gmail.com>
* */
public class DruidSqlDetailFrame extends JFrame {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** service的地址的根路径. */
private static final String BASE_URL = "/sql";
/** json中格式化sql的键名. */
private static final String KEY_FORMAT_SQL = "formattedSql";
/** json中sql的键名. */
private static final String KEY_SQL = "SQL";
/** sql语句的索引. */
private String id;
/** MBeanServerConnection对象. */
private MBeanServerConnection conn;
/** 解析信息内容表格的标题列. */
private static final ArrayList<String> PARESE_TITLE_LIST = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{
add("parsedTable");
add("parsedFields");
add("parsedConditions");
add("parsedRelationships");
add("parsedOrderbycolumns");
}
};
/** 慢查询信息表格的标题列. */
private static final ArrayList<String> LAST_SLOW_TITLE_LIST = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{
add("MaxTimespan");
add("MaxTimespanOccurTime");
add("LastSlowParameters");
}
};
/** 错误信息表格的标题列. */
private static final ArrayList<String> LAST_ERROR_TITLE_LIST = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{
add("LastErrorMessage");
add("LastErrorClass");
add("LastErrorTime");
add("LastErrorStackTrace");
}
};
/** 其他信息表格的标题列. */
private static final ArrayList<String> OTHER_ERROR_TITLE_LIST = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{
add("BatchSizeMax");
add("BatchSizeTotal");
add("BlobOpenCount");
add("ClobOpenCount");
add("ReaderOpenCount");
add("InputStreamOpenCount");
add("ReadStringLength");
add("ReadBytesLength");
}
};
/** 解析信息数据. */
private ArrayList<LinkedHashMap<String, Object>> parseData;
/** 慢查询信息数据. */
private ArrayList<LinkedHashMap<String, Object>> lastSlowData;
/** 错误信息数据. */
private ArrayList<LinkedHashMap<String, Object>> lastErrorData;
/** 其他信息数据. */
private ArrayList<LinkedHashMap<String, Object>> otherData;
/** 各个数据表格中最大的数据列长度. */
private int maxListLen;
/** 格式化好的sql语句内容. */
private String formatSql;
/** 未格式化的sql语句内容. */
private String sql;
/** 窗体的宽度. */
private static final int WIDTH = 800;
/** 窗体的高度. */
private static final int HEIGHT = 600;
/** The Constant LOG. */
private final static Log LOG = LogFactory.getLog(DruidSqlDetailFrame.class);
/**
* Instantiates a new druid sql detail frame.
*
* @param id sql语句索引
* @param conn MBeanServerConnection对象
*/
public DruidSqlDetailFrame(String id, MBeanServerConnection conn){
this.id = id;
this.conn = conn;
getMaxListLen();
init();
start();
}
/**
* 获取各个数据表格中最大的数据列长度
*
* @return the max list len
*/
private void getMaxListLen() {
maxListLen = PARESE_TITLE_LIST.size();
int slowLen = LAST_SLOW_TITLE_LIST.size();
int errLen = LAST_ERROR_TITLE_LIST.size();
int otherLen = OTHER_ERROR_TITLE_LIST.size();
if (maxListLen < slowLen) {
maxListLen = slowLen;
}
if (maxListLen < errLen) {
maxListLen = errLen;
}
if (maxListLen < otherLen) {
maxListLen = otherLen;
}
}
/**
* 初始化数据,得到四个表格的数据
*/
private void init() {
String url = BASE_URL + "-" + id + ".json";
try {
ArrayList<LinkedHashMap<String, Object>> data = TableDataProcessor.parseData(TableDataProcessor.getData(url,
conn));
if (data != null) {
LinkedHashMap<String, Object> contentEle = data.get(0);
formatSql = (String) contentEle.remove(KEY_FORMAT_SQL);
sql = (String) contentEle.remove(KEY_SQL);
int parseLen = PARESE_TITLE_LIST.size();
int slowLen = LAST_SLOW_TITLE_LIST.size();
int errLen = LAST_ERROR_TITLE_LIST.size();
int otherLen = OTHER_ERROR_TITLE_LIST.size();
LinkedHashMap<String, Object> parseDataEle = new LinkedHashMap<String, Object>();
LinkedHashMap<String, Object> slowDataEle = new LinkedHashMap<String, Object>();
LinkedHashMap<String, Object> errDataEle = new LinkedHashMap<String, Object>();
LinkedHashMap<String, Object> otherDataEle = new LinkedHashMap<String, Object>();
parseData = new ArrayList<LinkedHashMap<String, Object>>(1);
lastSlowData = new ArrayList<LinkedHashMap<String, Object>>(1);
lastErrorData = new ArrayList<LinkedHashMap<String, Object>>(1);
otherData = new ArrayList<LinkedHashMap<String, Object>>(1);
for (Iterator<Entry<String, Object>> it = contentEle.entrySet().iterator(); it.hasNext();) {
Entry<String, Object> entry = it.next();
String key = entry.getKey();
Object value = entry.getValue();
for (int i = 0; i < maxListLen; i++) {
if (i < parseLen && key.equals(PARESE_TITLE_LIST.get(i))) {
parseDataEle.put(key, value);
} else if (i < slowLen && key.equals(LAST_SLOW_TITLE_LIST.get(i))) {
slowDataEle.put(key, value);
} else if (i < errLen && key.equals(LAST_ERROR_TITLE_LIST.get(i))) {
errDataEle.put(key, value);
} else if (i < otherLen && key.equals(OTHER_ERROR_TITLE_LIST.get(i))) {
otherDataEle.put(key, value);
}
}
}
parseData.add(parseDataEle);
lastSlowData.add(slowDataEle);
lastErrorData.add(errDataEle);
otherData.add(otherDataEle);
} else {
LOG.warn("错误的json格式");
}
} catch (Exception e) {
LOG.warn("获取数据时异常", e);
}
}
/**
* 将表格添加到contentPanel对象内部。
*
* @param contentPanel JPanel对象
* @param 当前表格的标题
* @param data 当前表格的数据
*/
private void addTable(JPanel contentPanel, String title, ArrayList<LinkedHashMap<String, Object>> data) {
final JPanel content1 = new JPanel();
content1.setLayout(new BorderLayout());
content1.setBorder((TitledBorder) BorderFactory.createTitledBorder(title));
contentPanel.add(content1);
ColumnData colData = TableDataProcessor.row2col(data);
JTable table = new JTable();
DruidTableModel tableModel = new DruidTableModel(colData.getDatas());
table.setModel(tableModel);
TableColumn col = table.getColumnModel().getColumn(0);
col.setCellRenderer(new DruidTableCellRenderer());
final JTableHeader header1 = table.getTableHeader();
content1.add(header1, BorderLayout.NORTH);
content1.add(table);
}
/**
* 将各个界面添加到JFrame中
*
* @param pane JFrame内部的Container对象
*/
private void addComponentsToPane(Container pane) {
JScrollPane scrollPane = new JScrollPane();
JPanel contentPanel = new JPanel();
contentPanel.setLayout(new GridLayout(0, 1));
final JTextArea sqlField = new JTextArea(formatSql, 8, 20);
final JScrollPane content1 = new JScrollPane(sqlField);
content1.setBorder((TitledBorder) BorderFactory.createTitledBorder("SQL语句"));
contentPanel.add(content1);
addTable(contentPanel, "解析信息", parseData);
addTable(contentPanel, "上次慢查询信息", lastSlowData);
addTable(contentPanel, "上次错误查询信息", lastErrorData);
addTable(contentPanel, "其他信息", otherData);
scrollPane.setViewportView(contentPanel);
pane.add(scrollPane, BorderLayout.CENTER);
}
/**
* 显示JFrame
*/
private void start() {
addComponentsToPane(getContentPane());
setTitle("SQL:[" + sql + "]详情");
pack();
setSize(WIDTH, HEIGHT);
double width = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
double height = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
setLocation((int) (width - getWidth()) / 2, (int) (height - getHeight()) / 2);
setVisible(true);
}
}