HBASE-11062 hbtop (#476)
Signed-off-by: Stack <stack@apache.org> Signed-off-by: Sean Busbey <busbey@apache.org> Signed-off-by: Josh Elser <elserj@apache.org> Signed-off-by: Andrew Purtell <apurtell@apache.org>
21
bin/hbase
|
@ -118,6 +118,7 @@ if [ $# = 0 ]; then
|
||||||
echo " rowcounter Run RowCounter tool"
|
echo " rowcounter Run RowCounter tool"
|
||||||
echo " cellcounter Run CellCounter tool"
|
echo " cellcounter Run CellCounter tool"
|
||||||
echo " pre-upgrade Run Pre-Upgrade validator tool"
|
echo " pre-upgrade Run Pre-Upgrade validator tool"
|
||||||
|
echo " hbtop Run HBTop tool"
|
||||||
echo " CLASSNAME Run the class named CLASSNAME"
|
echo " CLASSNAME Run the class named CLASSNAME"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -221,7 +222,7 @@ if [ "${INTERNAL_CLASSPATH}" != "true" ]; then
|
||||||
done
|
done
|
||||||
|
|
||||||
# If command can use our shaded client, use it
|
# If command can use our shaded client, use it
|
||||||
declare -a commands_in_client_jar=("classpath" "version")
|
declare -a commands_in_client_jar=("classpath" "version" "hbtop")
|
||||||
for c in "${commands_in_client_jar[@]}"; do
|
for c in "${commands_in_client_jar[@]}"; do
|
||||||
if [ "${COMMAND}" = "${c}" ]; then
|
if [ "${COMMAND}" = "${c}" ]; then
|
||||||
if [ -n "${HADOOP_IN_PATH}" ] && [ -f "${HADOOP_IN_PATH}" ]; then
|
if [ -n "${HADOOP_IN_PATH}" ] && [ -f "${HADOOP_IN_PATH}" ]; then
|
||||||
|
@ -628,6 +629,24 @@ elif [ "$COMMAND" = "pre-upgrade" ] ; then
|
||||||
CLASS='org.apache.hadoop.hbase.tool.PreUpgradeValidator'
|
CLASS='org.apache.hadoop.hbase.tool.PreUpgradeValidator'
|
||||||
elif [ "$COMMAND" = "completebulkload" ] ; then
|
elif [ "$COMMAND" = "completebulkload" ] ; then
|
||||||
CLASS='org.apache.hadoop.hbase.tool.BulkLoadHFilesTool'
|
CLASS='org.apache.hadoop.hbase.tool.BulkLoadHFilesTool'
|
||||||
|
elif [ "$COMMAND" = "hbtop" ] ; then
|
||||||
|
CLASS='org.apache.hadoop.hbase.hbtop.HBTop'
|
||||||
|
if [ -n "${shaded_jar}" ] ; then
|
||||||
|
for f in "${HBASE_HOME}"/lib/hbase-hbtop*.jar; do
|
||||||
|
if [ -f "${f}" ]; then
|
||||||
|
CLASSPATH="${CLASSPATH}:${f}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for f in "${HBASE_HOME}"/lib/commons-lang3*.jar; do
|
||||||
|
if [ -f "${f}" ]; then
|
||||||
|
CLASSPATH="${CLASSPATH}:${f}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
HBASE_OPTS="${HBASE_OPTS} -Dlog4j.configuration=file:${HBASE_HOME}/conf/log4j-hbtop.properties"
|
||||||
else
|
else
|
||||||
CLASS=$COMMAND
|
CLASS=$COMMAND
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you 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.
|
||||||
|
|
||||||
|
log4j.rootLogger=WARN,console
|
||||||
|
log4j.threshold=WARN
|
||||||
|
|
||||||
|
# console
|
||||||
|
log4j.appender.console=org.apache.log4j.ConsoleAppender
|
||||||
|
log4j.appender.console.target=System.err
|
||||||
|
log4j.appender.console.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.console.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %m%n
|
||||||
|
|
||||||
|
# ZooKeeper will still put stuff at WARN
|
||||||
|
log4j.logger.org.apache.zookeeper=ERROR
|
|
@ -305,6 +305,10 @@
|
||||||
<groupId>org.apache.hbase</groupId>
|
<groupId>org.apache.hbase</groupId>
|
||||||
<artifactId>hbase-zookeeper</artifactId>
|
<artifactId>hbase-zookeeper</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hbase</groupId>
|
||||||
|
<artifactId>hbase-hbtop</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jline</groupId>
|
<groupId>jline</groupId>
|
||||||
<artifactId>jline</artifactId>
|
<artifactId>jline</artifactId>
|
||||||
|
|
|
@ -0,0 +1,248 @@
|
||||||
|
<!---
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
# hbtop
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`hbtop` is a real-time monitoring tool for HBase like Unix's top command.
|
||||||
|
It can display summary information as well as metrics per Region/Namespace/Table/RegionServer.
|
||||||
|
In this tool, you can see the metrics sorted by a selected field and filter the metrics to see only metrics you really want to see.
|
||||||
|
Also, with the drill-down feature, you can find hot regions easily in a top-down manner.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
You can run hbtop with the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ hbase hbtop
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, the values of `hbase.client.zookeeper.quorum` and `zookeeper.znode.parent` in `hbase-site.xml` in the classpath or the default values of them are used to connect.
|
||||||
|
|
||||||
|
Or, you can specify your own zookeeper quorum and znode parent as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ hbase hbtop -Dhbase.client.zookeeper.quorum=<zookeeper quorum> -Dzookeeper.znode.parent=<znode parent>
|
||||||
|
```
|
||||||
|
|
||||||
|
![Top screen](img/top_screen.gif "Top screen")
|
||||||
|
|
||||||
|
The top screen consists of a summary part and of a metrics part.
|
||||||
|
In the summary part, you can see `HBase Version`, `Cluster ID`, `The number of region servers`, `Region count`, `Average Cluster Load` and `Aggregated Request/s`.
|
||||||
|
In the metrics part, you can see metrics per Region/Namespace/Table/RegionServer depending on the selected mode.
|
||||||
|
The top screen is refreshed in a certain period – 3 seconds by default.
|
||||||
|
|
||||||
|
### Scrolling metric records
|
||||||
|
|
||||||
|
You can scroll the metric records in the metrics part.
|
||||||
|
|
||||||
|
![Scrolling metric records](img/scrolling_metric_records.gif "Scrolling metric records")
|
||||||
|
|
||||||
|
### Command line arguments
|
||||||
|
|
||||||
|
| Argument | Description |
|
||||||
|
|---|---|
|
||||||
|
| -d,--delay <arg> | The refresh delay (in seconds); default is 3 seconds |
|
||||||
|
| -h,--help | Print usage; for help while the tool is running press `h` key |
|
||||||
|
| -m,--mode <arg> | The mode; `n` (Namespace)|`t` (Table)|r (Region)|`s` (RegionServer), default is `r` (Region) |
|
||||||
|
|
||||||
|
### Modes
|
||||||
|
|
||||||
|
There are the following 4 modes in hbtop:
|
||||||
|
|
||||||
|
| Mode | Description |
|
||||||
|
|---|---|
|
||||||
|
| Region | Showing metric records per region |
|
||||||
|
| Namespace | Showing metric records per namespace |
|
||||||
|
| Table | Showing metric records per table |
|
||||||
|
| RegionServer | Showing metric records per region server |
|
||||||
|
|
||||||
|
#### Region mode
|
||||||
|
|
||||||
|
In Region mode, the default sort field is `#REQ/S`.
|
||||||
|
|
||||||
|
The fields in this mode are as follows:
|
||||||
|
|
||||||
|
| Field | Description | Displayed by default |
|
||||||
|
|---|---|---|
|
||||||
|
| RNAME | Region Name | false |
|
||||||
|
| NAMESPACE | Namespace Name | true |
|
||||||
|
| TABLE | Table Name | true |
|
||||||
|
| SCODE | Start Code | false |
|
||||||
|
| REPID | Replica ID | false |
|
||||||
|
| REGION | Encoded Region Name | true |
|
||||||
|
| RS | Short Region Server Name | true |
|
||||||
|
| LRS | Long Region Server Name | false |
|
||||||
|
| #REQ/S | Request Count per second | true |
|
||||||
|
| #READ/S | Read Request Count per second | true |
|
||||||
|
| #FREAD/S | Filtered Read Request Count per second | true |
|
||||||
|
| #WRITE/S | Write Request Count per second | true |
|
||||||
|
| SF | StoreFile Size | true |
|
||||||
|
| USF | Uncompressed StoreFile Size | false |
|
||||||
|
| #SF | Number of StoreFiles | true |
|
||||||
|
| MEMSTORE | MemStore Size | true |
|
||||||
|
| LOCALITY | Block Locality | true |
|
||||||
|
| SKEY | Start Key | false |
|
||||||
|
| #COMPingCELL | Compacting Cell Count | false |
|
||||||
|
| #COMPedCELL | Compacted Cell Count | false |
|
||||||
|
| %COMP | Compaction Progress | false |
|
||||||
|
| LASTMCOMP | Last Major Compaction Time | false |
|
||||||
|
|
||||||
|
#### Namespace mode
|
||||||
|
|
||||||
|
In Namespace mode, the default sort field is `#REQ/S`.
|
||||||
|
|
||||||
|
The fields in this mode are as follows:
|
||||||
|
|
||||||
|
| Field | Description | Displayed by default |
|
||||||
|
|---|---|---|
|
||||||
|
| NAMESPACE | Namespace Name | true |
|
||||||
|
| #REGION | Region Count | true |
|
||||||
|
| #REQ/S | Request Count per second | true |
|
||||||
|
| #READ/S | Read Request Count per second | true |
|
||||||
|
| #FREAD/S | Filtered Read Request Count per second | true |
|
||||||
|
| #WRITE/S | Write Request Count per second | true |
|
||||||
|
| SF | StoreFile Size | true |
|
||||||
|
| USF | Uncompressed StoreFile Size | false |
|
||||||
|
| #SF | Number of StoreFiles | true |
|
||||||
|
| MEMSTORE | MemStore Size | true |
|
||||||
|
|
||||||
|
#### Table mode
|
||||||
|
|
||||||
|
In Table mode, the default sort field is `#REQ/S`.
|
||||||
|
|
||||||
|
The fields in this mode are as follows:
|
||||||
|
|
||||||
|
| Field | Description | Displayed by default |
|
||||||
|
|---|---|---|
|
||||||
|
| NAMESPACE | Namespace Name | true |
|
||||||
|
| TABLE | Table Name | true |
|
||||||
|
| #REGION | Region Count | true |
|
||||||
|
| #REQ/S | Request Count per second | true |
|
||||||
|
| #READ/S | Read Request Count per second | true |
|
||||||
|
| #FREAD/S | Filtered Read Request Count per second | true |
|
||||||
|
| #WRITE/S | Write Request Count per second | true |
|
||||||
|
| SF | StoreFile Size | true |
|
||||||
|
| USF | Uncompressed StoreFile Size | false |
|
||||||
|
| #SF | Number of StoreFiles | true |
|
||||||
|
| MEMSTORE | MemStore Size | true |
|
||||||
|
|
||||||
|
#### RegionServer mode
|
||||||
|
|
||||||
|
In RegionServer mode, the default sort field is `#REQ/S`.
|
||||||
|
|
||||||
|
The fields in this mode are as follows:
|
||||||
|
|
||||||
|
| Field | Description | Displayed by default |
|
||||||
|
|---|---|---|
|
||||||
|
| RS | Short Region Server Name | true |
|
||||||
|
| LRS | Long Region Server Name | false |
|
||||||
|
| #REGION | Region Count | true |
|
||||||
|
| #REQ/S | Request Count per second | true |
|
||||||
|
| #READ/S | Read Request Count per second | true |
|
||||||
|
| #FREAD/S | Filtered Read Request Count per second | true |
|
||||||
|
| #WRITE/S | Write Request Count per second | true |
|
||||||
|
| SF | StoreFile Size | true |
|
||||||
|
| USF | Uncompressed StoreFile Size | false |
|
||||||
|
| #SF | Number of StoreFiles | true |
|
||||||
|
| MEMSTORE | MemStore Size | true |
|
||||||
|
| UHEAP | Used Heap Size | true |
|
||||||
|
| MHEAP | Max Heap Size | true |
|
||||||
|
|
||||||
|
### Changing mode
|
||||||
|
|
||||||
|
You can change mode by pressing `m` key in the top screen.
|
||||||
|
|
||||||
|
![Changing mode](img/changing_mode.gif "Changing mode")
|
||||||
|
|
||||||
|
### Changing the refresh delay
|
||||||
|
|
||||||
|
You can change the refresh by pressing `d` key in the top screen.
|
||||||
|
|
||||||
|
![Changing the refresh delay](img/changing_refresh_delay.gif "Changing the refresh delay")
|
||||||
|
|
||||||
|
### Changing the displayed fields
|
||||||
|
|
||||||
|
You can move to the field screen by pressing `f` key in the top screen. In the fields screen, you can change the displayed fields by choosing a field and pressing `d` key or `space` key.
|
||||||
|
|
||||||
|
![Changing the displayed fields](img/changing_displayed_fields.gif "Changing the displayed fields")
|
||||||
|
|
||||||
|
### Changing the sort field
|
||||||
|
|
||||||
|
You can move to the fields screen by pressing `f` key in the top screen. In the field screen, you can change the sort field by choosing a field and pressing `s`. Also, you can change the sort order (ascending or descending) by pressing `R` key.
|
||||||
|
|
||||||
|
![Changing the sort field](img/changing_sort_field.gif "Changing the sort field")
|
||||||
|
|
||||||
|
|
||||||
|
### Changing the order of the fields
|
||||||
|
|
||||||
|
You can move to the fields screen by pressing `f` key in the top screen. In the field screen, you can change the order of the fields.
|
||||||
|
|
||||||
|
![Changing the order of the fields](img/changing_order_of_fields.gif "Changing the sort field")
|
||||||
|
|
||||||
|
### Filters
|
||||||
|
|
||||||
|
You can filter the metric records with the filter feature. We can add filters by pressing `o` key for ignoring case or `O` key for case sensitive.
|
||||||
|
|
||||||
|
![Adding filters](img/adding_filters.gif "Adding filters")
|
||||||
|
|
||||||
|
The syntax is as follows:
|
||||||
|
```
|
||||||
|
<Field><Operator><Value>
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, we can add filters like the following:
|
||||||
|
```
|
||||||
|
NAMESPACE==default
|
||||||
|
REQ/S>1000
|
||||||
|
```
|
||||||
|
|
||||||
|
The operators we can specify are as follows:
|
||||||
|
|
||||||
|
| Operator | Description |
|
||||||
|
|---|---|
|
||||||
|
| = | Partial match |
|
||||||
|
| == | Exact match |
|
||||||
|
| > | Greater than |
|
||||||
|
| >= | Greater than or equal to |
|
||||||
|
| < | Less than |
|
||||||
|
| <= | Less than and equal to |
|
||||||
|
|
||||||
|
You can see the current filters by pressing `^o` key and clear them by pressing `=` key.
|
||||||
|
|
||||||
|
![Showing and clearing filters](img/showing_and_clearing_filters.gif "Showing and clearing filters")
|
||||||
|
|
||||||
|
### Drilling down
|
||||||
|
|
||||||
|
You can drill down the metric record by choosing a metric record that you want to drill down and pressing `i` key in the top screen. With this feature, you can find hot regions easily in a top-down manner.
|
||||||
|
|
||||||
|
![Drilling down](img/driling_down.gif "Drilling down")
|
||||||
|
|
||||||
|
### Help screen
|
||||||
|
|
||||||
|
You can see the help screen by pressing `h` key in the top screen.
|
||||||
|
|
||||||
|
![Help screen](img/help_screen.gif "Help screen")
|
||||||
|
|
||||||
|
## Others
|
||||||
|
|
||||||
|
### How hbtop gets the metrics data
|
||||||
|
|
||||||
|
hbtop gets the metrics from ClusterMetrics which is returned as the result of a call to Admin#getClusterMetrics() on the current HMaster. To add metrics to hbtop, they will need to be exposed via ClusterMetrics.
|
After Width: | Height: | Size: 2.6 MiB |
After Width: | Height: | Size: 2.5 MiB |
After Width: | Height: | Size: 3.2 MiB |
After Width: | Height: | Size: 2.7 MiB |
After Width: | Height: | Size: 2.2 MiB |
After Width: | Height: | Size: 3.3 MiB |
After Width: | Height: | Size: 3.8 MiB |
After Width: | Height: | Size: 909 KiB |
After Width: | Height: | Size: 6.3 MiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 2.0 MiB |
|
@ -0,0 +1,77 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<!--
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<artifactId>hbase-build-configuration</artifactId>
|
||||||
|
<groupId>org.apache.hbase</groupId>
|
||||||
|
<version>3.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../hbase-build-configuration</relativePath>
|
||||||
|
</parent>
|
||||||
|
<artifactId>hbase-hbtop</artifactId>
|
||||||
|
<name>Apache HBase - HBTop</name>
|
||||||
|
<description>A real-time monitoring tool for HBase like Unix's top command</description>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<!-- Make a jar and put the sources in the jar -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hbase</groupId>
|
||||||
|
<artifactId>hbase-shaded-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.stephenc.findbugs</groupId>
|
||||||
|
<artifactId>findbugs-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.hbase</groupId>
|
||||||
|
<artifactId>hbase-common</artifactId>
|
||||||
|
<type>test-jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,141 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.conf.Configured;
|
||||||
|
import org.apache.hadoop.hbase.HBaseConfiguration;
|
||||||
|
import org.apache.hadoop.hbase.HBaseInterfaceAudience;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.util.Tool;
|
||||||
|
import org.apache.hadoop.util.ToolRunner;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
|
||||||
|
import org.apache.hbase.thirdparty.org.apache.commons.cli.DefaultParser;
|
||||||
|
import org.apache.hbase.thirdparty.org.apache.commons.cli.HelpFormatter;
|
||||||
|
import org.apache.hbase.thirdparty.org.apache.commons.cli.Options;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A real-time monitoring tool for HBase like Unix top command.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
|
||||||
|
public class HBTop extends Configured implements Tool {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(HBTop.class);
|
||||||
|
|
||||||
|
public HBTop() {
|
||||||
|
this(HBaseConfiguration.create());
|
||||||
|
}
|
||||||
|
|
||||||
|
public HBTop(Configuration conf) {
|
||||||
|
super(Objects.requireNonNull(conf));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int run(String[] args) throws Exception {
|
||||||
|
long initialRefreshDelay = 3 * 1000;
|
||||||
|
Mode initialMode = Mode.REGION;
|
||||||
|
try {
|
||||||
|
// Command line options
|
||||||
|
Options opts = new Options();
|
||||||
|
opts.addOption("h", "help", false,
|
||||||
|
"Print usage; for help while the tool is running press 'h'");
|
||||||
|
opts.addOption("d", "delay", true,
|
||||||
|
"The refresh delay (in seconds); default is 3 seconds");
|
||||||
|
opts.addOption("m", "mode", true,
|
||||||
|
"The mode; n (Namespace)|t (Table)|r (Region)|s (RegionServer)"
|
||||||
|
+ ", default is r (Region)");
|
||||||
|
|
||||||
|
CommandLine commandLine = new DefaultParser().parse(opts, args);
|
||||||
|
|
||||||
|
if (commandLine.hasOption("help")) {
|
||||||
|
printUsage(opts);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandLine.hasOption("delay")) {
|
||||||
|
int delay = 0;
|
||||||
|
try {
|
||||||
|
delay = Integer.parseInt(commandLine.getOptionValue("delay"));
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delay < 1) {
|
||||||
|
LOGGER.warn("Delay set too low or invalid, using default");
|
||||||
|
} else {
|
||||||
|
initialRefreshDelay = delay * 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandLine.hasOption("mode")) {
|
||||||
|
String mode = commandLine.getOptionValue("mode");
|
||||||
|
switch (mode) {
|
||||||
|
case "n":
|
||||||
|
initialMode = Mode.NAMESPACE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "t":
|
||||||
|
initialMode = Mode.TABLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "r":
|
||||||
|
initialMode = Mode.REGION;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "s":
|
||||||
|
initialMode = Mode.REGION_SERVER;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOGGER.warn("Mode set invalid, using default");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Unable to parse options", e);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Screen screen = new Screen(getConf(), initialRefreshDelay, initialMode)) {
|
||||||
|
screen.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printUsage(Options opts) {
|
||||||
|
new HelpFormatter().printHelp("hbase hbtop [opts] [-D<property=value>]*", opts);
|
||||||
|
System.out.println("");
|
||||||
|
System.out.println(" Note: -D properties will be applied to the conf used.");
|
||||||
|
System.out.println(" For example:");
|
||||||
|
System.out.println(" -Dhbase.client.zookeeper.quorum=<zookeeper quorum>");
|
||||||
|
System.out.println(" -Dzookeeper.znode.parent=<znode parent>");
|
||||||
|
System.out.println("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
int res = ToolRunner.run(new HBTop(), args);
|
||||||
|
System.exit(res);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldValue;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldValueType;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a record of the metrics in the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class Record implements Map<Field, FieldValue> {
|
||||||
|
|
||||||
|
private final ImmutableMap<Field, FieldValue> values;
|
||||||
|
|
||||||
|
public final static class Entry extends AbstractMap.SimpleImmutableEntry<Field, FieldValue> {
|
||||||
|
private Entry(Field key, FieldValue value) {
|
||||||
|
super(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static class Builder {
|
||||||
|
|
||||||
|
private final ImmutableMap.Builder<Field, FieldValue> builder;
|
||||||
|
|
||||||
|
private Builder() {
|
||||||
|
builder = ImmutableMap.builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder put(Field key, Object value) {
|
||||||
|
builder.put(key, key.newValue(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder put(Field key, FieldValue value) {
|
||||||
|
builder.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder put(Entry entry) {
|
||||||
|
builder.put(entry);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder putAll(Map<Field, FieldValue> map) {
|
||||||
|
builder.putAll(map);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Record build() {
|
||||||
|
return new Record(builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Entry entry(Field field, Object value) {
|
||||||
|
return new Entry(field, field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Entry entry(Field field, FieldValue value) {
|
||||||
|
return new Entry(field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Record ofEntries(Entry... entries) {
|
||||||
|
return ofEntries(Stream.of(entries));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Record ofEntries(Stream<Entry> entries) {
|
||||||
|
return entries.collect(Record::builder, Builder::put, (r1, r2) -> {}).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Record(ImmutableMap<Field, FieldValue> values) {
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return values.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return values.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return values.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return values.containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldValue get(Object key) {
|
||||||
|
return values.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldValue put(Field key, FieldValue value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldValue remove(Object key) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(@NonNull Map<? extends Field, ? extends FieldValue> m) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Set<Field> keySet() {
|
||||||
|
return values.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Collection<FieldValue> values() {
|
||||||
|
return values.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Set<Map.Entry<Field, FieldValue>> entrySet() {
|
||||||
|
return values.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Record combine(Record o) {
|
||||||
|
return ofEntries(values.keySet().stream()
|
||||||
|
.map(k -> {
|
||||||
|
if (k.getFieldValueType() == FieldValueType.STRING) {
|
||||||
|
return entry(k, values.get(k));
|
||||||
|
}
|
||||||
|
return entry(k, values.get(k).plus(o.values.get(k)));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,336 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldValue;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a filter that's filtering the metric {@link Record}s.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class RecordFilter {
|
||||||
|
|
||||||
|
private enum Operator {
|
||||||
|
EQUAL("="),
|
||||||
|
DOUBLE_EQUALS("=="),
|
||||||
|
GREATER(">"),
|
||||||
|
GREATER_OR_EQUAL(">="),
|
||||||
|
LESS("<"),
|
||||||
|
LESS_OR_EQUAL("<=");
|
||||||
|
|
||||||
|
private final String operator;
|
||||||
|
|
||||||
|
Operator(String operator) {
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return operator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RecordFilter parse(String filterString, boolean ignoreCase) {
|
||||||
|
return parse(filterString, Arrays.asList(Field.values()), ignoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RecordFilter parse(String filterString, List<Field> fields, boolean ignoreCase) {
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
boolean not = isNot(filterString);
|
||||||
|
if (not) {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder fieldString = new StringBuilder();
|
||||||
|
while (filterString.length() > index && filterString.charAt(index) != '<'
|
||||||
|
&& filterString.charAt(index) != '>' && filterString.charAt(index) != '=') {
|
||||||
|
fieldString.append(filterString.charAt(index++));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldString.length() == 0 || filterString.length() == index) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Field field = getField(fields, fieldString.toString());
|
||||||
|
if (field == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder operatorString = new StringBuilder();
|
||||||
|
while (filterString.length() > index && (filterString.charAt(index) == '<' ||
|
||||||
|
filterString.charAt(index) == '>' || filterString.charAt(index) == '=')) {
|
||||||
|
operatorString.append(filterString.charAt(index++));
|
||||||
|
}
|
||||||
|
|
||||||
|
Operator operator = getOperator(operatorString.toString());
|
||||||
|
if (operator == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String value = filterString.substring(index);
|
||||||
|
FieldValue fieldValue = getFieldValue(field, value);
|
||||||
|
if (fieldValue == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RecordFilter(ignoreCase, not, field, operator, fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FieldValue getFieldValue(Field field, String value) {
|
||||||
|
try {
|
||||||
|
return field.newValue(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNot(String filterString) {
|
||||||
|
return filterString.startsWith("!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field getField(List<Field> fields, String fieldString) {
|
||||||
|
for (Field f : fields) {
|
||||||
|
if (f.getHeader().equals(fieldString)) {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operator getOperator(String operatorString) {
|
||||||
|
for (Operator o : Operator.values()) {
|
||||||
|
if (operatorString.equals(o.toString())) {
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final boolean ignoreCase;
|
||||||
|
private final boolean not;
|
||||||
|
private final Field field;
|
||||||
|
private final Operator operator;
|
||||||
|
private final FieldValue value;
|
||||||
|
|
||||||
|
private RecordFilter(boolean ignoreCase, boolean not, Field field, Operator operator,
|
||||||
|
FieldValue value) {
|
||||||
|
this.ignoreCase = ignoreCase;
|
||||||
|
this.not = not;
|
||||||
|
this.field = Objects.requireNonNull(field);
|
||||||
|
this.operator = Objects.requireNonNull(operator);
|
||||||
|
this.value = Objects.requireNonNull(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean execute(Record record) {
|
||||||
|
FieldValue fieldValue = record.get(field);
|
||||||
|
if (fieldValue == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operator == Operator.EQUAL) {
|
||||||
|
boolean ret;
|
||||||
|
if (ignoreCase) {
|
||||||
|
ret = fieldValue.asString().toLowerCase().contains(value.asString().toLowerCase());
|
||||||
|
} else {
|
||||||
|
ret = fieldValue.asString().contains(value.asString());
|
||||||
|
}
|
||||||
|
return not != ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare = ignoreCase ?
|
||||||
|
fieldValue.compareToIgnoreCase(value) : fieldValue.compareTo(value);
|
||||||
|
|
||||||
|
boolean ret;
|
||||||
|
switch (operator) {
|
||||||
|
case DOUBLE_EQUALS:
|
||||||
|
ret = compare == 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GREATER:
|
||||||
|
ret = compare > 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GREATER_OR_EQUAL:
|
||||||
|
ret = compare >= 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LESS:
|
||||||
|
ret = compare < 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LESS_OR_EQUAL:
|
||||||
|
ret = compare <= 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
return not != ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (not ? "!" : "") + field.getHeader() + operator + value.asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof RecordFilter)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
RecordFilter filter = (RecordFilter) o;
|
||||||
|
return ignoreCase == filter.ignoreCase && not == filter.not && field == filter.field
|
||||||
|
&& operator == filter.operator && value.equals(filter.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(ignoreCase, not, field, operator, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For FilterBuilder
|
||||||
|
*/
|
||||||
|
public static FilterBuilder newBuilder(Field field) {
|
||||||
|
return new FilterBuilder(field, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FilterBuilder newBuilder(Field field, boolean ignoreCase) {
|
||||||
|
return new FilterBuilder(field, ignoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class FilterBuilder {
|
||||||
|
private final Field field;
|
||||||
|
private final boolean ignoreCase;
|
||||||
|
|
||||||
|
private FilterBuilder(Field field, boolean ignoreCase) {
|
||||||
|
this.field = Objects.requireNonNull(field);
|
||||||
|
this.ignoreCase = ignoreCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter equal(FieldValue value) {
|
||||||
|
return newFilter(false, Operator.EQUAL, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter equal(Object value) {
|
||||||
|
return equal(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notEqual(FieldValue value) {
|
||||||
|
return newFilter(true, Operator.EQUAL, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notEqual(Object value) {
|
||||||
|
return notEqual(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter doubleEquals(FieldValue value) {
|
||||||
|
return newFilter(false, Operator.DOUBLE_EQUALS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter doubleEquals(Object value) {
|
||||||
|
return doubleEquals(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notDoubleEquals(FieldValue value) {
|
||||||
|
return newFilter(true, Operator.DOUBLE_EQUALS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notDoubleEquals(Object value) {
|
||||||
|
return notDoubleEquals(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter greater(FieldValue value) {
|
||||||
|
return newFilter(false, Operator.GREATER, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter greater(Object value) {
|
||||||
|
return greater(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notGreater(FieldValue value) {
|
||||||
|
return newFilter(true, Operator.GREATER, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notGreater(Object value) {
|
||||||
|
return notGreater(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter greaterOrEqual(FieldValue value) {
|
||||||
|
return newFilter(false, Operator.GREATER_OR_EQUAL, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter greaterOrEqual(Object value) {
|
||||||
|
return greaterOrEqual(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notGreaterOrEqual(FieldValue value) {
|
||||||
|
return newFilter(true, Operator.GREATER_OR_EQUAL, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notGreaterOrEqual(Object value) {
|
||||||
|
return notGreaterOrEqual(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter less(FieldValue value) {
|
||||||
|
return newFilter(false, Operator.LESS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter less(Object value) {
|
||||||
|
return less(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notLess(FieldValue value) {
|
||||||
|
return newFilter(true, Operator.LESS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notLess(Object value) {
|
||||||
|
return notLess(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter lessOrEqual(FieldValue value) {
|
||||||
|
return newFilter(false, Operator.LESS_OR_EQUAL, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter lessOrEqual(Object value) {
|
||||||
|
return lessOrEqual(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notLessOrEqual(FieldValue value) {
|
||||||
|
return newFilter(true, Operator.LESS_OR_EQUAL, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecordFilter notLessOrEqual(Object value) {
|
||||||
|
return notLessOrEqual(field.newValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private RecordFilter newFilter(boolean not, Operator operator, FieldValue value) {
|
||||||
|
return new RecordFilter(ignoreCase, not, field, operator, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.field;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents fields that are displayed in the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public enum Field {
|
||||||
|
REGION_NAME("RNAME", "Region Name", true, true, FieldValueType.STRING),
|
||||||
|
NAMESPACE("NAMESPACE", "Namespace Name", true, true, FieldValueType.STRING),
|
||||||
|
TABLE("TABLE", "Table Name", true, true, FieldValueType.STRING),
|
||||||
|
START_CODE("SCODE", "Start Code", false, true, FieldValueType.STRING),
|
||||||
|
REPLICA_ID("REPID", "Replica ID", false, false, FieldValueType.STRING),
|
||||||
|
REGION("REGION", "Encoded Region Name", false, true, FieldValueType.STRING),
|
||||||
|
REGION_SERVER("RS", "Short Region Server Name", true, true, FieldValueType.STRING),
|
||||||
|
LONG_REGION_SERVER("LRS", "Long Region Server Name", true, true, FieldValueType.STRING),
|
||||||
|
REQUEST_COUNT_PER_SECOND("#REQ/S", "Request Count per second", false, false,
|
||||||
|
FieldValueType.LONG),
|
||||||
|
READ_REQUEST_COUNT_PER_SECOND("#READ/S", "Read Request Count per second", false, false,
|
||||||
|
FieldValueType.LONG),
|
||||||
|
FILTERED_READ_REQUEST_COUNT_PER_SECOND("#FREAD/S", "Filtered Read Request Count per second",
|
||||||
|
false, false, FieldValueType.LONG),
|
||||||
|
WRITE_REQUEST_COUNT_PER_SECOND("#WRITE/S", "Write Request Count per second", false, false,
|
||||||
|
FieldValueType.LONG),
|
||||||
|
STORE_FILE_SIZE("SF", "StoreFile Size", false, false, FieldValueType.SIZE),
|
||||||
|
UNCOMPRESSED_STORE_FILE_SIZE("USF", "Uncompressed StoreFile Size", false, false,
|
||||||
|
FieldValueType.SIZE),
|
||||||
|
NUM_STORE_FILES("#SF", "Number of StoreFiles", false, false, FieldValueType.INTEGER),
|
||||||
|
MEM_STORE_SIZE("MEMSTORE", "MemStore Size", false, false, FieldValueType.SIZE),
|
||||||
|
LOCALITY("LOCALITY", "Block Locality", false, false, FieldValueType.FLOAT),
|
||||||
|
START_KEY("SKEY", "Start Key", true, true, FieldValueType.STRING),
|
||||||
|
COMPACTING_CELL_COUNT("#COMPingCELL", "Compacting Cell Count", false, false,
|
||||||
|
FieldValueType.LONG),
|
||||||
|
COMPACTED_CELL_COUNT("#COMPedCELL", "Compacted Cell Count", false, false, FieldValueType.LONG),
|
||||||
|
COMPACTION_PROGRESS("%COMP", "Compaction Progress", false, false, FieldValueType.PERCENT),
|
||||||
|
LAST_MAJOR_COMPACTION_TIME("LASTMCOMP", "Last Major Compaction Time", false, true,
|
||||||
|
FieldValueType.STRING),
|
||||||
|
REGION_COUNT("#REGION", "Region Count", false, false, FieldValueType.INTEGER),
|
||||||
|
USED_HEAP_SIZE("UHEAP", "Used Heap Size", false, false, FieldValueType.SIZE),
|
||||||
|
MAX_HEAP_SIZE("MHEAP", "Max Heap Size", false, false, FieldValueType.SIZE);
|
||||||
|
|
||||||
|
private final String header;
|
||||||
|
private final String description;
|
||||||
|
private final boolean autoAdjust;
|
||||||
|
private final boolean leftJustify;
|
||||||
|
private final FieldValueType fieldValueType;
|
||||||
|
|
||||||
|
Field(String header, String description, boolean autoAdjust, boolean leftJustify,
|
||||||
|
FieldValueType fieldValueType) {
|
||||||
|
this.header = Objects.requireNonNull(header);
|
||||||
|
this.description = Objects.requireNonNull(description);
|
||||||
|
this.autoAdjust = autoAdjust;
|
||||||
|
this.leftJustify = leftJustify;
|
||||||
|
this.fieldValueType = Objects.requireNonNull(fieldValueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldValue newValue(Object value) {
|
||||||
|
return new FieldValue(value, fieldValueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeader() {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAutoAdjust() {
|
||||||
|
return autoAdjust;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLeftJustify() {
|
||||||
|
return leftJustify;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldValueType getFieldValueType() {
|
||||||
|
return fieldValueType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.field;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a field.
|
||||||
|
*
|
||||||
|
* This has a {@link Field} itself and additional information (e.g. {@code defaultLength} and
|
||||||
|
* {@code displayByDefault}). This additional information is different between the
|
||||||
|
* {@link org.apache.hadoop.hbase.hbtop.mode.Mode}s even when the field is the same. That's why the
|
||||||
|
* additional information is separated from {@link Field}.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class FieldInfo {
|
||||||
|
private final Field field;
|
||||||
|
private final int defaultLength;
|
||||||
|
private final boolean displayByDefault;
|
||||||
|
|
||||||
|
public FieldInfo(Field field, int defaultLength, boolean displayByDefault) {
|
||||||
|
this.field = Objects.requireNonNull(field);
|
||||||
|
this.defaultLength = defaultLength;
|
||||||
|
this.displayByDefault = displayByDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field getField() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultLength() {
|
||||||
|
return defaultLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDisplayByDefault() {
|
||||||
|
return displayByDefault;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,284 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.field;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.Size;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a value of a field.
|
||||||
|
*
|
||||||
|
* The type of a value is defined by {@link FieldValue}.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class FieldValue implements Comparable<FieldValue> {
|
||||||
|
|
||||||
|
private final Object value;
|
||||||
|
private final FieldValueType type;
|
||||||
|
|
||||||
|
FieldValue(Object value, FieldValueType type) {
|
||||||
|
Objects.requireNonNull(value);
|
||||||
|
this.type = Objects.requireNonNull(type);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case STRING:
|
||||||
|
if (value instanceof String) {
|
||||||
|
this.value = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
|
||||||
|
case INTEGER:
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
this.value = value;
|
||||||
|
break;
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
this.value = Integer.valueOf((String) value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
|
||||||
|
case LONG:
|
||||||
|
if (value instanceof Long) {
|
||||||
|
this.value = value;
|
||||||
|
break;
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
this.value = Long.valueOf((String) value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
|
||||||
|
case FLOAT:
|
||||||
|
if (value instanceof Float) {
|
||||||
|
this.value = value;
|
||||||
|
break;
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
this.value = Float.valueOf((String) value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
|
||||||
|
case SIZE:
|
||||||
|
if (value instanceof Size) {
|
||||||
|
this.value = optimizeSize((Size) value);
|
||||||
|
break;
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
this.value = optimizeSize(parseSizeString((String) value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
|
||||||
|
case PERCENT:
|
||||||
|
if (value instanceof Float) {
|
||||||
|
this.value = value;
|
||||||
|
break;
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
this.value = parsePercentString((String) value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Size optimizeSize(Size size) {
|
||||||
|
if (size.get(Size.Unit.BYTE) < 1024d) {
|
||||||
|
return size.getUnit() == Size.Unit.BYTE ?
|
||||||
|
size : new Size(size.get(Size.Unit.BYTE), Size.Unit.BYTE);
|
||||||
|
} else if (size.get(Size.Unit.KILOBYTE) < 1024d) {
|
||||||
|
return size.getUnit() == Size.Unit.KILOBYTE ?
|
||||||
|
size : new Size(size.get(Size.Unit.KILOBYTE), Size.Unit.KILOBYTE);
|
||||||
|
} else if (size.get(Size.Unit.MEGABYTE) < 1024d) {
|
||||||
|
return size.getUnit() == Size.Unit.MEGABYTE ?
|
||||||
|
size : new Size(size.get(Size.Unit.MEGABYTE), Size.Unit.MEGABYTE);
|
||||||
|
} else if (size.get(Size.Unit.GIGABYTE) < 1024d) {
|
||||||
|
return size.getUnit() == Size.Unit.GIGABYTE ?
|
||||||
|
size : new Size(size.get(Size.Unit.GIGABYTE), Size.Unit.GIGABYTE);
|
||||||
|
} else if (size.get(Size.Unit.TERABYTE) < 1024d) {
|
||||||
|
return size.getUnit() == Size.Unit.TERABYTE ?
|
||||||
|
size : new Size(size.get(Size.Unit.TERABYTE), Size.Unit.TERABYTE);
|
||||||
|
}
|
||||||
|
return size.getUnit() == Size.Unit.PETABYTE ?
|
||||||
|
size : new Size(size.get(Size.Unit.PETABYTE), Size.Unit.PETABYTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Size parseSizeString(String sizeString) {
|
||||||
|
if (sizeString.length() < 3) {
|
||||||
|
throw new IllegalArgumentException("invalid size");
|
||||||
|
}
|
||||||
|
|
||||||
|
String valueString = sizeString.substring(0, sizeString.length() - 2);
|
||||||
|
String unitSimpleName = sizeString.substring(sizeString.length() - 2);
|
||||||
|
return new Size(Double.parseDouble(valueString), convertToUnit(unitSimpleName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Size.Unit convertToUnit(String unitSimpleName) {
|
||||||
|
for (Size.Unit unit: Size.Unit.values()) {
|
||||||
|
if (unitSimpleName.equals(unit.getSimpleName())) {
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("invalid size");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Float parsePercentString(String percentString) {
|
||||||
|
if (percentString.endsWith("%")) {
|
||||||
|
percentString = percentString.substring(0, percentString.length() - 1);
|
||||||
|
}
|
||||||
|
return Float.valueOf(percentString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String asString() {
|
||||||
|
return toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int asInt() {
|
||||||
|
return (Integer) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long asLong() {
|
||||||
|
return (Long) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float asFloat() {
|
||||||
|
return (Float) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size asSize() {
|
||||||
|
return (Size) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
switch (type) {
|
||||||
|
case STRING:
|
||||||
|
case INTEGER:
|
||||||
|
case LONG:
|
||||||
|
case FLOAT:
|
||||||
|
case SIZE:
|
||||||
|
return value.toString();
|
||||||
|
|
||||||
|
case PERCENT:
|
||||||
|
return String.format("%.2f", (Float) value) + "%";
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NonNull FieldValue o) {
|
||||||
|
if (type != o.type) {
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case STRING:
|
||||||
|
return ((String) value).compareTo((String) o.value);
|
||||||
|
|
||||||
|
case INTEGER:
|
||||||
|
return ((Integer) value).compareTo((Integer) o.value);
|
||||||
|
|
||||||
|
case LONG:
|
||||||
|
return ((Long) value).compareTo((Long) o.value);
|
||||||
|
|
||||||
|
case FLOAT:
|
||||||
|
case PERCENT:
|
||||||
|
return ((Float) value).compareTo((Float) o.value);
|
||||||
|
|
||||||
|
case SIZE:
|
||||||
|
return ((Size) value).compareTo((Size) o.value);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof FieldValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FieldValue that = (FieldValue) o;
|
||||||
|
return value.equals(that.value) && type == that.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(value, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldValue plus(FieldValue o) {
|
||||||
|
if (type != o.type) {
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case STRING:
|
||||||
|
return new FieldValue(((String) value).concat((String) o.value), type);
|
||||||
|
|
||||||
|
case INTEGER:
|
||||||
|
return new FieldValue(((Integer) value) + ((Integer) o.value), type);
|
||||||
|
|
||||||
|
case LONG:
|
||||||
|
return new FieldValue(((Long) value) + ((Long) o.value), type);
|
||||||
|
|
||||||
|
case FLOAT:
|
||||||
|
case PERCENT:
|
||||||
|
return new FieldValue(((Float) value) + ((Float) o.value), type);
|
||||||
|
|
||||||
|
case SIZE:
|
||||||
|
Size size = (Size) value;
|
||||||
|
Size oSize = (Size) o.value;
|
||||||
|
Size.Unit unit = size.getUnit();
|
||||||
|
return new FieldValue(new Size(size.get(unit) + oSize.get(unit), unit), type);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareToIgnoreCase(FieldValue o) {
|
||||||
|
if (type != o.type) {
|
||||||
|
throw new IllegalArgumentException("invalid type");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case STRING:
|
||||||
|
return ((String) value).compareToIgnoreCase((String) o.value);
|
||||||
|
|
||||||
|
case INTEGER:
|
||||||
|
case LONG:
|
||||||
|
case FLOAT:
|
||||||
|
case SIZE:
|
||||||
|
case PERCENT:
|
||||||
|
return compareTo(o);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.field;
|
||||||
|
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the type of a {@link FieldValue}.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public enum FieldValueType {
|
||||||
|
STRING, INTEGER, LONG, FLOAT, SIZE, PERCENT
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about drilling down.
|
||||||
|
*
|
||||||
|
* When drilling down, going to next {@link Mode} with initial {@link RecordFilter}s.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class DrillDownInfo {
|
||||||
|
private final Mode nextMode;
|
||||||
|
private final List<RecordFilter> initialFilters;
|
||||||
|
|
||||||
|
public DrillDownInfo(Mode nextMode, List<RecordFilter> initialFilters) {
|
||||||
|
this.nextMode = Objects.requireNonNull(nextMode);
|
||||||
|
this.initialFilters = Collections.unmodifiableList(new ArrayList<>(initialFilters));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mode getNextMode() {
|
||||||
|
return nextMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RecordFilter> getInitialFilters() {
|
||||||
|
return initialFilters;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetrics;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a display mode in the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public enum Mode {
|
||||||
|
NAMESPACE("Namespace", "Record per Namespace", new NamespaceModeStrategy()),
|
||||||
|
TABLE("Table", "Record per Table", new TableModeStrategy()),
|
||||||
|
REGION("Region", "Record per Region", new RegionModeStrategy()),
|
||||||
|
REGION_SERVER("RegionServer", "Record per RegionServer", new RegionServerModeStrategy());
|
||||||
|
|
||||||
|
private final String header;
|
||||||
|
private final String description;
|
||||||
|
private final ModeStrategy modeStrategy;
|
||||||
|
|
||||||
|
Mode(String header, String description, ModeStrategy modeStrategy) {
|
||||||
|
this.header = Objects.requireNonNull(header);
|
||||||
|
this.description = Objects.requireNonNull(description);
|
||||||
|
this.modeStrategy = Objects.requireNonNull(modeStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeader() {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Record> getRecords(ClusterMetrics clusterMetrics) {
|
||||||
|
return modeStrategy.getRecords(clusterMetrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FieldInfo> getFieldInfos() {
|
||||||
|
return modeStrategy.getFieldInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field getDefaultSortField() {
|
||||||
|
return modeStrategy.getDefaultSortField();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public DrillDownInfo drillDown(Record currentRecord) {
|
||||||
|
return modeStrategy.drillDown(currentRecord);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetrics;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for strategy logic for {@link Mode}.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
interface ModeStrategy {
|
||||||
|
List<FieldInfo> getFieldInfos();
|
||||||
|
Field getDefaultSortField();
|
||||||
|
List<Record> getRecords(ClusterMetrics clusterMetrics);
|
||||||
|
@Nullable DrillDownInfo drillDown(Record selectedRecord);
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetrics;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for {@link ModeStrategy} for Namespace Mode.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class NamespaceModeStrategy implements ModeStrategy {
|
||||||
|
|
||||||
|
private final List<FieldInfo> fieldInfos = Arrays.asList(
|
||||||
|
new FieldInfo(Field.NAMESPACE, 0, true),
|
||||||
|
new FieldInfo(Field.REGION_COUNT, 7, true),
|
||||||
|
new FieldInfo(Field.REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.READ_REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND, 8, true),
|
||||||
|
new FieldInfo(Field.WRITE_REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.STORE_FILE_SIZE, 13, true),
|
||||||
|
new FieldInfo(Field.UNCOMPRESSED_STORE_FILE_SIZE, 15, false),
|
||||||
|
new FieldInfo(Field.NUM_STORE_FILES, 7, true),
|
||||||
|
new FieldInfo(Field.MEM_STORE_SIZE, 11, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
private final RegionModeStrategy regionModeStrategy = new RegionModeStrategy();
|
||||||
|
|
||||||
|
NamespaceModeStrategy(){
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FieldInfo> getFieldInfos() {
|
||||||
|
return fieldInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Field getDefaultSortField() {
|
||||||
|
return Field.REQUEST_COUNT_PER_SECOND;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Record> getRecords(ClusterMetrics clusterMetrics) {
|
||||||
|
// Get records from RegionModeStrategy and add REGION_COUNT field
|
||||||
|
List<Record> records = regionModeStrategy.getRecords(clusterMetrics).stream()
|
||||||
|
.map(record ->
|
||||||
|
Record.ofEntries(fieldInfos.stream()
|
||||||
|
.filter(fi -> record.containsKey(fi.getField()))
|
||||||
|
.map(fi -> Record.entry(fi.getField(), record.get(fi.getField())))))
|
||||||
|
.map(record -> Record.builder().putAll(record).put(Field.REGION_COUNT, 1).build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Aggregation by NAMESPACE field
|
||||||
|
return records.stream()
|
||||||
|
.collect(Collectors.groupingBy(r -> r.get(Field.NAMESPACE).asString()))
|
||||||
|
.entrySet().stream()
|
||||||
|
.flatMap(
|
||||||
|
e -> e.getValue().stream()
|
||||||
|
.reduce(Record::combine)
|
||||||
|
.map(Stream::of)
|
||||||
|
.orElse(Stream.empty()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DrillDownInfo drillDown(Record selectedRecord) {
|
||||||
|
List<RecordFilter> initialFilters =
|
||||||
|
Collections.singletonList(RecordFilter.newBuilder(Field.NAMESPACE)
|
||||||
|
.doubleEquals(selectedRecord.get(Field.NAMESPACE)));
|
||||||
|
return new DrillDownInfo(Mode.TABLE, initialFilters);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.commons.lang3.time.FastDateFormat;
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetrics;
|
||||||
|
import org.apache.hadoop.hbase.RegionMetrics;
|
||||||
|
import org.apache.hadoop.hbase.ServerMetrics;
|
||||||
|
import org.apache.hadoop.hbase.TableName;
|
||||||
|
import org.apache.hadoop.hbase.client.RegionInfo;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for {@link ModeStrategy} for Region Mode.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class RegionModeStrategy implements ModeStrategy {
|
||||||
|
|
||||||
|
private final List<FieldInfo> fieldInfos = Arrays.asList(
|
||||||
|
new FieldInfo(Field.REGION_NAME, 0, false),
|
||||||
|
new FieldInfo(Field.NAMESPACE, 0, true),
|
||||||
|
new FieldInfo(Field.TABLE, 0, true),
|
||||||
|
new FieldInfo(Field.START_CODE, 13, false),
|
||||||
|
new FieldInfo(Field.REPLICA_ID, 5, false),
|
||||||
|
new FieldInfo(Field.REGION, 32, true),
|
||||||
|
new FieldInfo(Field.REGION_SERVER, 0, true),
|
||||||
|
new FieldInfo(Field.LONG_REGION_SERVER, 0, false),
|
||||||
|
new FieldInfo(Field.REQUEST_COUNT_PER_SECOND, 8, true),
|
||||||
|
new FieldInfo(Field.READ_REQUEST_COUNT_PER_SECOND, 8, true),
|
||||||
|
new FieldInfo(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND, 8, true),
|
||||||
|
new FieldInfo(Field.WRITE_REQUEST_COUNT_PER_SECOND, 8, true),
|
||||||
|
new FieldInfo(Field.STORE_FILE_SIZE, 10, true),
|
||||||
|
new FieldInfo(Field.UNCOMPRESSED_STORE_FILE_SIZE, 12, false),
|
||||||
|
new FieldInfo(Field.NUM_STORE_FILES,4, true),
|
||||||
|
new FieldInfo(Field.MEM_STORE_SIZE, 8, true),
|
||||||
|
new FieldInfo(Field.LOCALITY, 8, true),
|
||||||
|
new FieldInfo(Field.START_KEY, 0, false),
|
||||||
|
new FieldInfo(Field.COMPACTING_CELL_COUNT, 12, false),
|
||||||
|
new FieldInfo(Field.COMPACTED_CELL_COUNT, 12, false),
|
||||||
|
new FieldInfo(Field.COMPACTION_PROGRESS, 7, false),
|
||||||
|
new FieldInfo(Field.LAST_MAJOR_COMPACTION_TIME, 19, false)
|
||||||
|
);
|
||||||
|
|
||||||
|
private final Map<String, RequestCountPerSecond> requestCountPerSecondMap = new HashMap<>();
|
||||||
|
|
||||||
|
RegionModeStrategy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FieldInfo> getFieldInfos() {
|
||||||
|
return fieldInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Field getDefaultSortField() {
|
||||||
|
return Field.REQUEST_COUNT_PER_SECOND;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Record> getRecords(ClusterMetrics clusterMetrics) {
|
||||||
|
List<Record> ret = new ArrayList<>();
|
||||||
|
for (ServerMetrics sm : clusterMetrics.getLiveServerMetrics().values()) {
|
||||||
|
long lastReportTimestamp = sm.getLastReportTimestamp();
|
||||||
|
for (RegionMetrics rm : sm.getRegionMetrics().values()) {
|
||||||
|
ret.add(createRecord(sm, rm, lastReportTimestamp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Record createRecord(ServerMetrics serverMetrics, RegionMetrics regionMetrics,
|
||||||
|
long lastReportTimestamp) {
|
||||||
|
|
||||||
|
Record.Builder builder = Record.builder();
|
||||||
|
|
||||||
|
String regionName = regionMetrics.getNameAsString();
|
||||||
|
builder.put(Field.REGION_NAME, regionName);
|
||||||
|
|
||||||
|
String namespaceName = "";
|
||||||
|
String tableName = "";
|
||||||
|
String region = "";
|
||||||
|
String startKey = "";
|
||||||
|
String startCode = "";
|
||||||
|
String replicaId = "";
|
||||||
|
try {
|
||||||
|
byte[][] elements = RegionInfo.parseRegionName(regionMetrics.getRegionName());
|
||||||
|
TableName tn = TableName.valueOf(elements[0]);
|
||||||
|
namespaceName = tn.getNamespaceAsString();
|
||||||
|
tableName = tn.getQualifierAsString();
|
||||||
|
startKey = Bytes.toStringBinary(elements[1]);
|
||||||
|
startCode = Bytes.toString(elements[2]);
|
||||||
|
replicaId = elements.length == 4 ?
|
||||||
|
Integer.valueOf(Bytes.toString(elements[3])).toString() : "";
|
||||||
|
region = RegionInfo.encodeRegionName(regionMetrics.getRegionName());
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.put(Field.NAMESPACE, namespaceName);
|
||||||
|
builder.put(Field.TABLE, tableName);
|
||||||
|
builder.put(Field.START_CODE, startCode);
|
||||||
|
builder.put(Field.REPLICA_ID, replicaId);
|
||||||
|
builder.put(Field.REGION, region);
|
||||||
|
builder.put(Field.START_KEY, startKey);
|
||||||
|
builder.put(Field.REGION_SERVER, serverMetrics.getServerName().toShortString());
|
||||||
|
builder.put(Field.LONG_REGION_SERVER, serverMetrics.getServerName().getServerName());
|
||||||
|
|
||||||
|
RequestCountPerSecond requestCountPerSecond = requestCountPerSecondMap.get(regionName);
|
||||||
|
if (requestCountPerSecond == null) {
|
||||||
|
requestCountPerSecond = new RequestCountPerSecond();
|
||||||
|
requestCountPerSecondMap.put(regionName, requestCountPerSecond);
|
||||||
|
}
|
||||||
|
requestCountPerSecond.refresh(lastReportTimestamp, regionMetrics.getReadRequestCount(),
|
||||||
|
regionMetrics.getFilteredReadRequestCount(), regionMetrics.getWriteRequestCount());
|
||||||
|
|
||||||
|
builder.put(Field.READ_REQUEST_COUNT_PER_SECOND,
|
||||||
|
requestCountPerSecond.getReadRequestCountPerSecond());
|
||||||
|
builder.put(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND,
|
||||||
|
requestCountPerSecond.getFilteredReadRequestCountPerSecond());
|
||||||
|
builder.put(Field.WRITE_REQUEST_COUNT_PER_SECOND,
|
||||||
|
requestCountPerSecond.getWriteRequestCountPerSecond());
|
||||||
|
builder.put(Field.REQUEST_COUNT_PER_SECOND,
|
||||||
|
requestCountPerSecond.getRequestCountPerSecond());
|
||||||
|
|
||||||
|
builder.put(Field.STORE_FILE_SIZE, regionMetrics.getStoreFileSize());
|
||||||
|
builder.put(Field.UNCOMPRESSED_STORE_FILE_SIZE, regionMetrics.getUncompressedStoreFileSize());
|
||||||
|
builder.put(Field.NUM_STORE_FILES, regionMetrics.getStoreFileCount());
|
||||||
|
builder.put(Field.MEM_STORE_SIZE, regionMetrics.getMemStoreSize());
|
||||||
|
builder.put(Field.LOCALITY, regionMetrics.getDataLocality());
|
||||||
|
|
||||||
|
long compactingCellCount = regionMetrics.getCompactingCellCount();
|
||||||
|
long compactedCellCount = regionMetrics.getCompactedCellCount();
|
||||||
|
float compactionProgress = 0;
|
||||||
|
if (compactedCellCount > 0) {
|
||||||
|
compactionProgress = 100 * ((float) compactedCellCount / compactingCellCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.put(Field.COMPACTING_CELL_COUNT, compactingCellCount);
|
||||||
|
builder.put(Field.COMPACTED_CELL_COUNT, compactedCellCount);
|
||||||
|
builder.put(Field.COMPACTION_PROGRESS, compactionProgress);
|
||||||
|
|
||||||
|
FastDateFormat df = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
|
||||||
|
long lastMajorCompactionTimestamp = regionMetrics.getLastMajorCompactionTimestamp();
|
||||||
|
|
||||||
|
builder.put(Field.LAST_MAJOR_COMPACTION_TIME,
|
||||||
|
lastMajorCompactionTimestamp == 0 ? "" : df.format(lastMajorCompactionTimestamp));
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public DrillDownInfo drillDown(Record selectedRecord) {
|
||||||
|
// do nothing
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetrics;
|
||||||
|
import org.apache.hadoop.hbase.ServerMetrics;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for {@link ModeStrategy} for RegionServer Mode.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class RegionServerModeStrategy implements ModeStrategy {
|
||||||
|
|
||||||
|
private final List<FieldInfo> fieldInfos = Arrays.asList(
|
||||||
|
new FieldInfo(Field.REGION_SERVER, 0, true),
|
||||||
|
new FieldInfo(Field.LONG_REGION_SERVER, 0, false),
|
||||||
|
new FieldInfo(Field.REGION_COUNT, 7, true),
|
||||||
|
new FieldInfo(Field.REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.READ_REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND, 8, true),
|
||||||
|
new FieldInfo(Field.WRITE_REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.STORE_FILE_SIZE, 13, true),
|
||||||
|
new FieldInfo(Field.UNCOMPRESSED_STORE_FILE_SIZE, 15, false),
|
||||||
|
new FieldInfo(Field.NUM_STORE_FILES, 7, true),
|
||||||
|
new FieldInfo(Field.MEM_STORE_SIZE, 11, true),
|
||||||
|
new FieldInfo(Field.USED_HEAP_SIZE, 11, true),
|
||||||
|
new FieldInfo(Field.MAX_HEAP_SIZE, 11, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
private final RegionModeStrategy regionModeStrategy = new RegionModeStrategy();
|
||||||
|
|
||||||
|
RegionServerModeStrategy(){
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FieldInfo> getFieldInfos() {
|
||||||
|
return fieldInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Field getDefaultSortField() {
|
||||||
|
return Field.REQUEST_COUNT_PER_SECOND;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Record> getRecords(ClusterMetrics clusterMetrics) {
|
||||||
|
// Get records from RegionModeStrategy and add REGION_COUNT field
|
||||||
|
List<Record> records = regionModeStrategy.getRecords(clusterMetrics).stream()
|
||||||
|
.map(record ->
|
||||||
|
Record.ofEntries(fieldInfos.stream()
|
||||||
|
.filter(fi -> record.containsKey(fi.getField()))
|
||||||
|
.map(fi -> Record.entry(fi.getField(), record.get(fi.getField())))))
|
||||||
|
.map(record -> Record.builder().putAll(record).put(Field.REGION_COUNT, 1).build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Aggregation by LONG_REGION_SERVER field
|
||||||
|
Map<String, Record> retMap = records.stream()
|
||||||
|
.collect(Collectors.groupingBy(r -> r.get(Field.LONG_REGION_SERVER).asString()))
|
||||||
|
.entrySet().stream()
|
||||||
|
.flatMap(
|
||||||
|
e -> e.getValue().stream()
|
||||||
|
.reduce(Record::combine)
|
||||||
|
.map(Stream::of)
|
||||||
|
.orElse(Stream.empty()))
|
||||||
|
.collect(Collectors.toMap(r -> r.get(Field.LONG_REGION_SERVER).asString(), r -> r));
|
||||||
|
|
||||||
|
// Add USED_HEAP_SIZE field and MAX_HEAP_SIZE field
|
||||||
|
for (ServerMetrics sm : clusterMetrics.getLiveServerMetrics().values()) {
|
||||||
|
Record record = retMap.get(sm.getServerName().getServerName());
|
||||||
|
if (record == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Record newRecord = Record.builder().putAll(record)
|
||||||
|
.put(Field.USED_HEAP_SIZE, sm.getUsedHeapSize())
|
||||||
|
.put(Field.MAX_HEAP_SIZE, sm.getMaxHeapSize()).build();
|
||||||
|
|
||||||
|
retMap.put(sm.getServerName().getServerName(), newRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<>(retMap.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DrillDownInfo drillDown(Record selectedRecord) {
|
||||||
|
List<RecordFilter> initialFilters = Collections.singletonList(RecordFilter
|
||||||
|
.newBuilder(Field.REGION_SERVER)
|
||||||
|
.doubleEquals(selectedRecord.get(Field.REGION_SERVER)));
|
||||||
|
return new DrillDownInfo(Mode.REGION, initialFilters);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for calculating request counts per second.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class RequestCountPerSecond {
|
||||||
|
private long previousLastReportTimestamp;
|
||||||
|
private long previousReadRequestCount;
|
||||||
|
private long previousFilteredReadRequestCount;
|
||||||
|
private long previousWriteRequestCount;
|
||||||
|
private long readRequestCountPerSecond;
|
||||||
|
private long filteredReadRequestCountPerSecond;
|
||||||
|
private long writeRequestCountPerSecond;
|
||||||
|
|
||||||
|
public void refresh(long lastReportTimestamp, long readRequestCount,
|
||||||
|
long filteredReadRequestCount, long writeRequestCount) {
|
||||||
|
if (previousLastReportTimestamp == 0) {
|
||||||
|
previousLastReportTimestamp = lastReportTimestamp;
|
||||||
|
previousReadRequestCount = readRequestCount;
|
||||||
|
previousFilteredReadRequestCount = filteredReadRequestCount;
|
||||||
|
previousWriteRequestCount = writeRequestCount;
|
||||||
|
} else if (previousLastReportTimestamp != lastReportTimestamp) {
|
||||||
|
readRequestCountPerSecond = (readRequestCount - previousReadRequestCount) /
|
||||||
|
((lastReportTimestamp - previousLastReportTimestamp) / 1000);
|
||||||
|
filteredReadRequestCountPerSecond =
|
||||||
|
(filteredReadRequestCount - previousFilteredReadRequestCount) /
|
||||||
|
((lastReportTimestamp - previousLastReportTimestamp) / 1000);
|
||||||
|
writeRequestCountPerSecond = (writeRequestCount - previousWriteRequestCount) /
|
||||||
|
((lastReportTimestamp - previousLastReportTimestamp) / 1000);
|
||||||
|
|
||||||
|
previousLastReportTimestamp = lastReportTimestamp;
|
||||||
|
previousReadRequestCount = readRequestCount;
|
||||||
|
previousFilteredReadRequestCount = filteredReadRequestCount;
|
||||||
|
previousWriteRequestCount = writeRequestCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getReadRequestCountPerSecond() {
|
||||||
|
return readRequestCountPerSecond < 0 ? 0 : readRequestCountPerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFilteredReadRequestCountPerSecond() {
|
||||||
|
return filteredReadRequestCountPerSecond < 0 ? 0 : filteredReadRequestCountPerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getWriteRequestCountPerSecond() {
|
||||||
|
return writeRequestCountPerSecond < 0 ? 0 : writeRequestCountPerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRequestCountPerSecond() {
|
||||||
|
return getReadRequestCountPerSecond() + getWriteRequestCountPerSecond();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetrics;
|
||||||
|
import org.apache.hadoop.hbase.TableName;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for {@link ModeStrategy} for Table Mode.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class TableModeStrategy implements ModeStrategy {
|
||||||
|
|
||||||
|
private final List<FieldInfo> fieldInfos = Arrays.asList(
|
||||||
|
new FieldInfo(Field.NAMESPACE, 0, true),
|
||||||
|
new FieldInfo(Field.TABLE, 0, true),
|
||||||
|
new FieldInfo(Field.REGION_COUNT, 7, true),
|
||||||
|
new FieldInfo(Field.REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.READ_REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND, 8, true),
|
||||||
|
new FieldInfo(Field.WRITE_REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.STORE_FILE_SIZE, 13, true),
|
||||||
|
new FieldInfo(Field.UNCOMPRESSED_STORE_FILE_SIZE, 15, false),
|
||||||
|
new FieldInfo(Field.NUM_STORE_FILES, 7, true),
|
||||||
|
new FieldInfo(Field.MEM_STORE_SIZE, 11, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
private final RegionModeStrategy regionModeStrategy = new RegionModeStrategy();
|
||||||
|
|
||||||
|
TableModeStrategy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FieldInfo> getFieldInfos() {
|
||||||
|
return fieldInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Field getDefaultSortField() {
|
||||||
|
return Field.REQUEST_COUNT_PER_SECOND;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Record> getRecords(ClusterMetrics clusterMetrics) {
|
||||||
|
// Get records from RegionModeStrategy and add REGION_COUNT field
|
||||||
|
List<Record> records = regionModeStrategy.getRecords(clusterMetrics).stream()
|
||||||
|
.map(record ->
|
||||||
|
Record.ofEntries(fieldInfos.stream()
|
||||||
|
.filter(fi -> record.containsKey(fi.getField()))
|
||||||
|
.map(fi -> Record.entry(fi.getField(), record.get(fi.getField())))))
|
||||||
|
.map(record -> Record.builder().putAll(record).put(Field.REGION_COUNT, 1).build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Aggregation by NAMESPACE field and TABLE field
|
||||||
|
return records.stream()
|
||||||
|
.collect(Collectors.groupingBy(r -> {
|
||||||
|
String namespace = r.get(Field.NAMESPACE).asString();
|
||||||
|
String table = r.get(Field.TABLE).asString();
|
||||||
|
return TableName.valueOf(namespace, table);
|
||||||
|
}))
|
||||||
|
.entrySet().stream()
|
||||||
|
.flatMap(
|
||||||
|
e -> e.getValue().stream()
|
||||||
|
.reduce(Record::combine)
|
||||||
|
.map(Stream::of)
|
||||||
|
.orElse(Stream.empty()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DrillDownInfo drillDown(Record selectedRecord) {
|
||||||
|
List<RecordFilter> initialFilters = Arrays.asList(
|
||||||
|
RecordFilter.newBuilder(Field.NAMESPACE).doubleEquals(selectedRecord.get(Field.NAMESPACE)),
|
||||||
|
RecordFilter.newBuilder(Field.TABLE).doubleEquals(selectedRecord.get(Field.TABLE)));
|
||||||
|
return new DrillDownInfo(Mode.REGION, initialFilters);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract class for {@link ScreenView} that has the common useful methods and the default
|
||||||
|
* implementations for the abstract methods.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public abstract class AbstractScreenView implements ScreenView {
|
||||||
|
|
||||||
|
private final Screen screen;
|
||||||
|
private final Terminal terminal;
|
||||||
|
|
||||||
|
public AbstractScreenView(Screen screen, Terminal terminal) {
|
||||||
|
this.screen = Objects.requireNonNull(screen);
|
||||||
|
this.terminal = Objects.requireNonNull(terminal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleKeyPress(KeyPress keyPress) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleTimer() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Screen getScreen() {
|
||||||
|
return screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Terminal getTerminal() {
|
||||||
|
return terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTimer(long delay) {
|
||||||
|
screen.setTimer(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void cancelTimer() {
|
||||||
|
screen.cancelTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TerminalPrinter getTerminalPrinter(int startRow) {
|
||||||
|
return terminal.getTerminalPrinter(startRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TerminalSize getTerminalSize() {
|
||||||
|
return terminal.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected TerminalSize doResizeIfNecessary() {
|
||||||
|
return terminal.doResizeIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearTerminal() {
|
||||||
|
terminal.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshTerminal() {
|
||||||
|
terminal.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideCursor() {
|
||||||
|
terminal.hideCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCursorPosition(int column, int row) {
|
||||||
|
terminal.setCursorPosition(column, row);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.hbase.client.Admin;
|
||||||
|
import org.apache.hadoop.hbase.client.Connection;
|
||||||
|
import org.apache.hadoop.hbase.client.ConnectionFactory;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.top.TopScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.impl.TerminalImpl;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This dispatches key presses and timers to the current {@link ScreenView}.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class Screen implements Closeable {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(Screen.class);
|
||||||
|
private static final long SLEEP_TIMEOUT_MILLISECONDS = 100;
|
||||||
|
|
||||||
|
private final Connection connection;
|
||||||
|
private final Admin admin;
|
||||||
|
private final Terminal terminal;
|
||||||
|
|
||||||
|
private ScreenView currentScreenView;
|
||||||
|
private Long timerTimestamp;
|
||||||
|
|
||||||
|
public Screen(Configuration conf, long initialRefreshDelay, Mode initialMode)
|
||||||
|
throws IOException {
|
||||||
|
connection = ConnectionFactory.createConnection(conf);
|
||||||
|
admin = connection.getAdmin();
|
||||||
|
|
||||||
|
// The first screen is the top screen
|
||||||
|
this.terminal = new TerminalImpl("hbtop");
|
||||||
|
currentScreenView = new TopScreenView(this, terminal, initialRefreshDelay, admin,
|
||||||
|
initialMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
try {
|
||||||
|
admin.close();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
connection.close();
|
||||||
|
} finally {
|
||||||
|
terminal.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
currentScreenView.init();
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
KeyPress keyPress = terminal.pollKeyPress();
|
||||||
|
|
||||||
|
ScreenView nextScreenView;
|
||||||
|
if (keyPress != null) {
|
||||||
|
// Dispatch the key press to the current screen
|
||||||
|
nextScreenView = currentScreenView.handleKeyPress(keyPress);
|
||||||
|
} else {
|
||||||
|
if (timerTimestamp != null) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (timerTimestamp <= now) {
|
||||||
|
// Dispatch the timer to the current screen
|
||||||
|
timerTimestamp = null;
|
||||||
|
nextScreenView = currentScreenView.handleTimer();
|
||||||
|
} else {
|
||||||
|
if (timerTimestamp - now < SLEEP_TIMEOUT_MILLISECONDS) {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(timerTimestamp - now);
|
||||||
|
} else {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(SLEEP_TIMEOUT_MILLISECONDS);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(SLEEP_TIMEOUT_MILLISECONDS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the next screen is null, then exit
|
||||||
|
if (nextScreenView == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the next screen is not the previous, then go to the next screen
|
||||||
|
if (nextScreenView != currentScreenView) {
|
||||||
|
currentScreenView = nextScreenView;
|
||||||
|
currentScreenView.init();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Caught an exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimer(long delay) {
|
||||||
|
timerTimestamp = System.currentTimeMillis() + delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancelTimer() {
|
||||||
|
timerTimestamp = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for a screen view that handles key presses and timers.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public interface ScreenView {
|
||||||
|
void init();
|
||||||
|
@Nullable ScreenView handleKeyPress(KeyPress keyPress);
|
||||||
|
@Nullable ScreenView handleTimer();
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.field;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presentation logic for the field screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class FieldScreenPresenter {
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ResultListener {
|
||||||
|
void accept(Field sortField, List<Field> fields, EnumMap<Field, Boolean> fieldDisplayMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final FieldScreenView fieldScreenView;
|
||||||
|
private Field sortField;
|
||||||
|
private final List<Field> fields;
|
||||||
|
private final EnumMap<Field, Boolean> fieldDisplayMap;
|
||||||
|
private final ResultListener resultListener;
|
||||||
|
private final ScreenView nextScreenView;
|
||||||
|
|
||||||
|
private final int headerMaxLength;
|
||||||
|
private final int descriptionMaxLength;
|
||||||
|
|
||||||
|
private int currentPosition;
|
||||||
|
private boolean moveMode;
|
||||||
|
|
||||||
|
public FieldScreenPresenter(FieldScreenView fieldScreenView, Field sortField, List<Field> fields,
|
||||||
|
EnumMap<Field, Boolean> fieldDisplayMap, ResultListener resultListener,
|
||||||
|
ScreenView nextScreenView) {
|
||||||
|
this.fieldScreenView = Objects.requireNonNull(fieldScreenView);
|
||||||
|
this.sortField = Objects.requireNonNull(sortField);
|
||||||
|
this.fields = new ArrayList<>(Objects.requireNonNull(fields));
|
||||||
|
this.fieldDisplayMap = new EnumMap<>(Objects.requireNonNull(fieldDisplayMap));
|
||||||
|
this.resultListener = Objects.requireNonNull(resultListener);
|
||||||
|
this.nextScreenView = Objects.requireNonNull(nextScreenView);
|
||||||
|
|
||||||
|
int headerLength = 0;
|
||||||
|
int descriptionLength = 0;
|
||||||
|
for (int i = 0; i < fields.size(); i ++) {
|
||||||
|
Field field = fields.get(i);
|
||||||
|
|
||||||
|
if (field == sortField) {
|
||||||
|
currentPosition = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headerLength < field.getHeader().length()) {
|
||||||
|
headerLength = field.getHeader().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptionLength < field.getDescription().length()) {
|
||||||
|
descriptionLength = field.getDescription().length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headerMaxLength = headerLength;
|
||||||
|
descriptionMaxLength = descriptionLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
fieldScreenView.hideCursor();
|
||||||
|
fieldScreenView.clearTerminal();
|
||||||
|
fieldScreenView.showFieldScreen(sortField.getHeader(), fields, fieldDisplayMap,
|
||||||
|
currentPosition, headerMaxLength, descriptionMaxLength, moveMode);
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowUp() {
|
||||||
|
if (currentPosition > 0) {
|
||||||
|
currentPosition -= 1;
|
||||||
|
|
||||||
|
if (moveMode) {
|
||||||
|
Field tmp = fields.remove(currentPosition);
|
||||||
|
fields.add(currentPosition + 1, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
showField(currentPosition);
|
||||||
|
showField(currentPosition + 1);
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowDown() {
|
||||||
|
if (currentPosition < fields.size() - 1) {
|
||||||
|
currentPosition += 1;
|
||||||
|
|
||||||
|
if (moveMode) {
|
||||||
|
Field tmp = fields.remove(currentPosition - 1);
|
||||||
|
fields.add(currentPosition, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
showField(currentPosition);
|
||||||
|
showField(currentPosition - 1);
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pageUp() {
|
||||||
|
if (currentPosition > 0 && !moveMode) {
|
||||||
|
int previousPosition = currentPosition;
|
||||||
|
currentPosition = 0;
|
||||||
|
showField(previousPosition);
|
||||||
|
showField(currentPosition);
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pageDown() {
|
||||||
|
if (currentPosition < fields.size() - 1 && !moveMode) {
|
||||||
|
int previousPosition = currentPosition;
|
||||||
|
currentPosition = fields.size() - 1;
|
||||||
|
showField(previousPosition);
|
||||||
|
showField(currentPosition);
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void turnOnMoveMode() {
|
||||||
|
moveMode = true;
|
||||||
|
showField(currentPosition);
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void turnOffMoveMode() {
|
||||||
|
moveMode = false;
|
||||||
|
showField(currentPosition);
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchFieldDisplay() {
|
||||||
|
if (!moveMode) {
|
||||||
|
Field field = fields.get(currentPosition);
|
||||||
|
fieldDisplayMap.put(field, !fieldDisplayMap.get(field));
|
||||||
|
showField(currentPosition);
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showField(int pos) {
|
||||||
|
Field field = fields.get(pos);
|
||||||
|
fieldScreenView.showField(pos, field, fieldDisplayMap.get(field), pos == currentPosition,
|
||||||
|
headerMaxLength, descriptionMaxLength, moveMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortField() {
|
||||||
|
if (!moveMode) {
|
||||||
|
Field newSortField = fields.get(currentPosition);
|
||||||
|
if (newSortField != this.sortField) {
|
||||||
|
this.sortField = newSortField;
|
||||||
|
fieldScreenView.showScreenDescription(sortField.getHeader());
|
||||||
|
fieldScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView transitionToNextScreen() {
|
||||||
|
resultListener.accept(sortField, fields, fieldDisplayMap);
|
||||||
|
return nextScreenView;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.field;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The screen where we can change the displayed fields, the sort key and the order of the fields.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class FieldScreenView extends AbstractScreenView {
|
||||||
|
|
||||||
|
private static final int SCREEN_DESCRIPTION_START_ROW = 0;
|
||||||
|
private static final int FIELD_START_ROW = 5;
|
||||||
|
|
||||||
|
private final FieldScreenPresenter fieldScreenPresenter;
|
||||||
|
|
||||||
|
public FieldScreenView(Screen screen, Terminal terminal, Field sortField, List<Field> fields,
|
||||||
|
EnumMap<Field, Boolean> fieldDisplayMap, FieldScreenPresenter.ResultListener resultListener,
|
||||||
|
ScreenView nextScreenView) {
|
||||||
|
super(screen, terminal);
|
||||||
|
this.fieldScreenPresenter = new FieldScreenPresenter(this, sortField, fields, fieldDisplayMap,
|
||||||
|
resultListener, nextScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
fieldScreenPresenter.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleKeyPress(KeyPress keyPress) {
|
||||||
|
switch (keyPress.getType()) {
|
||||||
|
case Escape:
|
||||||
|
return fieldScreenPresenter.transitionToNextScreen();
|
||||||
|
|
||||||
|
case ArrowUp:
|
||||||
|
fieldScreenPresenter.arrowUp();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case ArrowDown:
|
||||||
|
fieldScreenPresenter.arrowDown();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case PageUp:
|
||||||
|
case Home:
|
||||||
|
fieldScreenPresenter.pageUp();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case PageDown:
|
||||||
|
case End:
|
||||||
|
fieldScreenPresenter.pageDown();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case ArrowRight:
|
||||||
|
fieldScreenPresenter.turnOnMoveMode();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case ArrowLeft:
|
||||||
|
case Enter:
|
||||||
|
fieldScreenPresenter.turnOffMoveMode();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyPress.getType() != KeyPress.Type.Character) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert keyPress.getCharacter() != null;
|
||||||
|
switch (keyPress.getCharacter()) {
|
||||||
|
case 'd':
|
||||||
|
case ' ':
|
||||||
|
fieldScreenPresenter.switchFieldDisplay();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
fieldScreenPresenter.setSortField();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
return fieldScreenPresenter.transitionToNextScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showFieldScreen(String sortFieldHeader, List<Field> fields,
|
||||||
|
EnumMap<Field, Boolean> fieldDisplayMap, int currentPosition, int headerMaxLength,
|
||||||
|
int descriptionMaxLength, boolean moveMode) {
|
||||||
|
showScreenDescription(sortFieldHeader);
|
||||||
|
|
||||||
|
for (int i = 0; i < fields.size(); i ++) {
|
||||||
|
Field field = fields.get(i);
|
||||||
|
showField(i, field, fieldDisplayMap.get(field), i == currentPosition, headerMaxLength,
|
||||||
|
descriptionMaxLength, moveMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showScreenDescription(String sortKeyHeader) {
|
||||||
|
TerminalPrinter printer = getTerminalPrinter(SCREEN_DESCRIPTION_START_ROW);
|
||||||
|
printer.startBold().print("Fields Management").stopBold().endOfLine();
|
||||||
|
printer.print("Current Sort Field: ").startBold().print(sortKeyHeader).stopBold().endOfLine();
|
||||||
|
printer.print("Navigate with up/down, Right selects for move then <Enter> or Left commits,")
|
||||||
|
.endOfLine();
|
||||||
|
printer.print("'d' or <Space> toggles display, 's' sets sort. Use 'q' or <Esc> to end!")
|
||||||
|
.endOfLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showField(int pos, Field field, boolean display, boolean selected,
|
||||||
|
int fieldHeaderMaxLength, int fieldDescriptionMaxLength, boolean moveMode) {
|
||||||
|
|
||||||
|
String fieldHeader = String.format("%-" + fieldHeaderMaxLength + "s", field.getHeader());
|
||||||
|
String fieldDescription = String.format("%-" + fieldDescriptionMaxLength + "s",
|
||||||
|
field.getDescription());
|
||||||
|
|
||||||
|
int row = FIELD_START_ROW + pos;
|
||||||
|
TerminalPrinter printer = getTerminalPrinter(row);
|
||||||
|
if (selected) {
|
||||||
|
String prefix = display ? "* " : " ";
|
||||||
|
if (moveMode) {
|
||||||
|
printer.print(prefix);
|
||||||
|
|
||||||
|
if (display) {
|
||||||
|
printer.startBold();
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.startHighlight()
|
||||||
|
.printFormat("%s = %s", fieldHeader, fieldDescription).stopHighlight();
|
||||||
|
|
||||||
|
if (display) {
|
||||||
|
printer.stopBold();
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.endOfLine();
|
||||||
|
} else {
|
||||||
|
printer.print(prefix);
|
||||||
|
|
||||||
|
if (display) {
|
||||||
|
printer.startBold();
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.startHighlight().print(fieldHeader).stopHighlight()
|
||||||
|
.printFormat(" = %s", fieldDescription);
|
||||||
|
|
||||||
|
if (display) {
|
||||||
|
printer.stopBold();
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.endOfLine();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (display) {
|
||||||
|
printer.print("* ").startBold().printFormat("%s = %s", fieldHeader, fieldDescription)
|
||||||
|
.stopBold().endOfLine();
|
||||||
|
} else {
|
||||||
|
printer.printFormat(" %s = %s", fieldHeader, fieldDescription).endOfLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.help;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a description of a command that we can execute in the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class CommandDescription {
|
||||||
|
|
||||||
|
private final List<String> keys;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
public CommandDescription(String key, String description) {
|
||||||
|
this(Collections.singletonList(Objects.requireNonNull(key)), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandDescription(List<String> keys, String description) {
|
||||||
|
this.keys = Collections.unmodifiableList(new ArrayList<>(Objects.requireNonNull(keys)));
|
||||||
|
this.description = Objects.requireNonNull(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getKeys() {
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.help;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presentation logic for the help screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class HelpScreenPresenter {
|
||||||
|
|
||||||
|
private static final CommandDescription[] COMMAND_DESCRIPTIONS = new CommandDescription[] {
|
||||||
|
new CommandDescription("f", "Add/Remove/Order/Sort the fields"),
|
||||||
|
new CommandDescription("R", "Toggle the sort order (ascending/descending)"),
|
||||||
|
new CommandDescription("m", "Select mode"),
|
||||||
|
new CommandDescription("o", "Add a filter with ignoring case"),
|
||||||
|
new CommandDescription("O", "Add a filter with case sensitive"),
|
||||||
|
new CommandDescription("^o", "Show the current filters"),
|
||||||
|
new CommandDescription("=", "Clear the current filters"),
|
||||||
|
new CommandDescription("i", "Drill down"),
|
||||||
|
new CommandDescription(
|
||||||
|
Arrays.asList("up", "down", "left", "right", "pageUp", "pageDown", "home", "end"),
|
||||||
|
"Scroll the metrics"),
|
||||||
|
new CommandDescription("d", "Change the refresh delay"),
|
||||||
|
new CommandDescription("X", "Adjust the field length"),
|
||||||
|
new CommandDescription("<Enter>", "Refresh the display"),
|
||||||
|
new CommandDescription("h", "Display this screen"),
|
||||||
|
new CommandDescription(Arrays.asList("q", "<Esc>"), "Quit")
|
||||||
|
};
|
||||||
|
|
||||||
|
private final HelpScreenView helpScreenView;
|
||||||
|
private final long refreshDelay;
|
||||||
|
private final ScreenView nextScreenView;
|
||||||
|
|
||||||
|
public HelpScreenPresenter(HelpScreenView helpScreenView, long refreshDelay,
|
||||||
|
ScreenView nextScreenView) {
|
||||||
|
this.helpScreenView = Objects.requireNonNull(helpScreenView);
|
||||||
|
this.refreshDelay = refreshDelay;
|
||||||
|
this.nextScreenView = Objects.requireNonNull(nextScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
helpScreenView.hideCursor();
|
||||||
|
helpScreenView.clearTerminal();
|
||||||
|
helpScreenView.showHelpScreen(refreshDelay, COMMAND_DESCRIPTIONS);
|
||||||
|
helpScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView transitionToNextScreen() {
|
||||||
|
return nextScreenView;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.help;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The help screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class HelpScreenView extends AbstractScreenView {
|
||||||
|
|
||||||
|
private static final int SCREEN_DESCRIPTION_START_ROW = 0;
|
||||||
|
private static final int COMMAND_DESCRIPTION_START_ROW = 3;
|
||||||
|
|
||||||
|
private final HelpScreenPresenter helpScreenPresenter;
|
||||||
|
|
||||||
|
public HelpScreenView(Screen screen, Terminal terminal, long refreshDelay,
|
||||||
|
ScreenView nextScreenView) {
|
||||||
|
super(screen, terminal);
|
||||||
|
this.helpScreenPresenter = new HelpScreenPresenter(this, refreshDelay, nextScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
helpScreenPresenter.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleKeyPress(KeyPress keyPress) {
|
||||||
|
return helpScreenPresenter.transitionToNextScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showHelpScreen(long refreshDelay, CommandDescription[] commandDescriptions) {
|
||||||
|
showScreenDescription(refreshDelay);
|
||||||
|
|
||||||
|
TerminalPrinter printer = getTerminalPrinter(COMMAND_DESCRIPTION_START_ROW);
|
||||||
|
for (CommandDescription commandDescription : commandDescriptions) {
|
||||||
|
showCommandDescription(printer, commandDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.endOfLine();
|
||||||
|
printer.print("Press any key to continue").endOfLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showScreenDescription(long refreshDelay) {
|
||||||
|
TerminalPrinter printer = getTerminalPrinter(SCREEN_DESCRIPTION_START_ROW);
|
||||||
|
printer.startBold().print("Help for Interactive Commands").stopBold().endOfLine();
|
||||||
|
printer.print("Refresh delay: ").startBold()
|
||||||
|
.print((double) refreshDelay / 1000).stopBold().endOfLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showCommandDescription(TerminalPrinter terminalPrinter,
|
||||||
|
CommandDescription commandDescription) {
|
||||||
|
terminalPrinter.print(" ");
|
||||||
|
boolean first = true;
|
||||||
|
for (String key : commandDescription.getKeys()) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
terminalPrinter.print(",");
|
||||||
|
}
|
||||||
|
terminalPrinter.startBold().print(key).stopBold();
|
||||||
|
}
|
||||||
|
|
||||||
|
terminalPrinter.printFormat(": %s", commandDescription.getDescription()).endOfLine();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.mode;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presentation logic for the mode screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class ModeScreenPresenter {
|
||||||
|
|
||||||
|
private final ModeScreenView modeScreenView;
|
||||||
|
private final Mode currentMode;
|
||||||
|
private final Consumer<Mode> resultListener;
|
||||||
|
private final ScreenView nextScreenView;
|
||||||
|
|
||||||
|
private final int modeHeaderMaxLength;
|
||||||
|
private final int modeDescriptionMaxLength;
|
||||||
|
private final List<Mode> modes = Arrays.asList(Mode.values());
|
||||||
|
|
||||||
|
private int currentPosition;
|
||||||
|
|
||||||
|
public ModeScreenPresenter(ModeScreenView modeScreenView, Mode currentMode,
|
||||||
|
Consumer<Mode> resultListener, ScreenView nextScreenView) {
|
||||||
|
this.modeScreenView = Objects.requireNonNull(modeScreenView);
|
||||||
|
this.currentMode = Objects.requireNonNull(currentMode);
|
||||||
|
this.resultListener = Objects.requireNonNull(resultListener);
|
||||||
|
this.nextScreenView = Objects.requireNonNull(nextScreenView);
|
||||||
|
|
||||||
|
int modeHeaderLength = 0;
|
||||||
|
int modeDescriptionLength = 0;
|
||||||
|
for (int i = 0; i < modes.size(); i++) {
|
||||||
|
Mode mode = modes.get(i);
|
||||||
|
if (mode == currentMode) {
|
||||||
|
currentPosition = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modeHeaderLength < mode.getHeader().length()) {
|
||||||
|
modeHeaderLength = mode.getHeader().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modeDescriptionLength < mode.getDescription().length()) {
|
||||||
|
modeDescriptionLength = mode.getDescription().length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modeHeaderMaxLength = modeHeaderLength;
|
||||||
|
modeDescriptionMaxLength = modeDescriptionLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
modeScreenView.hideCursor();
|
||||||
|
modeScreenView.clearTerminal();
|
||||||
|
modeScreenView.showModeScreen(currentMode, modes, currentPosition, modeHeaderMaxLength,
|
||||||
|
modeDescriptionMaxLength);
|
||||||
|
modeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowUp() {
|
||||||
|
if (currentPosition > 0) {
|
||||||
|
currentPosition -= 1;
|
||||||
|
showMode(currentPosition);
|
||||||
|
showMode(currentPosition + 1);
|
||||||
|
modeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowDown() {
|
||||||
|
if (currentPosition < modes.size() - 1) {
|
||||||
|
currentPosition += 1;
|
||||||
|
showMode(currentPosition);
|
||||||
|
showMode(currentPosition - 1);
|
||||||
|
modeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pageUp() {
|
||||||
|
if (currentPosition > 0) {
|
||||||
|
int previousPosition = currentPosition;
|
||||||
|
currentPosition = 0;
|
||||||
|
showMode(previousPosition);
|
||||||
|
showMode(currentPosition);
|
||||||
|
modeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pageDown() {
|
||||||
|
if (currentPosition < modes.size() - 1) {
|
||||||
|
int previousPosition = currentPosition;
|
||||||
|
currentPosition = modes.size() - 1;
|
||||||
|
showMode(previousPosition);
|
||||||
|
showMode(currentPosition);
|
||||||
|
modeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showMode(int pos) {
|
||||||
|
modeScreenView.showMode(pos, modes.get(pos), pos == currentPosition, modeHeaderMaxLength,
|
||||||
|
modeDescriptionMaxLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView transitionToNextScreen(boolean changeMode) {
|
||||||
|
Mode selectedMode = modes.get(currentPosition);
|
||||||
|
if (changeMode && currentMode != selectedMode) {
|
||||||
|
resultListener.accept(selectedMode);
|
||||||
|
}
|
||||||
|
return nextScreenView;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.mode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The screen where we can choose the {@link Mode} in the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class ModeScreenView extends AbstractScreenView {
|
||||||
|
|
||||||
|
private static final int SCREEN_DESCRIPTION_START_ROW = 0;
|
||||||
|
private static final int MODE_START_ROW = 4;
|
||||||
|
|
||||||
|
private final ModeScreenPresenter modeScreenPresenter;
|
||||||
|
|
||||||
|
public ModeScreenView(Screen screen, Terminal terminal, Mode currentMode,
|
||||||
|
Consumer<Mode> resultListener, ScreenView nextScreenView) {
|
||||||
|
super(screen, terminal);
|
||||||
|
this.modeScreenPresenter = new ModeScreenPresenter(this, currentMode, resultListener,
|
||||||
|
nextScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
modeScreenPresenter.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleKeyPress(KeyPress keyPress) {
|
||||||
|
switch (keyPress.getType()) {
|
||||||
|
case Escape:
|
||||||
|
return modeScreenPresenter.transitionToNextScreen(false);
|
||||||
|
|
||||||
|
case Enter:
|
||||||
|
return modeScreenPresenter.transitionToNextScreen(true);
|
||||||
|
|
||||||
|
case ArrowUp:
|
||||||
|
modeScreenPresenter.arrowUp();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case ArrowDown:
|
||||||
|
modeScreenPresenter.arrowDown();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case PageUp:
|
||||||
|
case Home:
|
||||||
|
modeScreenPresenter.pageUp();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case PageDown:
|
||||||
|
case End:
|
||||||
|
modeScreenPresenter.pageDown();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyPress.getType() != KeyPress.Type.Character) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert keyPress.getCharacter() != null;
|
||||||
|
switch (keyPress.getCharacter()) {
|
||||||
|
case 'q':
|
||||||
|
return modeScreenPresenter.transitionToNextScreen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showModeScreen(Mode currentMode, List<Mode> modes, int currentPosition,
|
||||||
|
int modeHeaderMaxLength, int modeDescriptionMaxLength) {
|
||||||
|
showScreenDescription(currentMode);
|
||||||
|
|
||||||
|
for (int i = 0; i < modes.size(); i++) {
|
||||||
|
showMode(i, modes.get(i), i == currentPosition,
|
||||||
|
modeHeaderMaxLength, modeDescriptionMaxLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showScreenDescription(Mode currentMode) {
|
||||||
|
TerminalPrinter printer = getTerminalPrinter(SCREEN_DESCRIPTION_START_ROW);
|
||||||
|
printer.startBold().print("Mode Management").stopBold().endOfLine();
|
||||||
|
printer.print("Current mode: ")
|
||||||
|
.startBold().print(currentMode.getHeader()).stopBold().endOfLine();
|
||||||
|
printer.print("Select mode followed by <Enter>").endOfLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showMode(int pos, Mode mode, boolean selected, int modeHeaderMaxLength,
|
||||||
|
int modeDescriptionMaxLength) {
|
||||||
|
|
||||||
|
String modeHeader = String.format("%-" + modeHeaderMaxLength + "s", mode.getHeader());
|
||||||
|
String modeDescription = String.format("%-" + modeDescriptionMaxLength + "s",
|
||||||
|
mode.getDescription());
|
||||||
|
|
||||||
|
int row = MODE_START_ROW + pos;
|
||||||
|
TerminalPrinter printer = getTerminalPrinter(row);
|
||||||
|
if (selected) {
|
||||||
|
printer.startHighlight().print(modeHeader).stopHighlight()
|
||||||
|
.printFormat(" = %s", modeDescription).endOfLine();
|
||||||
|
} else {
|
||||||
|
printer.printFormat("%s = %s", modeHeader, modeDescription).endOfLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presentation logic for the filter display mode.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class FilterDisplayModeScreenPresenter {
|
||||||
|
|
||||||
|
private final FilterDisplayModeScreenView filterDisplayModeScreenView;
|
||||||
|
private final List<RecordFilter> filters;
|
||||||
|
private final ScreenView nextScreenView;
|
||||||
|
|
||||||
|
public FilterDisplayModeScreenPresenter(FilterDisplayModeScreenView filterDisplayModeScreenView,
|
||||||
|
List<RecordFilter> filters, ScreenView nextScreenView) {
|
||||||
|
this.filterDisplayModeScreenView = Objects.requireNonNull(filterDisplayModeScreenView);
|
||||||
|
this.filters = Collections.unmodifiableList(new ArrayList<>(Objects.requireNonNull(filters)));
|
||||||
|
this.nextScreenView = Objects.requireNonNull(nextScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
filterDisplayModeScreenView.showFilters(filters);
|
||||||
|
filterDisplayModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView returnToNextScreen() {
|
||||||
|
return nextScreenView;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filter display mode in the top screen.
|
||||||
|
*
|
||||||
|
* Exit if Enter key is pressed.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class FilterDisplayModeScreenView extends AbstractScreenView {
|
||||||
|
|
||||||
|
private final int row;
|
||||||
|
private final FilterDisplayModeScreenPresenter filterDisplayModeScreenPresenter;
|
||||||
|
|
||||||
|
public FilterDisplayModeScreenView(Screen screen, Terminal terminal, int row,
|
||||||
|
List<RecordFilter> filters, ScreenView nextScreenView) {
|
||||||
|
super(screen, terminal);
|
||||||
|
this.row = row;
|
||||||
|
this.filterDisplayModeScreenPresenter =
|
||||||
|
new FilterDisplayModeScreenPresenter(this, filters, nextScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
filterDisplayModeScreenPresenter.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleKeyPress(KeyPress keyPress) {
|
||||||
|
switch (keyPress.getType()) {
|
||||||
|
case Enter:
|
||||||
|
return filterDisplayModeScreenPresenter.returnToNextScreen();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showFilters(List<RecordFilter> filters) {
|
||||||
|
String filtersString = "none";
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
filtersString = String.join(" + ",
|
||||||
|
filters.stream().map(f -> String.format("'%s'", f)).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
getTerminalPrinter(row).startBold().print("<Enter> to resume, filters: " + filtersString)
|
||||||
|
.stopBold().endOfLine();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents headers for the metrics in the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class Header {
|
||||||
|
private final Field field;
|
||||||
|
private final int length;
|
||||||
|
|
||||||
|
public Header(Field field, int length) {
|
||||||
|
this.field = Objects.requireNonNull(field);
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String format() {
|
||||||
|
return "%" + (field.isLeftJustify() ? "-" : "") + length + "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field getField() {
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presentation logic for the input mode.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class InputModeScreenPresenter {
|
||||||
|
private final InputModeScreenView inputModeScreenView;
|
||||||
|
private final String message;
|
||||||
|
private final List<String> histories;
|
||||||
|
private final Function<String, ScreenView> resultListener;
|
||||||
|
|
||||||
|
private StringBuilder inputString = new StringBuilder();
|
||||||
|
private int cursorPosition;
|
||||||
|
private int historyPosition = -1;
|
||||||
|
|
||||||
|
public InputModeScreenPresenter(InputModeScreenView inputModeScreenView, String message,
|
||||||
|
@Nullable List<String> histories, Function<String, ScreenView> resultListener) {
|
||||||
|
this.inputModeScreenView = Objects.requireNonNull(inputModeScreenView);
|
||||||
|
this.message = Objects.requireNonNull(message);
|
||||||
|
|
||||||
|
if (histories != null) {
|
||||||
|
this.histories = Collections.unmodifiableList(new ArrayList<>(histories));
|
||||||
|
} else {
|
||||||
|
this.histories = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resultListener = Objects.requireNonNull(resultListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView returnToNextScreen() {
|
||||||
|
inputModeScreenView.hideCursor();
|
||||||
|
String result = inputString.toString();
|
||||||
|
|
||||||
|
return resultListener.apply(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void character(Character character) {
|
||||||
|
inputString.insert(cursorPosition, character);
|
||||||
|
cursorPosition += 1;
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void backspace() {
|
||||||
|
if (cursorPosition == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputString.deleteCharAt(cursorPosition - 1);
|
||||||
|
cursorPosition -= 1;
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
if (inputString.length() == 0 || cursorPosition > inputString.length() - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputString.deleteCharAt(cursorPosition);
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowLeft() {
|
||||||
|
if (cursorPosition == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorPosition -= 1;
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowRight() {
|
||||||
|
if (cursorPosition > inputString.length() - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorPosition += 1;
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void home() {
|
||||||
|
cursorPosition = 0;
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end() {
|
||||||
|
cursorPosition = inputString.length();
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowUp() {
|
||||||
|
if (historyPosition == 0 || histories.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (historyPosition == -1) {
|
||||||
|
historyPosition = histories.size() - 1;
|
||||||
|
} else {
|
||||||
|
historyPosition -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputString = new StringBuilder(histories.get(historyPosition));
|
||||||
|
|
||||||
|
cursorPosition = inputString.length();
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowDown() {
|
||||||
|
if (historyPosition == -1 || histories.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (historyPosition == histories.size() - 1) {
|
||||||
|
historyPosition = -1;
|
||||||
|
inputString = new StringBuilder();
|
||||||
|
} else {
|
||||||
|
historyPosition += 1;
|
||||||
|
inputString = new StringBuilder(histories.get(historyPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorPosition = inputString.length();
|
||||||
|
inputModeScreenView.showInput(message, inputString.toString(), cursorPosition);
|
||||||
|
inputModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The input mode in the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class InputModeScreenView extends AbstractScreenView {
|
||||||
|
|
||||||
|
private final int row;
|
||||||
|
private final InputModeScreenPresenter inputModeScreenPresenter;
|
||||||
|
|
||||||
|
public InputModeScreenView(Screen screen, Terminal terminal, int row, String message,
|
||||||
|
List<String> histories, Function<String, ScreenView> resultListener) {
|
||||||
|
super(screen, terminal);
|
||||||
|
this.row = row;
|
||||||
|
this.inputModeScreenPresenter = new InputModeScreenPresenter(this, message, histories,
|
||||||
|
resultListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
inputModeScreenPresenter.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleKeyPress(KeyPress keyPress) {
|
||||||
|
|
||||||
|
switch (keyPress.getType()) {
|
||||||
|
case Enter:
|
||||||
|
return inputModeScreenPresenter.returnToNextScreen();
|
||||||
|
|
||||||
|
case Character:
|
||||||
|
inputModeScreenPresenter.character(keyPress.getCharacter());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Backspace:
|
||||||
|
inputModeScreenPresenter.backspace();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Delete:
|
||||||
|
inputModeScreenPresenter.delete();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArrowLeft:
|
||||||
|
inputModeScreenPresenter.arrowLeft();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArrowRight:
|
||||||
|
inputModeScreenPresenter.arrowRight();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Home:
|
||||||
|
inputModeScreenPresenter.home();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case End:
|
||||||
|
inputModeScreenPresenter.end();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArrowUp:
|
||||||
|
inputModeScreenPresenter.arrowUp();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArrowDown:
|
||||||
|
inputModeScreenPresenter.arrowDown();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showInput(String message, String inputString, int cursorPosition) {
|
||||||
|
getTerminalPrinter(row).startBold().print(message).stopBold().print(" ").print(inputString)
|
||||||
|
.endOfLine();
|
||||||
|
setCursorPosition(message.length() + 1 + cursorPosition, row);
|
||||||
|
refreshTerminal();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presentation logic for the message mode.
|
||||||
|
*
|
||||||
|
* Exit after 2 seconds or if any key is pressed.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class MessageModeScreenPresenter {
|
||||||
|
|
||||||
|
private final MessageModeScreenView messageModeScreenView;
|
||||||
|
private final String message;
|
||||||
|
private final ScreenView nextScreenView;
|
||||||
|
|
||||||
|
public MessageModeScreenPresenter(MessageModeScreenView messageModeScreenView, String message,
|
||||||
|
ScreenView nextScreenView) {
|
||||||
|
this.messageModeScreenView = Objects.requireNonNull(messageModeScreenView);
|
||||||
|
this.message = Objects.requireNonNull(message);
|
||||||
|
this.nextScreenView = Objects.requireNonNull(nextScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
messageModeScreenView.showMessage(message);
|
||||||
|
messageModeScreenView.refreshTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView returnToNextScreen() {
|
||||||
|
return nextScreenView;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The message mode in the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class MessageModeScreenView extends AbstractScreenView {
|
||||||
|
|
||||||
|
private final int row;
|
||||||
|
private final MessageModeScreenPresenter messageModeScreenPresenter;
|
||||||
|
|
||||||
|
public MessageModeScreenView(Screen screen, Terminal terminal, int row, String message,
|
||||||
|
ScreenView nextScreenView) {
|
||||||
|
super(screen, terminal);
|
||||||
|
this.row = row;
|
||||||
|
this.messageModeScreenPresenter =
|
||||||
|
new MessageModeScreenPresenter(this, message, nextScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
messageModeScreenPresenter.init();
|
||||||
|
setTimer(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleTimer() {
|
||||||
|
return messageModeScreenPresenter.returnToNextScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleKeyPress(KeyPress keyPress) {
|
||||||
|
cancelTimer();
|
||||||
|
return messageModeScreenPresenter.returnToNextScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showMessage(String message) {
|
||||||
|
getTerminalPrinter(row).startHighlight().print(" ").print(message).print(" ").stopHighlight()
|
||||||
|
.endOfLine();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for paging for the metrics.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class Paging {
|
||||||
|
private int currentPosition;
|
||||||
|
private int pageStartPosition;
|
||||||
|
private int pageEndPosition;
|
||||||
|
|
||||||
|
private int pageSize;
|
||||||
|
private int recordsSize;
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
currentPosition = 0;
|
||||||
|
pageStartPosition = 0;
|
||||||
|
pageEndPosition = Math.min(pageSize, recordsSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePageSize(int pageSize) {
|
||||||
|
this.pageSize = pageSize;
|
||||||
|
|
||||||
|
if (pageSize == 0) {
|
||||||
|
pageStartPosition = 0;
|
||||||
|
pageEndPosition = 0;
|
||||||
|
} else {
|
||||||
|
pageEndPosition = pageStartPosition + pageSize;
|
||||||
|
keepConsistent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateRecordsSize(int recordsSize) {
|
||||||
|
if (this.recordsSize == 0) {
|
||||||
|
currentPosition = 0;
|
||||||
|
pageStartPosition = 0;
|
||||||
|
pageEndPosition = Math.min(pageSize, recordsSize);
|
||||||
|
this.recordsSize = recordsSize;
|
||||||
|
} else if (recordsSize == 0) {
|
||||||
|
currentPosition = 0;
|
||||||
|
pageStartPosition = 0;
|
||||||
|
pageEndPosition = 0;
|
||||||
|
this.recordsSize = recordsSize;
|
||||||
|
} else {
|
||||||
|
this.recordsSize = recordsSize;
|
||||||
|
if (pageSize > 0) {
|
||||||
|
pageEndPosition = pageStartPosition + pageSize;
|
||||||
|
keepConsistent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowUp() {
|
||||||
|
if (currentPosition > 0) {
|
||||||
|
currentPosition -= 1;
|
||||||
|
if (pageSize > 0) {
|
||||||
|
keepConsistent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowDown() {
|
||||||
|
if (currentPosition < recordsSize - 1) {
|
||||||
|
currentPosition += 1;
|
||||||
|
if (pageSize > 0) {
|
||||||
|
keepConsistent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pageUp() {
|
||||||
|
if (pageSize > 0 && currentPosition > 0) {
|
||||||
|
currentPosition -= pageSize;
|
||||||
|
if (currentPosition < 0) {
|
||||||
|
currentPosition = 0;
|
||||||
|
}
|
||||||
|
keepConsistent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pageDown() {
|
||||||
|
if (pageSize > 0 && currentPosition < recordsSize - 1) {
|
||||||
|
|
||||||
|
currentPosition = currentPosition + pageSize;
|
||||||
|
if (currentPosition >= recordsSize) {
|
||||||
|
currentPosition = recordsSize - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageStartPosition = currentPosition;
|
||||||
|
pageEndPosition = pageStartPosition + pageSize;
|
||||||
|
keepConsistent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void keepConsistent() {
|
||||||
|
if (currentPosition < pageStartPosition) {
|
||||||
|
pageStartPosition = currentPosition;
|
||||||
|
pageEndPosition = pageStartPosition + pageSize;
|
||||||
|
} else if (currentPosition > recordsSize - 1) {
|
||||||
|
currentPosition = recordsSize - 1;
|
||||||
|
pageEndPosition = recordsSize;
|
||||||
|
pageStartPosition = pageEndPosition - pageSize;
|
||||||
|
} else if (currentPosition > pageEndPosition - 1) {
|
||||||
|
pageEndPosition = currentPosition + 1;
|
||||||
|
pageStartPosition = pageEndPosition - pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageStartPosition < 0) {
|
||||||
|
pageStartPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageEndPosition > recordsSize) {
|
||||||
|
pageEndPosition = recordsSize;
|
||||||
|
pageStartPosition = pageEndPosition - pageSize;
|
||||||
|
if (pageStartPosition < 0) {
|
||||||
|
pageStartPosition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentPosition() {
|
||||||
|
return currentPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPageStartPosition() {
|
||||||
|
return pageStartPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPageEndPosition() {
|
||||||
|
return pageEndPosition;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the summary of the metrics.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class Summary {
|
||||||
|
private final String currentTime;
|
||||||
|
private final String version;
|
||||||
|
private final String clusterId;
|
||||||
|
private final int servers;
|
||||||
|
private final int liveServers;
|
||||||
|
private final int deadServers;
|
||||||
|
private final int regionCount;
|
||||||
|
private final int ritCount;
|
||||||
|
private final double averageLoad;
|
||||||
|
private final long aggregateRequestPerSecond;
|
||||||
|
|
||||||
|
public Summary(String currentTime, String version, String clusterId, int servers,
|
||||||
|
int liveServers, int deadServers, int regionCount, int ritCount, double averageLoad,
|
||||||
|
long aggregateRequestPerSecond) {
|
||||||
|
this.currentTime = Objects.requireNonNull(currentTime);
|
||||||
|
this.version = Objects.requireNonNull(version);
|
||||||
|
this.clusterId = Objects.requireNonNull(clusterId);
|
||||||
|
this.servers = servers;
|
||||||
|
this.liveServers = liveServers;
|
||||||
|
this.deadServers = deadServers;
|
||||||
|
this.regionCount = regionCount;
|
||||||
|
this.ritCount = ritCount;
|
||||||
|
this.averageLoad = averageLoad;
|
||||||
|
this.aggregateRequestPerSecond = aggregateRequestPerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentTime() {
|
||||||
|
return currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClusterId() {
|
||||||
|
return clusterId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getServers() {
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLiveServers() {
|
||||||
|
return liveServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeadServers() {
|
||||||
|
return deadServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRegionCount() {
|
||||||
|
return regionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRitCount() {
|
||||||
|
return ritCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAverageLoad() {
|
||||||
|
return averageLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAggregateRequestPerSecond() {
|
||||||
|
return aggregateRequestPerSecond;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetrics;
|
||||||
|
import org.apache.hadoop.hbase.client.Admin;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldValue;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.DrillDownInfo;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data and business logic for the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class TopScreenModel {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(TopScreenModel.class);
|
||||||
|
|
||||||
|
private final Admin admin;
|
||||||
|
|
||||||
|
private Mode currentMode;
|
||||||
|
private Field currentSortField;
|
||||||
|
private List<FieldInfo> fieldInfos;
|
||||||
|
private List<Field> fields;
|
||||||
|
|
||||||
|
private Summary summary;
|
||||||
|
private List<Record> records;
|
||||||
|
|
||||||
|
private final List<RecordFilter> filters = new ArrayList<>();
|
||||||
|
private final List<String> filterHistories = new ArrayList<>();
|
||||||
|
|
||||||
|
private boolean ascendingSort;
|
||||||
|
|
||||||
|
public TopScreenModel(Admin admin, Mode initialMode) {
|
||||||
|
this.admin = Objects.requireNonNull(admin);
|
||||||
|
switchMode(Objects.requireNonNull(initialMode), null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchMode(Mode nextMode, List<RecordFilter> initialFilters,
|
||||||
|
boolean keepSortFieldAndSortOrderIfPossible) {
|
||||||
|
|
||||||
|
currentMode = nextMode;
|
||||||
|
fieldInfos = Collections.unmodifiableList(new ArrayList<>(currentMode.getFieldInfos()));
|
||||||
|
fields = Collections.unmodifiableList(currentMode.getFieldInfos().stream()
|
||||||
|
.map(FieldInfo::getField).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
if (keepSortFieldAndSortOrderIfPossible) {
|
||||||
|
boolean match = fields.stream().anyMatch(f -> f == currentSortField);
|
||||||
|
if (!match) {
|
||||||
|
currentSortField = nextMode.getDefaultSortField();
|
||||||
|
ascendingSort = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentSortField = nextMode.getDefaultSortField();
|
||||||
|
ascendingSort = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearFilters();
|
||||||
|
if (initialFilters != null) {
|
||||||
|
filters.addAll(initialFilters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortFieldAndFields(Field sortField, List<Field> fields) {
|
||||||
|
this.currentSortField = sortField;
|
||||||
|
this.fields = Collections.unmodifiableList(new ArrayList<>(fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HBTop only calls this from a single thread, and if that ever changes, this needs
|
||||||
|
* synchronization
|
||||||
|
*/
|
||||||
|
public void refreshMetricsData() {
|
||||||
|
ClusterMetrics clusterMetrics;
|
||||||
|
try {
|
||||||
|
clusterMetrics = admin.getClusterMetrics();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Unable to get cluster metrics", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshSummary(clusterMetrics);
|
||||||
|
refreshRecords(clusterMetrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshSummary(ClusterMetrics clusterMetrics) {
|
||||||
|
String currentTime = DateFormatUtils.ISO_8601_EXTENDED_TIME_FORMAT
|
||||||
|
.format(System.currentTimeMillis());
|
||||||
|
String version = clusterMetrics.getHBaseVersion();
|
||||||
|
String clusterId = clusterMetrics.getClusterId();
|
||||||
|
int liveServers = clusterMetrics.getLiveServerMetrics().size();
|
||||||
|
int deadServers = clusterMetrics.getDeadServerNames().size();
|
||||||
|
int regionCount = clusterMetrics.getRegionCount();
|
||||||
|
int ritCount = clusterMetrics.getRegionStatesInTransition().size();
|
||||||
|
double averageLoad = clusterMetrics.getAverageLoad();
|
||||||
|
long aggregateRequestPerSecond = clusterMetrics.getLiveServerMetrics().entrySet().stream()
|
||||||
|
.mapToLong(e -> e.getValue().getRequestCountPerSecond()).sum();
|
||||||
|
|
||||||
|
summary = new Summary(currentTime, version, clusterId, liveServers + deadServers,
|
||||||
|
liveServers, deadServers, regionCount, ritCount, averageLoad, aggregateRequestPerSecond);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshRecords(ClusterMetrics clusterMetrics) {
|
||||||
|
List<Record> records = currentMode.getRecords(clusterMetrics);
|
||||||
|
|
||||||
|
// Filter and sort
|
||||||
|
records = records.stream()
|
||||||
|
.filter(r -> filters.stream().allMatch(f -> f.execute(r)))
|
||||||
|
.sorted((recordLeft, recordRight) -> {
|
||||||
|
FieldValue left = recordLeft.get(currentSortField);
|
||||||
|
FieldValue right = recordRight.get(currentSortField);
|
||||||
|
return (ascendingSort ? 1 : -1) * left.compareTo(right);
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
this.records = Collections.unmodifiableList(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchSortOrder() {
|
||||||
|
ascendingSort = !ascendingSort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addFilter(String filterString, boolean ignoreCase) {
|
||||||
|
RecordFilter filter = RecordFilter.parse(filterString, fields, ignoreCase);
|
||||||
|
if (filter == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.add(filter);
|
||||||
|
filterHistories.add(filterString);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearFilters() {
|
||||||
|
filters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean drillDown(Record selectedRecord) {
|
||||||
|
DrillDownInfo drillDownInfo = currentMode.drillDown(selectedRecord);
|
||||||
|
if (drillDownInfo == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switchMode(drillDownInfo.getNextMode(), drillDownInfo.getInitialFilters(), true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mode getCurrentMode() {
|
||||||
|
return currentMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field getCurrentSortField() {
|
||||||
|
return currentSortField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FieldInfo> getFieldInfos() {
|
||||||
|
return fieldInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Field> getFields() {
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Summary getSummary() {
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Record> getRecords() {
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RecordFilter> getFilters() {
|
||||||
|
return Collections.unmodifiableList(filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getFilterHistories() {
|
||||||
|
return Collections.unmodifiableList(filterHistories);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,330 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.field.FieldScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.help.HelpScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.mode.ModeScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The presentation logic for the top screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class TopScreenPresenter {
|
||||||
|
private final TopScreenView topScreenView;
|
||||||
|
private final AtomicLong refreshDelay;
|
||||||
|
private long lastRefreshTimestamp;
|
||||||
|
|
||||||
|
private final AtomicBoolean adjustFieldLength = new AtomicBoolean(true);
|
||||||
|
private final TopScreenModel topScreenModel;
|
||||||
|
private int terminalLength;
|
||||||
|
private int horizontalScroll;
|
||||||
|
private final Paging paging = new Paging();
|
||||||
|
|
||||||
|
private final EnumMap<Field, Boolean> fieldDisplayMap = new EnumMap<>(Field.class);
|
||||||
|
private final EnumMap<Field, Integer> fieldLengthMap = new EnumMap<>(Field.class);
|
||||||
|
|
||||||
|
public TopScreenPresenter(TopScreenView topScreenView, long initialRefreshDelay,
|
||||||
|
TopScreenModel topScreenModel) {
|
||||||
|
this.topScreenView = Objects.requireNonNull(topScreenView);
|
||||||
|
this.refreshDelay = new AtomicLong(initialRefreshDelay);
|
||||||
|
this.topScreenModel = Objects.requireNonNull(topScreenModel);
|
||||||
|
|
||||||
|
initFieldDisplayMapAndFieldLengthMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
terminalLength = topScreenView.getTerminalSize().getColumns();
|
||||||
|
paging.updatePageSize(topScreenView.getPageSize());
|
||||||
|
topScreenView.hideCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long refresh(boolean force) {
|
||||||
|
if (!force) {
|
||||||
|
long delay = System.currentTimeMillis() - lastRefreshTimestamp;
|
||||||
|
if (delay < refreshDelay.get()) {
|
||||||
|
return refreshDelay.get() - delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalSize newTerminalSize = topScreenView.doResizeIfNecessary();
|
||||||
|
if (newTerminalSize != null) {
|
||||||
|
terminalLength = newTerminalSize.getColumns();
|
||||||
|
paging.updatePageSize(topScreenView.getPageSize());
|
||||||
|
topScreenView.clearTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
paging.updateRecordsSize(topScreenModel.getRecords().size());
|
||||||
|
|
||||||
|
adjustFieldLengthIfNeeded();
|
||||||
|
|
||||||
|
topScreenView.showTopScreen(topScreenModel.getSummary(), getDisplayedHeaders(),
|
||||||
|
getDisplayedRecords(), getSelectedRecord());
|
||||||
|
|
||||||
|
topScreenView.refreshTerminal();
|
||||||
|
|
||||||
|
lastRefreshTimestamp = System.currentTimeMillis();
|
||||||
|
return refreshDelay.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void adjustFieldLength() {
|
||||||
|
adjustFieldLength.set(true);
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustFieldLengthIfNeeded() {
|
||||||
|
if (adjustFieldLength.get()) {
|
||||||
|
adjustFieldLength.set(false);
|
||||||
|
|
||||||
|
for (Field f : topScreenModel.getFields()) {
|
||||||
|
if (f.isAutoAdjust()) {
|
||||||
|
int maxLength = topScreenModel.getRecords().stream()
|
||||||
|
.map(r -> r.get(f).asString().length())
|
||||||
|
.max(Integer::compareTo).orElse(0);
|
||||||
|
fieldLengthMap.put(f, Math.max(maxLength, f.getHeader().length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Header> getDisplayedHeaders() {
|
||||||
|
List<Field> displayFields =
|
||||||
|
topScreenModel.getFields().stream()
|
||||||
|
.filter(fieldDisplayMap::get).collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (displayFields.isEmpty()) {
|
||||||
|
horizontalScroll = 0;
|
||||||
|
} else if (horizontalScroll > displayFields.size() - 1) {
|
||||||
|
horizontalScroll = displayFields.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Header> ret = new ArrayList<>();
|
||||||
|
|
||||||
|
int length = 0;
|
||||||
|
for (int i = horizontalScroll; i < displayFields.size(); i++) {
|
||||||
|
Field field = displayFields.get(i);
|
||||||
|
int fieldLength = fieldLengthMap.get(field);
|
||||||
|
|
||||||
|
length += fieldLength + 1;
|
||||||
|
if (length > terminalLength) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret.add(new Header(field, fieldLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Record> getDisplayedRecords() {
|
||||||
|
List<Record> ret = new ArrayList<>();
|
||||||
|
for (int i = paging.getPageStartPosition(); i < paging.getPageEndPosition(); i++) {
|
||||||
|
ret.add(topScreenModel.getRecords().get(i));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Record getSelectedRecord() {
|
||||||
|
if (topScreenModel.getRecords().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return topScreenModel.getRecords().get(paging.getCurrentPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowUp() {
|
||||||
|
paging.arrowUp();
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowDown() {
|
||||||
|
paging.arrowDown();
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pageUp() {
|
||||||
|
paging.pageUp();
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pageDown() {
|
||||||
|
paging.pageDown();
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowLeft() {
|
||||||
|
if (horizontalScroll > 0) {
|
||||||
|
horizontalScroll -= 1;
|
||||||
|
}
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrowRight() {
|
||||||
|
if (horizontalScroll < getHeaderSize() - 1) {
|
||||||
|
horizontalScroll += 1;
|
||||||
|
}
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void home() {
|
||||||
|
if (horizontalScroll > 0) {
|
||||||
|
horizontalScroll = 0;
|
||||||
|
}
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end() {
|
||||||
|
int headerSize = getHeaderSize();
|
||||||
|
horizontalScroll = headerSize == 0 ? 0 : headerSize - 1;
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getHeaderSize() {
|
||||||
|
return (int) topScreenModel.getFields().stream()
|
||||||
|
.filter(fieldDisplayMap::get).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchSortOrder() {
|
||||||
|
topScreenModel.switchSortOrder();
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView transitionToHelpScreen(Screen screen, Terminal terminal) {
|
||||||
|
return new HelpScreenView(screen, terminal, refreshDelay.get(), topScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView transitionToModeScreen(Screen screen, Terminal terminal) {
|
||||||
|
return new ModeScreenView(screen, terminal, topScreenModel.getCurrentMode(), this::switchMode,
|
||||||
|
topScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView transitionToFieldScreen(Screen screen, Terminal terminal) {
|
||||||
|
return new FieldScreenView(screen, terminal,
|
||||||
|
topScreenModel.getCurrentSortField(), topScreenModel.getFields(),
|
||||||
|
fieldDisplayMap,
|
||||||
|
(sortKey, fields, fieldDisplayMap) -> {
|
||||||
|
topScreenModel.setSortFieldAndFields(sortKey, fields);
|
||||||
|
this.fieldDisplayMap.clear();
|
||||||
|
this.fieldDisplayMap.putAll(fieldDisplayMap);
|
||||||
|
}, topScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchMode(Mode nextMode) {
|
||||||
|
topScreenModel.switchMode(nextMode, null, false);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drillDown() {
|
||||||
|
Record selectedRecord = getSelectedRecord();
|
||||||
|
if (selectedRecord == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (topScreenModel.drillDown(selectedRecord)) {
|
||||||
|
reset();
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
initFieldDisplayMapAndFieldLengthMap();
|
||||||
|
adjustFieldLength.set(true);
|
||||||
|
paging.init();
|
||||||
|
horizontalScroll = 0;
|
||||||
|
topScreenView.clearTerminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initFieldDisplayMapAndFieldLengthMap() {
|
||||||
|
fieldDisplayMap.clear();
|
||||||
|
fieldLengthMap.clear();
|
||||||
|
for (FieldInfo fieldInfo : topScreenModel.getFieldInfos()) {
|
||||||
|
fieldDisplayMap.put(fieldInfo.getField(), fieldInfo.isDisplayByDefault());
|
||||||
|
fieldLengthMap.put(fieldInfo.getField(), fieldInfo.getDefaultLength());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView goToMessageMode(Screen screen, Terminal terminal, int row, String message) {
|
||||||
|
return new MessageModeScreenView(screen, terminal, row, message, topScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView goToInputModeForRefreshDelay(Screen screen, Terminal terminal, int row) {
|
||||||
|
return new InputModeScreenView(screen, terminal, row,
|
||||||
|
"Change refresh delay from " + (double) refreshDelay.get() / 1000 + " to", null,
|
||||||
|
(inputString) -> {
|
||||||
|
if (inputString.isEmpty()) {
|
||||||
|
return topScreenView;
|
||||||
|
}
|
||||||
|
|
||||||
|
double delay;
|
||||||
|
try {
|
||||||
|
delay = Double.valueOf(inputString);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return goToMessageMode(screen, terminal, row, "Unacceptable floating point");
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshDelay.set((long) (delay * 1000));
|
||||||
|
return topScreenView;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView goToInputModeForFilter(Screen screen, Terminal terminal, int row,
|
||||||
|
boolean ignoreCase) {
|
||||||
|
return new InputModeScreenView(screen, terminal, row,
|
||||||
|
"add filter #" + (topScreenModel.getFilters().size() + 1) +
|
||||||
|
" (" + (ignoreCase ? "ignoring case" : "case sensitive") + ") as: [!]FLD?VAL",
|
||||||
|
topScreenModel.getFilterHistories(),
|
||||||
|
(inputString) -> {
|
||||||
|
if (inputString.isEmpty()) {
|
||||||
|
return topScreenView;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!topScreenModel.addFilter(inputString, ignoreCase)) {
|
||||||
|
return goToMessageMode(screen, terminal, row, "Unacceptable filter expression");
|
||||||
|
}
|
||||||
|
|
||||||
|
paging.init();
|
||||||
|
return topScreenView;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearFilters() {
|
||||||
|
topScreenModel.clearFilters();
|
||||||
|
paging.init();
|
||||||
|
refresh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenView goToFilterDisplayMode(Screen screen, Terminal terminal, int row) {
|
||||||
|
return new FilterDisplayModeScreenView(screen, terminal, row, topScreenModel.getFilters(),
|
||||||
|
topScreenView);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,300 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.hadoop.hbase.client.Admin;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.AbstractScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.Screen;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The screen that provides a dynamic real-time view for the HBase metrics.
|
||||||
|
*
|
||||||
|
* This shows the metric {@link Summary} and the metric {@link Record}s. The summary and the
|
||||||
|
* metrics are updated periodically (3 seconds by default).
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class TopScreenView extends AbstractScreenView {
|
||||||
|
|
||||||
|
private static final int SUMMARY_START_ROW = 0;
|
||||||
|
private static final int SUMMARY_ROW_NUM = 7;
|
||||||
|
private static final int MESSAGE_ROW = 7;
|
||||||
|
private static final int RECORD_HEADER_ROW = 8;
|
||||||
|
private static final int RECORD_START_ROW = 9;
|
||||||
|
|
||||||
|
private final TopScreenPresenter topScreenPresenter;
|
||||||
|
private int pageSize;
|
||||||
|
|
||||||
|
public TopScreenView(Screen screen, Terminal terminal, long initialRefreshDelay, Admin admin,
|
||||||
|
Mode initialMode) {
|
||||||
|
super(screen, terminal);
|
||||||
|
this.topScreenPresenter = new TopScreenPresenter(this, initialRefreshDelay,
|
||||||
|
new TopScreenModel(admin, initialMode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
topScreenPresenter.init();
|
||||||
|
long delay = topScreenPresenter.refresh(true);
|
||||||
|
setTimer(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScreenView handleTimer() {
|
||||||
|
long delay = topScreenPresenter.refresh(false);
|
||||||
|
setTimer(delay);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ScreenView handleKeyPress(KeyPress keyPress) {
|
||||||
|
switch (keyPress.getType()) {
|
||||||
|
case Enter:
|
||||||
|
topScreenPresenter.refresh(true);
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case ArrowUp:
|
||||||
|
topScreenPresenter.arrowUp();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case ArrowDown:
|
||||||
|
topScreenPresenter.arrowDown();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case ArrowLeft:
|
||||||
|
topScreenPresenter.arrowLeft();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case ArrowRight:
|
||||||
|
topScreenPresenter.arrowRight();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case PageUp:
|
||||||
|
topScreenPresenter.pageUp();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case PageDown:
|
||||||
|
topScreenPresenter.pageDown();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case Home:
|
||||||
|
topScreenPresenter.home();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case End:
|
||||||
|
topScreenPresenter.end();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
case Escape:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyPress.getType() != KeyPress.Type.Character) {
|
||||||
|
return unknownCommandMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert keyPress.getCharacter() != null;
|
||||||
|
switch (keyPress.getCharacter()) {
|
||||||
|
case 'R':
|
||||||
|
topScreenPresenter.switchSortOrder();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
cancelTimer();
|
||||||
|
return topScreenPresenter.transitionToFieldScreen(getScreen(), getTerminal());
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
cancelTimer();
|
||||||
|
return topScreenPresenter.transitionToModeScreen(getScreen(), getTerminal());
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
cancelTimer();
|
||||||
|
return topScreenPresenter.transitionToHelpScreen(getScreen(), getTerminal());
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
cancelTimer();
|
||||||
|
return topScreenPresenter.goToInputModeForRefreshDelay(getScreen(), getTerminal(),
|
||||||
|
MESSAGE_ROW);
|
||||||
|
|
||||||
|
case 'o':
|
||||||
|
cancelTimer();
|
||||||
|
if (keyPress.isCtrl()) {
|
||||||
|
return topScreenPresenter.goToFilterDisplayMode(getScreen(), getTerminal(), MESSAGE_ROW);
|
||||||
|
}
|
||||||
|
return topScreenPresenter.goToInputModeForFilter(getScreen(), getTerminal(), MESSAGE_ROW,
|
||||||
|
true);
|
||||||
|
|
||||||
|
case 'O':
|
||||||
|
cancelTimer();
|
||||||
|
return topScreenPresenter.goToInputModeForFilter(getScreen(), getTerminal(), MESSAGE_ROW,
|
||||||
|
false);
|
||||||
|
|
||||||
|
case '=':
|
||||||
|
topScreenPresenter.clearFilters();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'X':
|
||||||
|
topScreenPresenter.adjustFieldLength();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
topScreenPresenter.drillDown();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
return null;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return unknownCommandMessage();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerminalSize getTerminalSize() {
|
||||||
|
TerminalSize terminalSize = super.getTerminalSize();
|
||||||
|
updatePageSize(terminalSize);
|
||||||
|
return terminalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerminalSize doResizeIfNecessary() {
|
||||||
|
TerminalSize terminalSize = super.doResizeIfNecessary();
|
||||||
|
if (terminalSize == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
updatePageSize(terminalSize);
|
||||||
|
return terminalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePageSize(TerminalSize terminalSize) {
|
||||||
|
pageSize = terminalSize.getRows() - SUMMARY_ROW_NUM - 2;
|
||||||
|
if (pageSize < 0) {
|
||||||
|
pageSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPageSize() {
|
||||||
|
return pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showTopScreen(Summary summary, List<Header> headers, List<Record> records,
|
||||||
|
Record selectedRecord) {
|
||||||
|
showSummary(summary);
|
||||||
|
clearMessage();
|
||||||
|
showHeaders(headers);
|
||||||
|
showRecords(headers, records, selectedRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showSummary(Summary summary) {
|
||||||
|
TerminalPrinter printer = getTerminalPrinter(SUMMARY_START_ROW);
|
||||||
|
printer.print(String.format("HBase hbtop - %s", summary.getCurrentTime())).endOfLine();
|
||||||
|
printer.print(String.format("Version: %s", summary.getVersion())).endOfLine();
|
||||||
|
printer.print(String.format("Cluster ID: %s", summary.getClusterId())).endOfLine();
|
||||||
|
printer.print("RegionServer(s): ")
|
||||||
|
.startBold().print(Integer.toString(summary.getServers())).stopBold()
|
||||||
|
.print(" total, ")
|
||||||
|
.startBold().print(Integer.toString(summary.getLiveServers())).stopBold()
|
||||||
|
.print(" live, ")
|
||||||
|
.startBold().print(Integer.toString(summary.getDeadServers())).stopBold()
|
||||||
|
.print(" dead").endOfLine();
|
||||||
|
printer.print("RegionCount: ")
|
||||||
|
.startBold().print(Integer.toString(summary.getRegionCount())).stopBold()
|
||||||
|
.print(" total, ")
|
||||||
|
.startBold().print(Integer.toString(summary.getRitCount())).stopBold()
|
||||||
|
.print(" rit").endOfLine();
|
||||||
|
printer.print("Average Cluster Load: ")
|
||||||
|
.startBold().print(String.format("%.2f", summary.getAverageLoad())).stopBold().endOfLine();
|
||||||
|
printer.print("Aggregate Request/s: ")
|
||||||
|
.startBold().print(Long.toString(summary.getAggregateRequestPerSecond())).stopBold()
|
||||||
|
.endOfLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showRecords(List<Header> headers, List<Record> records, Record selectedRecord) {
|
||||||
|
TerminalPrinter printer = getTerminalPrinter(RECORD_START_ROW);
|
||||||
|
List<String> buf = new ArrayList<>(headers.size());
|
||||||
|
for (int i = 0; i < pageSize; i++) {
|
||||||
|
if(i < records.size()) {
|
||||||
|
Record record = records.get(i);
|
||||||
|
buf.clear();
|
||||||
|
for (Header header : headers) {
|
||||||
|
String value = "";
|
||||||
|
if (record.containsKey(header.getField())) {
|
||||||
|
value = record.get(header.getField()).asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.add(limitLineLength(String.format(header.format(), value), header.getLength()));
|
||||||
|
}
|
||||||
|
|
||||||
|
String recordString = String.join(" ", buf);
|
||||||
|
if (!recordString.isEmpty()) {
|
||||||
|
recordString += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record == selectedRecord) {
|
||||||
|
printer.startHighlight().print(recordString).stopHighlight().endOfLine();
|
||||||
|
} else {
|
||||||
|
printer.print(recordString).endOfLine();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printer.endOfLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showHeaders(List<Header> headers) {
|
||||||
|
String header = headers.stream()
|
||||||
|
.map(h -> String.format(h.format(), h.getField().getHeader()))
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
|
||||||
|
if (!header.isEmpty()) {
|
||||||
|
header += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
getTerminalPrinter(RECORD_HEADER_ROW).startHighlight().print(header).stopHighlight()
|
||||||
|
.endOfLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String limitLineLength(String line, int length) {
|
||||||
|
if (line.length() > length) {
|
||||||
|
return line.substring(0, length - 1) + "+";
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearMessage() {
|
||||||
|
getTerminalPrinter(MESSAGE_ROW).print("").endOfLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScreenView unknownCommandMessage() {
|
||||||
|
cancelTimer();
|
||||||
|
return topScreenPresenter.goToMessageMode(getScreen(), getTerminal(), MESSAGE_ROW,
|
||||||
|
"Unknown command - try 'h' for help");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes of text in the terminal.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class Attributes {
|
||||||
|
private boolean bold;
|
||||||
|
private boolean blink;
|
||||||
|
private boolean reverse;
|
||||||
|
private boolean underline;
|
||||||
|
private Color foregroundColor;
|
||||||
|
private Color backgroundColor;
|
||||||
|
|
||||||
|
public Attributes() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Attributes(Attributes attributes) {
|
||||||
|
set(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBold() {
|
||||||
|
return bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBold(boolean bold) {
|
||||||
|
this.bold = bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBlink() {
|
||||||
|
return blink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlink(boolean blink) {
|
||||||
|
this.blink = blink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReverse() {
|
||||||
|
return reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReverse(boolean reverse) {
|
||||||
|
this.reverse = reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUnderline() {
|
||||||
|
return underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnderline(boolean underline) {
|
||||||
|
this.underline = underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getForegroundColor() {
|
||||||
|
return foregroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setForegroundColor(Color foregroundColor) {
|
||||||
|
this.foregroundColor = foregroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getBackgroundColor() {
|
||||||
|
return backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackgroundColor(Color backgroundColor) {
|
||||||
|
this.backgroundColor = backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
bold = false;
|
||||||
|
blink = false;
|
||||||
|
reverse = false;
|
||||||
|
underline = false;
|
||||||
|
foregroundColor = Color.WHITE;
|
||||||
|
backgroundColor = Color.BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(Attributes attributes) {
|
||||||
|
bold = attributes.bold;
|
||||||
|
blink = attributes.blink;
|
||||||
|
reverse = attributes.reverse;
|
||||||
|
underline = attributes.underline;
|
||||||
|
foregroundColor = attributes.foregroundColor;
|
||||||
|
backgroundColor = attributes.backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof Attributes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Attributes that = (Attributes) o;
|
||||||
|
return bold == that.bold && blink == that.blink && reverse == that.reverse
|
||||||
|
&& underline == that.underline && foregroundColor == that.foregroundColor
|
||||||
|
&& backgroundColor == that.backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(bold, blink, reverse, underline, foregroundColor, backgroundColor);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminal color definitions.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public enum Color {
|
||||||
|
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 2-d position in 'terminal space'.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class CursorPosition {
|
||||||
|
private final int column;
|
||||||
|
private final int row;
|
||||||
|
|
||||||
|
public CursorPosition(int column, int row) {
|
||||||
|
this.column = column;
|
||||||
|
this.row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColumn() {
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRow() {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof CursorPosition)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CursorPosition that = (CursorPosition) o;
|
||||||
|
return column == that.column && row == that.row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(column, row);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the user pressing a key on the keyboard.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class KeyPress {
|
||||||
|
public enum Type {
|
||||||
|
Character,
|
||||||
|
Escape,
|
||||||
|
Backspace,
|
||||||
|
ArrowLeft,
|
||||||
|
ArrowRight,
|
||||||
|
ArrowUp,
|
||||||
|
ArrowDown,
|
||||||
|
Insert,
|
||||||
|
Delete,
|
||||||
|
Home,
|
||||||
|
End,
|
||||||
|
PageUp,
|
||||||
|
PageDown,
|
||||||
|
ReverseTab,
|
||||||
|
Tab,
|
||||||
|
Enter,
|
||||||
|
F1,
|
||||||
|
F2,
|
||||||
|
F3,
|
||||||
|
F4,
|
||||||
|
F5,
|
||||||
|
F6,
|
||||||
|
F7,
|
||||||
|
F8,
|
||||||
|
F9,
|
||||||
|
F10,
|
||||||
|
F11,
|
||||||
|
F12,
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Type type;
|
||||||
|
private final Character character;
|
||||||
|
private final boolean alt;
|
||||||
|
private final boolean ctrl;
|
||||||
|
private final boolean shift;
|
||||||
|
|
||||||
|
public KeyPress(Type type, @Nullable Character character, boolean alt, boolean ctrl,
|
||||||
|
boolean shift) {
|
||||||
|
this.type = Objects.requireNonNull(type);
|
||||||
|
this.character = character;
|
||||||
|
this.alt = alt;
|
||||||
|
this.ctrl = ctrl;
|
||||||
|
this.shift = shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Character getCharacter() {
|
||||||
|
return character;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAlt() {
|
||||||
|
return alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCtrl() {
|
||||||
|
return ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShift() {
|
||||||
|
return shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "KeyPress{" +
|
||||||
|
"type=" + type +
|
||||||
|
", character=" + escape(character) +
|
||||||
|
", alt=" + alt +
|
||||||
|
", ctrl=" + ctrl +
|
||||||
|
", shift=" + shift +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
private String escape(Character character) {
|
||||||
|
if (character == null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (character) {
|
||||||
|
case '\n':
|
||||||
|
return "\\n";
|
||||||
|
|
||||||
|
case '\b':
|
||||||
|
return "\\b";
|
||||||
|
|
||||||
|
case '\t':
|
||||||
|
return "\\t";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return character.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The terminal interface that is an abstraction of terminal screen.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public interface Terminal extends Closeable {
|
||||||
|
void clear();
|
||||||
|
void refresh();
|
||||||
|
TerminalSize getSize();
|
||||||
|
@Nullable TerminalSize doResizeIfNecessary();
|
||||||
|
@Nullable KeyPress pollKeyPress();
|
||||||
|
CursorPosition getCursorPosition();
|
||||||
|
void setCursorPosition(int column, int row);
|
||||||
|
void hideCursor();
|
||||||
|
TerminalPrinter getTerminalPrinter(int startRow);
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface responsible for printing to the terminal.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public interface TerminalPrinter {
|
||||||
|
TerminalPrinter print(String value);
|
||||||
|
|
||||||
|
default TerminalPrinter print(Object value) {
|
||||||
|
print(value.toString());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TerminalPrinter print(char value) {
|
||||||
|
print(Character.toString(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TerminalPrinter print(short value) {
|
||||||
|
print(Short.toString(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TerminalPrinter print(int value) {
|
||||||
|
print(Integer.toString(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TerminalPrinter print(long value) {
|
||||||
|
print(Long.toString(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TerminalPrinter print(float value) {
|
||||||
|
print(Float.toString(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TerminalPrinter print(double value) {
|
||||||
|
print(Double.toString(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
default TerminalPrinter printFormat(String format, Object... args) {
|
||||||
|
print(String.format(format, args));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalPrinter startHighlight();
|
||||||
|
|
||||||
|
TerminalPrinter stopHighlight();
|
||||||
|
|
||||||
|
TerminalPrinter startBold();
|
||||||
|
|
||||||
|
TerminalPrinter stopBold();
|
||||||
|
|
||||||
|
void endOfLine();
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminal dimensions in 2-d space, measured in number of rows and columns.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class TerminalSize {
|
||||||
|
private final int columns;
|
||||||
|
private final int rows;
|
||||||
|
|
||||||
|
public TerminalSize(int columns, int rows) {
|
||||||
|
this.columns = columns;
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColumns() {
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRows() {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof TerminalSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TerminalSize that = (TerminalSize) o;
|
||||||
|
return columns == that.columns && rows == that.rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(columns, rows);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal.impl;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Attributes;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Color;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single text cell of the terminal.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class Cell {
|
||||||
|
private static final char UNSET_VALUE = (char) 65535;
|
||||||
|
private static final char END_OF_LINE = '\0';
|
||||||
|
|
||||||
|
private final Attributes attributes;
|
||||||
|
private char ch;
|
||||||
|
|
||||||
|
public Cell() {
|
||||||
|
attributes = new Attributes();
|
||||||
|
ch = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getChar() {
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChar(char ch) {
|
||||||
|
this.ch = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
attributes.reset();
|
||||||
|
ch = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unset() {
|
||||||
|
attributes.reset();
|
||||||
|
ch = UNSET_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endOfLine() {
|
||||||
|
attributes.reset();
|
||||||
|
ch = END_OF_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEndOfLine() {
|
||||||
|
return ch == END_OF_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(Cell cell) {
|
||||||
|
attributes.set(cell.attributes);
|
||||||
|
this.ch = cell.ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Attributes getAttributes() {
|
||||||
|
return new Attributes(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributes(Attributes attributes) {
|
||||||
|
this.attributes.set(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBold() {
|
||||||
|
return attributes.isBold();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBlink() {
|
||||||
|
return attributes.isBlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReverse() {
|
||||||
|
return attributes.isReverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUnderline() {
|
||||||
|
return attributes.isUnderline();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getForegroundColor() {
|
||||||
|
return attributes.getForegroundColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getBackgroundColor() {
|
||||||
|
return attributes.getBackgroundColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof Cell)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Cell cell = (Cell) o;
|
||||||
|
return ch == cell.ch && attributes.equals(cell.attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(attributes, ch);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal.impl;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Color;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for escape sequences.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public final class EscapeSequences {
|
||||||
|
|
||||||
|
private EscapeSequences() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String clearAll() {
|
||||||
|
return "\033[0;37;40m\033[2J";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String setTitle(String title) {
|
||||||
|
return "\033]2;" + title + "\007";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String cursor(boolean on) {
|
||||||
|
if (on) {
|
||||||
|
return "\033[?25h";
|
||||||
|
}
|
||||||
|
return "\033[?25l";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String moveCursor(int column, int row) {
|
||||||
|
return String.format("\033[%d;%dH", row + 1, column + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String clearRemainingLine() {
|
||||||
|
return "\033[0;37;40m\033[K";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String color(Color foregroundColor, Color backgroundColor, boolean bold,
|
||||||
|
boolean reverse, boolean blink, boolean underline) {
|
||||||
|
|
||||||
|
int foregroundColorValue = getColorValue(foregroundColor, true);
|
||||||
|
int backgroundColorValue = getColorValue(backgroundColor, false);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (bold && reverse && blink && !underline) {
|
||||||
|
sb.append("\033[0;1;7;5;");
|
||||||
|
} else if (bold && reverse && !blink && !underline) {
|
||||||
|
sb.append("\033[0;1;7;");
|
||||||
|
} else if (!bold && reverse && blink && !underline) {
|
||||||
|
sb.append("\033[0;7;5;");
|
||||||
|
} else if (bold && !reverse && blink && !underline) {
|
||||||
|
sb.append("\033[0;1;5;");
|
||||||
|
} else if (bold && !reverse && !blink && !underline) {
|
||||||
|
sb.append("\033[0;1;");
|
||||||
|
} else if (!bold && reverse && !blink && !underline) {
|
||||||
|
sb.append("\033[0;7;");
|
||||||
|
} else if (!bold && !reverse && blink && !underline) {
|
||||||
|
sb.append("\033[0;5;");
|
||||||
|
} else if (bold && reverse && blink) {
|
||||||
|
sb.append("\033[0;1;7;5;4;");
|
||||||
|
} else if (bold && reverse) {
|
||||||
|
sb.append("\033[0;1;7;4;");
|
||||||
|
} else if (!bold && reverse && blink) {
|
||||||
|
sb.append("\033[0;7;5;4;");
|
||||||
|
} else if (bold && blink) {
|
||||||
|
sb.append("\033[0;1;5;4;");
|
||||||
|
} else if (bold) {
|
||||||
|
sb.append("\033[0;1;4;");
|
||||||
|
} else if (reverse) {
|
||||||
|
sb.append("\033[0;7;4;");
|
||||||
|
} else if (blink) {
|
||||||
|
sb.append("\033[0;5;4;");
|
||||||
|
} else if (underline) {
|
||||||
|
sb.append("\033[0;4;");
|
||||||
|
} else {
|
||||||
|
sb.append("\033[0;");
|
||||||
|
}
|
||||||
|
sb.append(String.format("%d;%dm", foregroundColorValue, backgroundColorValue));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getColorValue(Color color, boolean foreground) {
|
||||||
|
int baseValue;
|
||||||
|
if (foreground) {
|
||||||
|
baseValue = 30;
|
||||||
|
} else { // background
|
||||||
|
baseValue = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (color) {
|
||||||
|
case BLACK:
|
||||||
|
return baseValue;
|
||||||
|
|
||||||
|
case RED:
|
||||||
|
return baseValue + 1;
|
||||||
|
|
||||||
|
case GREEN:
|
||||||
|
return baseValue + 2;
|
||||||
|
|
||||||
|
case YELLOW:
|
||||||
|
return baseValue + 3;
|
||||||
|
|
||||||
|
case BLUE:
|
||||||
|
return baseValue + 4;
|
||||||
|
|
||||||
|
case MAGENTA:
|
||||||
|
return baseValue + 5;
|
||||||
|
|
||||||
|
case CYAN:
|
||||||
|
return baseValue + 6;
|
||||||
|
|
||||||
|
case WHITE:
|
||||||
|
return baseValue + 7;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String normal() {
|
||||||
|
return "\033[0;37;40m";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,488 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.util.Threads;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.hbase.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This generates {@link KeyPress} objects from the given input stream and offers them to the
|
||||||
|
* given queue.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class KeyPressGenerator {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(KeyPressGenerator.class);
|
||||||
|
|
||||||
|
private enum ParseState {
|
||||||
|
START, ESCAPE, ESCAPE_SEQUENCE_PARAM1, ESCAPE_SEQUENCE_PARAM2
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Queue<KeyPress> keyPressQueue;
|
||||||
|
private final BlockingQueue<Character> inputCharacterQueue = new LinkedBlockingQueue<>();
|
||||||
|
private final Reader input;
|
||||||
|
private final InputStream inputStream;
|
||||||
|
private final AtomicBoolean stopThreads = new AtomicBoolean();
|
||||||
|
private final ExecutorService executorService;
|
||||||
|
|
||||||
|
private ParseState parseState;
|
||||||
|
private int param1;
|
||||||
|
private int param2;
|
||||||
|
|
||||||
|
public KeyPressGenerator(InputStream inputStream, Queue<KeyPress> keyPressQueue) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
input = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||||
|
this.keyPressQueue = keyPressQueue;
|
||||||
|
|
||||||
|
executorService = Executors.newFixedThreadPool(2, new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("KeyPressGenerator-%d").setDaemon(true)
|
||||||
|
.setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER).build());
|
||||||
|
|
||||||
|
initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
executorService.submit(this::readerThread);
|
||||||
|
executorService.submit(this::generatorThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initState() {
|
||||||
|
parseState = ParseState.START;
|
||||||
|
param1 = 0;
|
||||||
|
param2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readerThread() {
|
||||||
|
boolean done = false;
|
||||||
|
char[] readBuffer = new char[128];
|
||||||
|
|
||||||
|
while (!done && !stopThreads.get()) {
|
||||||
|
try {
|
||||||
|
int n = inputStream.available();
|
||||||
|
if (n > 0) {
|
||||||
|
if (readBuffer.length < n) {
|
||||||
|
readBuffer = new char[readBuffer.length * 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = input.read(readBuffer, 0, readBuffer.length);
|
||||||
|
if (rc == -1) {
|
||||||
|
// EOF
|
||||||
|
done = true;
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < rc; i++) {
|
||||||
|
int ch = readBuffer[i];
|
||||||
|
inputCharacterQueue.offer((char) ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Thread.sleep(20);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("Caught an exception", e);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generatorThread() {
|
||||||
|
while (!stopThreads.get()) {
|
||||||
|
Character ch;
|
||||||
|
try {
|
||||||
|
ch = inputCharacterQueue.poll(100, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == null) {
|
||||||
|
if (parseState == ParseState.ESCAPE) {
|
||||||
|
offer(new KeyPress(KeyPress.Type.Escape, null, false, false, false));
|
||||||
|
initState();
|
||||||
|
} else if (parseState != ParseState.START) {
|
||||||
|
offer(new KeyPress(KeyPress.Type.Unknown, null, false, false, false));
|
||||||
|
initState();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseState == ParseState.START) {
|
||||||
|
if (ch == 0x1B) {
|
||||||
|
parseState = ParseState.ESCAPE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
offer(new KeyPress(KeyPress.Type.Enter, '\n', false, false, false));
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case 0x08:
|
||||||
|
case 0x7F:
|
||||||
|
offer(new KeyPress(KeyPress.Type.Backspace, '\b', false, false, false));
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case '\t':
|
||||||
|
offer(new KeyPress(KeyPress.Type.Tab, '\t', false, false, false));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch < 32) {
|
||||||
|
ctrlAndCharacter(ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPrintableChar(ch)) {
|
||||||
|
// Normal character
|
||||||
|
offer(new KeyPress(KeyPress.Type.Character, ch, false, false, false));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offer(new KeyPress(KeyPress.Type.Unknown, null, false, false, false));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseState == ParseState.ESCAPE) {
|
||||||
|
if (ch == 0x1B) {
|
||||||
|
offer(new KeyPress(KeyPress.Type.Escape, null, false, false, false));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch < 32 && ch != 0x08) {
|
||||||
|
ctrlAltAndCharacter(ch);
|
||||||
|
initState();
|
||||||
|
continue;
|
||||||
|
} else if (ch == 0x7F || ch == 0x08) {
|
||||||
|
offer(new KeyPress(KeyPress.Type.Backspace, '\b', false, false, false));
|
||||||
|
initState();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '[' || ch == 'O') {
|
||||||
|
parseState = ParseState.ESCAPE_SEQUENCE_PARAM1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPrintableChar(ch)) {
|
||||||
|
// Alt and character
|
||||||
|
offer(new KeyPress(KeyPress.Type.Character, ch, true, false, false));
|
||||||
|
initState();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offer(new KeyPress(KeyPress.Type.Escape, null, false, false, false));
|
||||||
|
offer(new KeyPress(KeyPress.Type.Unknown, null, false, false, false));
|
||||||
|
initState();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
escapeSequenceCharacter(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ctrlAndCharacter(char ch) {
|
||||||
|
char ctrlCode;
|
||||||
|
switch (ch) {
|
||||||
|
case 0:
|
||||||
|
ctrlCode = ' ';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 28:
|
||||||
|
ctrlCode = '\\';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 29:
|
||||||
|
ctrlCode = ']';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 30:
|
||||||
|
ctrlCode = '^';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 31:
|
||||||
|
ctrlCode = '_';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ctrlCode = (char) ('a' - 1 + ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offer(new KeyPress(KeyPress.Type.Character, ctrlCode, false, true, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPrintableChar(char ch) {
|
||||||
|
if (Character.isISOControl(ch)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Character.UnicodeBlock block = Character.UnicodeBlock.of(ch);
|
||||||
|
return block != null && block != Character.UnicodeBlock.SPECIALS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ctrlAltAndCharacter(char ch) {
|
||||||
|
char ctrlCode;
|
||||||
|
switch (ch) {
|
||||||
|
case 0:
|
||||||
|
ctrlCode = ' ';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 28:
|
||||||
|
ctrlCode = '\\';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 29:
|
||||||
|
ctrlCode = ']';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 30:
|
||||||
|
ctrlCode = '^';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 31:
|
||||||
|
ctrlCode = '_';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ctrlCode = (char) ('a' - 1 + ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offer(new KeyPress(KeyPress.Type.Character, ctrlCode, true, true, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void escapeSequenceCharacter(char ch) {
|
||||||
|
switch (parseState) {
|
||||||
|
case ESCAPE_SEQUENCE_PARAM1:
|
||||||
|
if (ch == ';') {
|
||||||
|
parseState = ParseState.ESCAPE_SEQUENCE_PARAM2;
|
||||||
|
} else if (Character.isDigit(ch)) {
|
||||||
|
param1 = param1 * 10 + Character.digit(ch, 10);
|
||||||
|
} else {
|
||||||
|
doneEscapeSequenceCharacter(ch);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESCAPE_SEQUENCE_PARAM2:
|
||||||
|
if (Character.isDigit(ch)) {
|
||||||
|
param2 = param2 * 10 + Character.digit(ch, 10);
|
||||||
|
} else {
|
||||||
|
doneEscapeSequenceCharacter(ch);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doneEscapeSequenceCharacter(char last) {
|
||||||
|
boolean alt = false;
|
||||||
|
boolean ctrl = false;
|
||||||
|
boolean shift = false;
|
||||||
|
if (param2 != 0) {
|
||||||
|
alt = isAlt(param2);
|
||||||
|
ctrl = isCtrl(param2);
|
||||||
|
shift = isShift(param2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != '~') {
|
||||||
|
switch (last) {
|
||||||
|
case 'A':
|
||||||
|
offer(new KeyPress(KeyPress.Type.ArrowUp, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'B':
|
||||||
|
offer(new KeyPress(KeyPress.Type.ArrowDown, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'C':
|
||||||
|
offer(new KeyPress(KeyPress.Type.ArrowRight, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'D':
|
||||||
|
offer(new KeyPress(KeyPress.Type.ArrowLeft, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'H':
|
||||||
|
offer(new KeyPress(KeyPress.Type.Home, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'F':
|
||||||
|
offer(new KeyPress(KeyPress.Type.End, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
offer(new KeyPress(KeyPress.Type.F1, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Q':
|
||||||
|
offer(new KeyPress(KeyPress.Type.F2, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'R':
|
||||||
|
offer(new KeyPress(KeyPress.Type.F3, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
offer(new KeyPress(KeyPress.Type.F4, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Z':
|
||||||
|
offer(new KeyPress(KeyPress.Type.ReverseTab, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
offer(new KeyPress(KeyPress.Type.Unknown, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
initState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (param1) {
|
||||||
|
case 1:
|
||||||
|
offer(new KeyPress(KeyPress.Type.Home, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
offer(new KeyPress(KeyPress.Type.Insert, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
offer(new KeyPress(KeyPress.Type.Delete, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
offer(new KeyPress(KeyPress.Type.End, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
offer(new KeyPress(KeyPress.Type.PageUp, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
offer(new KeyPress(KeyPress.Type.PageDown, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 11:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F1, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 12:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F2, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 13:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F3, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 14:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F4, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 15:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F5, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 17:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F6, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 18:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F7, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 19:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F8, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 20:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F9, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 21:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F10, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 23:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F11, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 24:
|
||||||
|
offer(new KeyPress(KeyPress.Type.F12, null, alt, ctrl, shift));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
offer(new KeyPress(KeyPress.Type.Unknown, null, false, false, false));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isShift(int param) {
|
||||||
|
return (param & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAlt(int param) {
|
||||||
|
return (param & 2) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCtrl(int param) {
|
||||||
|
return (param & 4) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void offer(KeyPress keyPress) {
|
||||||
|
// Handle ctrl + c
|
||||||
|
if (keyPress.isCtrl() && keyPress.getType() == KeyPress.Type.Character &&
|
||||||
|
keyPress.getCharacter() == 'c') {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPressQueue.offer(keyPress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
stopThreads.set(true);
|
||||||
|
|
||||||
|
executorService.shutdown();
|
||||||
|
try {
|
||||||
|
while (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
|
||||||
|
LOGGER.warn("Waiting for thread-pool to terminate");
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOGGER.warn("Interrupted while waiting for thread-pool termination", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal.impl;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.clearRemainingLine;
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.color;
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.cursor;
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.moveCursor;
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.normal;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Attributes;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.CursorPosition;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a buffer of the terminal screen for double-buffering.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class ScreenBuffer {
|
||||||
|
private int columns;
|
||||||
|
private int rows;
|
||||||
|
|
||||||
|
private Cell[][] buffer;
|
||||||
|
private Cell[][] physical;
|
||||||
|
|
||||||
|
private boolean cursorVisible;
|
||||||
|
private int cursorColumn;
|
||||||
|
private int cursorRow;
|
||||||
|
|
||||||
|
public void reallocate(int columns, int rows) {
|
||||||
|
buffer = new Cell[columns][rows];
|
||||||
|
physical = new Cell[columns][rows];
|
||||||
|
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
for (int column = 0; column < columns; column++) {
|
||||||
|
buffer[column][row] = new Cell();
|
||||||
|
|
||||||
|
physical[column][row] = new Cell();
|
||||||
|
physical[column][row].unset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.columns = columns;
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
for (int col = 0; col < columns; col++) {
|
||||||
|
buffer[col][row].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush(PrintWriter output) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append(normal());
|
||||||
|
Attributes attributes = new Attributes();
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
flushRow(row, sb, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursorVisible && cursorRow >= 0 && cursorColumn >= 0 && cursorRow < rows &&
|
||||||
|
cursorColumn < columns) {
|
||||||
|
sb.append(cursor(true));
|
||||||
|
sb.append(moveCursor(cursorColumn, cursorRow));
|
||||||
|
} else {
|
||||||
|
sb.append(cursor(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
output.write(sb.toString());
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushRow(int row, StringBuilder sb, Attributes lastAttributes) {
|
||||||
|
int lastColumn = -1;
|
||||||
|
for (int column = 0; column < columns; column++) {
|
||||||
|
Cell cell = buffer[column][row];
|
||||||
|
Cell pCell = physical[column][row];
|
||||||
|
|
||||||
|
if (!cell.equals(pCell)) {
|
||||||
|
if (lastColumn != column - 1 || lastColumn == -1) {
|
||||||
|
sb.append(moveCursor(column, row));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell.isEndOfLine()) {
|
||||||
|
for (int i = column; i < columns; i++) {
|
||||||
|
physical[i][row].set(buffer[i][row]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(clearRemainingLine());
|
||||||
|
lastAttributes.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cell.getAttributes().equals(lastAttributes)) {
|
||||||
|
sb.append(color(cell.getForegroundColor(), cell.getBackgroundColor(), cell.isBold(),
|
||||||
|
cell.isReverse(), cell.isBlink(), cell.isUnderline()));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(cell.getChar());
|
||||||
|
|
||||||
|
lastColumn = column;
|
||||||
|
lastAttributes.set(cell.getAttributes());
|
||||||
|
|
||||||
|
physical[column][row].set(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CursorPosition getCursorPosition() {
|
||||||
|
return new CursorPosition(cursorColumn, cursorRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCursorPosition(int column, int row) {
|
||||||
|
cursorVisible = true;
|
||||||
|
cursorColumn = column;
|
||||||
|
cursorRow = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideCursor() {
|
||||||
|
cursorVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putString(int column, int row, String string, Attributes attributes) {
|
||||||
|
int i = column;
|
||||||
|
for (int j = 0; j < string.length(); j++) {
|
||||||
|
char ch = string.charAt(j);
|
||||||
|
putChar(i, row, ch, attributes);
|
||||||
|
i += 1;
|
||||||
|
if (i == columns) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putChar(int column, int row, char ch, Attributes attributes) {
|
||||||
|
if (column >= 0 && column < columns && row >= 0 && row < rows) {
|
||||||
|
buffer[column][row].setAttributes(attributes);
|
||||||
|
buffer[column][row].setChar(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endOfLine(int column, int row) {
|
||||||
|
if (column >= 0 && column < columns && row >= 0 && row < rows) {
|
||||||
|
buffer[column][row].endOfLine();
|
||||||
|
for (int i = column + 1; i < columns; i++) {
|
||||||
|
buffer[i][row].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,227 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal.impl;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.clearAll;
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.cursor;
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.moveCursor;
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.terminal.impl.EscapeSequences.normal;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.CursorPosition;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.KeyPress;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementation of the {@link Terminal} interface.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class TerminalImpl implements Terminal {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(TerminalImpl.class);
|
||||||
|
|
||||||
|
private TerminalSize cachedTerminalSize;
|
||||||
|
|
||||||
|
private final PrintWriter output;
|
||||||
|
|
||||||
|
private final ScreenBuffer screenBuffer;
|
||||||
|
|
||||||
|
private final Queue<KeyPress> keyPressQueue;
|
||||||
|
private final KeyPressGenerator keyPressGenerator;
|
||||||
|
|
||||||
|
public TerminalImpl() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerminalImpl(@Nullable String title) {
|
||||||
|
output = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
|
||||||
|
sttyRaw();
|
||||||
|
|
||||||
|
if (title != null) {
|
||||||
|
setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
screenBuffer = new ScreenBuffer();
|
||||||
|
|
||||||
|
cachedTerminalSize = queryTerminalSize();
|
||||||
|
updateTerminalSize(cachedTerminalSize.getColumns(), cachedTerminalSize.getRows());
|
||||||
|
|
||||||
|
keyPressQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
keyPressGenerator = new KeyPressGenerator(System.in, keyPressQueue);
|
||||||
|
keyPressGenerator.start();
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
output.printf("%s%s%s%s", moveCursor(0, 0), cursor(true), normal(), clearAll());
|
||||||
|
output.flush();
|
||||||
|
sttyCooked();
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Clear the terminal
|
||||||
|
output.write(clearAll());
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTitle(String title) {
|
||||||
|
output.write(EscapeSequences.setTitle(title));
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTerminalSize(int columns, int rows) {
|
||||||
|
screenBuffer.reallocate(columns, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
screenBuffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refresh() {
|
||||||
|
screenBuffer.flush(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalSize getSize() {
|
||||||
|
return cachedTerminalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public TerminalSize doResizeIfNecessary() {
|
||||||
|
TerminalSize currentTerminalSize = queryTerminalSize();
|
||||||
|
if (!currentTerminalSize.equals(cachedTerminalSize)) {
|
||||||
|
cachedTerminalSize = currentTerminalSize;
|
||||||
|
updateTerminalSize(cachedTerminalSize.getColumns(), cachedTerminalSize.getRows());
|
||||||
|
return cachedTerminalSize;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public KeyPress pollKeyPress() {
|
||||||
|
return keyPressQueue.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CursorPosition getCursorPosition() {
|
||||||
|
return screenBuffer.getCursorPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCursorPosition(int column, int row) {
|
||||||
|
screenBuffer.setCursorPosition(column, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hideCursor() {
|
||||||
|
screenBuffer.hideCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalPrinter getTerminalPrinter(int startRow) {
|
||||||
|
return new TerminalPrinterImpl(screenBuffer, startRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
keyPressGenerator.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TerminalSize queryTerminalSize() {
|
||||||
|
String sizeString = doStty("size");
|
||||||
|
|
||||||
|
int rows = 0;
|
||||||
|
int columns = 0;
|
||||||
|
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(sizeString);
|
||||||
|
int rc = Integer.parseInt(tokenizer.nextToken());
|
||||||
|
if (rc > 0) {
|
||||||
|
rows = rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = Integer.parseInt(tokenizer.nextToken());
|
||||||
|
if (rc > 0) {
|
||||||
|
columns = rc;
|
||||||
|
}
|
||||||
|
return new TerminalSize(columns, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sttyRaw() {
|
||||||
|
doStty("-ignbrk -brkint -parmrk -istrip -inlcr -igncr -icrnl -ixon -opost " +
|
||||||
|
"-echo -echonl -icanon -isig -iexten -parenb cs8 min 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sttyCooked() {
|
||||||
|
doStty("sane cooked");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String doStty(String sttyOptionsString) {
|
||||||
|
String [] cmd = {"/bin/sh", "-c", "stty " + sttyOptionsString + " < /dev/tty"};
|
||||||
|
|
||||||
|
try {
|
||||||
|
Process process = Runtime.getRuntime().exec(cmd);
|
||||||
|
|
||||||
|
String ret;
|
||||||
|
|
||||||
|
// stdout
|
||||||
|
try (BufferedReader stdout = new BufferedReader(new InputStreamReader(
|
||||||
|
process.getInputStream(), StandardCharsets.UTF_8))) {
|
||||||
|
ret = stdout.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
// stderr
|
||||||
|
try (BufferedReader stderr = new BufferedReader(new InputStreamReader(
|
||||||
|
process.getErrorStream(), StandardCharsets.UTF_8))) {
|
||||||
|
String line = stderr.readLine();
|
||||||
|
if ((line != null) && (line.length() > 0)) {
|
||||||
|
LOGGER.error("Error output from stty: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
process.waitFor();
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitValue = process.exitValue();
|
||||||
|
if (exitValue != 0) {
|
||||||
|
LOGGER.error("stty returned error code: " + exitValue);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal.impl;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Attributes;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.Color;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalPrinter;
|
||||||
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementation of the {@link TerminalPrinter} interface.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Private
|
||||||
|
public class TerminalPrinterImpl implements TerminalPrinter {
|
||||||
|
private final ScreenBuffer screenBuffer;
|
||||||
|
private int row;
|
||||||
|
private int column;
|
||||||
|
|
||||||
|
private final Attributes attributes = new Attributes();
|
||||||
|
|
||||||
|
TerminalPrinterImpl(ScreenBuffer screenBuffer, int startRow) {
|
||||||
|
this.screenBuffer = Objects.requireNonNull(screenBuffer);
|
||||||
|
this.row = startRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalPrinter print(String value) {
|
||||||
|
screenBuffer.putString(column, row, value, attributes);
|
||||||
|
column += value.length();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalPrinter startHighlight() {
|
||||||
|
attributes.setForegroundColor(Color.BLACK);
|
||||||
|
attributes.setBackgroundColor(Color.WHITE);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalPrinter stopHighlight() {
|
||||||
|
attributes.setForegroundColor(Color.WHITE);
|
||||||
|
attributes.setBackgroundColor(Color.BLACK);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalPrinter startBold() {
|
||||||
|
attributes.setBold(true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalPrinter stopBold() {
|
||||||
|
attributes.setBold(false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endOfLine() {
|
||||||
|
screenBuffer.endOfLine(column, row);
|
||||||
|
row += 1;
|
||||||
|
column = 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.hasItems;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.Size;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class RecordFilterTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(RecordFilterTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseAndBuilder() {
|
||||||
|
testParseAndBuilder("REGION=region1", false,
|
||||||
|
RecordFilter.newBuilder(Field.REGION).equal("region1"));
|
||||||
|
|
||||||
|
testParseAndBuilder("REGION=", false,
|
||||||
|
RecordFilter.newBuilder(Field.REGION).equal(""));
|
||||||
|
|
||||||
|
testParseAndBuilder("!REGION=region1", false,
|
||||||
|
RecordFilter.newBuilder(Field.REGION).notEqual("region1"));
|
||||||
|
|
||||||
|
testParseAndBuilder("REGION==region2", true,
|
||||||
|
RecordFilter.newBuilder(Field.REGION, true).doubleEquals("region2"));
|
||||||
|
|
||||||
|
testParseAndBuilder("!REGION==region2", true,
|
||||||
|
RecordFilter.newBuilder(Field.REGION, true).notDoubleEquals("region2"));
|
||||||
|
|
||||||
|
testParseAndBuilder("#REQ/S>100", false,
|
||||||
|
RecordFilter.newBuilder(Field.REQUEST_COUNT_PER_SECOND).greater(100L));
|
||||||
|
|
||||||
|
testParseAndBuilder("!#REQ/S>100", false,
|
||||||
|
RecordFilter.newBuilder(Field.REQUEST_COUNT_PER_SECOND).notGreater(100L));
|
||||||
|
|
||||||
|
testParseAndBuilder("SF>=50MB", true,
|
||||||
|
RecordFilter.newBuilder(Field.STORE_FILE_SIZE, true).greaterOrEqual("50MB"));
|
||||||
|
|
||||||
|
testParseAndBuilder("!SF>=50MB", true,
|
||||||
|
RecordFilter.newBuilder(Field.STORE_FILE_SIZE, true).notGreaterOrEqual("50MB"));
|
||||||
|
|
||||||
|
testParseAndBuilder("#REQ/S<20", false,
|
||||||
|
RecordFilter.newBuilder(Field.REQUEST_COUNT_PER_SECOND).less(20L));
|
||||||
|
|
||||||
|
testParseAndBuilder("!#REQ/S<20", false,
|
||||||
|
RecordFilter.newBuilder(Field.REQUEST_COUNT_PER_SECOND).notLess(20L));
|
||||||
|
|
||||||
|
testParseAndBuilder("%COMP<=50%", true,
|
||||||
|
RecordFilter.newBuilder(Field.COMPACTION_PROGRESS, true).lessOrEqual("50%"));
|
||||||
|
|
||||||
|
testParseAndBuilder("!%COMP<=50%", true,
|
||||||
|
RecordFilter.newBuilder(Field.COMPACTION_PROGRESS, true).notLessOrEqual("50%"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testParseAndBuilder(String filterString, boolean ignoreCase, RecordFilter expected) {
|
||||||
|
RecordFilter actual = RecordFilter.parse(filterString, ignoreCase);
|
||||||
|
assertThat(expected, is(actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseFailure() {
|
||||||
|
RecordFilter filter = RecordFilter.parse("REGIO=region1", false);
|
||||||
|
assertThat(filter, is(nullValue()));
|
||||||
|
|
||||||
|
filter = RecordFilter.parse("", false);
|
||||||
|
assertThat(filter, is(nullValue()));
|
||||||
|
|
||||||
|
filter = RecordFilter.parse("#REQ/S==aaa", false);
|
||||||
|
assertThat(filter, is(nullValue()));
|
||||||
|
|
||||||
|
filter = RecordFilter.parse("SF>=50", false);
|
||||||
|
assertThat(filter, is(nullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToString() {
|
||||||
|
testToString("REGION=region1");
|
||||||
|
testToString("!REGION=region1");
|
||||||
|
testToString("REGION==region2");
|
||||||
|
testToString("!REGION==region2");
|
||||||
|
testToString("#REQ/S>100");
|
||||||
|
testToString("!#REQ/S>100");
|
||||||
|
testToString("SF>=50.0MB");
|
||||||
|
testToString("!SF>=50.0MB");
|
||||||
|
testToString("#REQ/S<20");
|
||||||
|
testToString("!#REQ/S<20");
|
||||||
|
testToString("%COMP<=50.00%");
|
||||||
|
testToString("!%COMP<=50.00%");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testToString(String filterString) {
|
||||||
|
RecordFilter filter = RecordFilter.parse(filterString, false);
|
||||||
|
assertThat(filter, is(notNullValue()));
|
||||||
|
assertThat(filterString, is(filter.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilters() {
|
||||||
|
List<Record> records = createTestRecords();
|
||||||
|
|
||||||
|
testFilter(records, "REGION=region", false,
|
||||||
|
"region1", "region2", "region3", "region4", "region5");
|
||||||
|
testFilter(records, "!REGION=region", false);
|
||||||
|
testFilter(records, "REGION=Region", false);
|
||||||
|
|
||||||
|
testFilter(records, "REGION==region", false);
|
||||||
|
testFilter(records, "REGION==region1", false, "region1");
|
||||||
|
testFilter(records, "!REGION==region1", false, "region2", "region3", "region4", "region5");
|
||||||
|
|
||||||
|
testFilter(records, "#REQ/S==100", false, "region1");
|
||||||
|
testFilter(records, "#REQ/S>100", false, "region2", "region5");
|
||||||
|
testFilter(records, "SF>=100MB", false, "region1", "region2", "region4", "region5");
|
||||||
|
testFilter(records, "!#SF>=10", false, "region1", "region4");
|
||||||
|
testFilter(records, "LOCALITY<0.5", false, "region5");
|
||||||
|
testFilter(records, "%COMP<=50%", false, "region2", "region3", "region4", "region5");
|
||||||
|
|
||||||
|
testFilters(records, Arrays.asList("SF>=100MB", "#REQ/S>100"), false,
|
||||||
|
"region2", "region5");
|
||||||
|
testFilters(records, Arrays.asList("%COMP<=50%", "!#SF>=10"), false, "region4");
|
||||||
|
testFilters(records, Arrays.asList("!REGION==region1", "LOCALITY<0.5", "#REQ/S>100"), false,
|
||||||
|
"region5");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFiltersIgnoreCase() {
|
||||||
|
List<Record> records = createTestRecords();
|
||||||
|
|
||||||
|
testFilter(records, "REGION=Region", true,
|
||||||
|
"region1", "region2", "region3", "region4", "region5");
|
||||||
|
testFilter(records, "REGION=REGION", true,
|
||||||
|
"region1", "region2", "region3", "region4", "region5");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Record> createTestRecords() {
|
||||||
|
List<Record> ret = new ArrayList<>();
|
||||||
|
ret.add(createTestRecord("region1", 100L, new Size(100, Size.Unit.MEGABYTE), 2, 1.0f, 80f));
|
||||||
|
ret.add(createTestRecord("region2", 120L, new Size(100, Size.Unit.GIGABYTE), 10, 0.5f, 20f));
|
||||||
|
ret.add(createTestRecord("region3", 50L, new Size(500, Size.Unit.KILOBYTE), 15, 0.8f, 50f));
|
||||||
|
ret.add(createTestRecord("region4", 90L, new Size(10, Size.Unit.TERABYTE), 5, 0.9f, 30f));
|
||||||
|
ret.add(createTestRecord("region5", 200L, new Size(1, Size.Unit.PETABYTE), 13, 0.1f, 40f));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Record createTestRecord(String region, long requestCountPerSecond,
|
||||||
|
Size storeFileSize, int numStoreFiles, float locality, float compactionProgress) {
|
||||||
|
Record.Builder builder = Record.builder();
|
||||||
|
builder.put(Field.REGION, region);
|
||||||
|
builder.put(Field.REQUEST_COUNT_PER_SECOND, requestCountPerSecond);
|
||||||
|
builder.put(Field.STORE_FILE_SIZE, storeFileSize);
|
||||||
|
builder.put(Field.NUM_STORE_FILES, numStoreFiles);
|
||||||
|
builder.put(Field.LOCALITY, locality);
|
||||||
|
builder.put(Field.COMPACTION_PROGRESS, compactionProgress);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testFilter(List<Record> records, String filterString, boolean ignoreCase,
|
||||||
|
String... expectedRegions) {
|
||||||
|
testFilters(records, Collections.singletonList(filterString), ignoreCase, expectedRegions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testFilters(List<Record> records, List<String> filterStrings, boolean ignoreCase,
|
||||||
|
String... expectedRegions) {
|
||||||
|
List<String> actual =
|
||||||
|
records.stream().filter(r -> filterStrings.stream()
|
||||||
|
.map(f -> RecordFilter.parse(f, ignoreCase))
|
||||||
|
.allMatch(f -> f.execute(r)))
|
||||||
|
.map(r -> r.get(Field.REGION).asString())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(actual, hasItems(expectedRegions));
|
||||||
|
assertThat(actual.size(), is(expectedRegions.length));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.Record.entry;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class RecordTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(RecordTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilder() {
|
||||||
|
Record actual1 = Record.builder().put(Field.TABLE, "tableName")
|
||||||
|
.put(entry(Field.REGION_COUNT, 3))
|
||||||
|
.put(Field.REQUEST_COUNT_PER_SECOND, Field.REQUEST_COUNT_PER_SECOND.newValue(100L))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(actual1.size(), is(3));
|
||||||
|
assertThat(actual1.get(Field.TABLE).asString(), is("tableName"));
|
||||||
|
assertThat(actual1.get(Field.REGION_COUNT).asInt(), is(3));
|
||||||
|
assertThat(actual1.get(Field.REQUEST_COUNT_PER_SECOND).asLong(), is(100L));
|
||||||
|
|
||||||
|
Record actual2 = Record.builder().putAll(actual1).build();
|
||||||
|
|
||||||
|
assertThat(actual2.size(), is(3));
|
||||||
|
assertThat(actual2.get(Field.TABLE).asString(), is("tableName"));
|
||||||
|
assertThat(actual2.get(Field.REGION_COUNT).asInt(), is(3));
|
||||||
|
assertThat(actual2.get(Field.REQUEST_COUNT_PER_SECOND).asLong(), is(100L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOfEntries() {
|
||||||
|
Record actual = Record.ofEntries(
|
||||||
|
entry(Field.TABLE, "tableName"),
|
||||||
|
entry(Field.REGION_COUNT, 3),
|
||||||
|
entry(Field.REQUEST_COUNT_PER_SECOND, 100L)
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(3));
|
||||||
|
assertThat(actual.get(Field.TABLE).asString(), is("tableName"));
|
||||||
|
assertThat(actual.get(Field.REGION_COUNT).asInt(), is(3));
|
||||||
|
assertThat(actual.get(Field.REQUEST_COUNT_PER_SECOND).asLong(), is(100L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCombine() {
|
||||||
|
Record record1 = Record.ofEntries(
|
||||||
|
entry(Field.TABLE, "tableName"),
|
||||||
|
entry(Field.REGION_COUNT, 3),
|
||||||
|
entry(Field.REQUEST_COUNT_PER_SECOND, 100L)
|
||||||
|
);
|
||||||
|
|
||||||
|
Record record2 = Record.ofEntries(
|
||||||
|
entry(Field.TABLE, "tableName"),
|
||||||
|
entry(Field.REGION_COUNT, 5),
|
||||||
|
entry(Field.REQUEST_COUNT_PER_SECOND, 500L)
|
||||||
|
);
|
||||||
|
|
||||||
|
Record actual = record1.combine(record2);
|
||||||
|
|
||||||
|
assertThat(actual.size(), is(3));
|
||||||
|
assertThat(actual.get(Field.TABLE).asString(), is("tableName"));
|
||||||
|
assertThat(actual.get(Field.REGION_COUNT).asInt(), is(8));
|
||||||
|
assertThat(actual.get(Field.REQUEST_COUNT_PER_SECOND).asLong(), is(600L));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,402 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.commons.lang3.time.FastDateFormat;
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetrics;
|
||||||
|
import org.apache.hadoop.hbase.ClusterMetricsBuilder;
|
||||||
|
import org.apache.hadoop.hbase.RegionMetrics;
|
||||||
|
import org.apache.hadoop.hbase.RegionMetricsBuilder;
|
||||||
|
import org.apache.hadoop.hbase.ServerMetrics;
|
||||||
|
import org.apache.hadoop.hbase.ServerMetricsBuilder;
|
||||||
|
import org.apache.hadoop.hbase.ServerName;
|
||||||
|
import org.apache.hadoop.hbase.Size;
|
||||||
|
import org.apache.hadoop.hbase.TableName;
|
||||||
|
import org.apache.hadoop.hbase.client.RegionInfoBuilder;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.top.Summary;
|
||||||
|
import org.apache.hadoop.hbase.master.RegionState;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
|
||||||
|
|
||||||
|
public final class TestUtils {
|
||||||
|
|
||||||
|
private TestUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClusterMetrics createDummyClusterMetrics() {
|
||||||
|
Map<ServerName, ServerMetrics> serverMetricsMap = new HashMap<>();
|
||||||
|
|
||||||
|
// host1
|
||||||
|
List<RegionMetrics> regionMetricsList = new ArrayList<>();
|
||||||
|
regionMetricsList.add(createRegionMetrics(
|
||||||
|
"table1,,1.00000000000000000000000000000000.",
|
||||||
|
100, 50, 100,
|
||||||
|
new Size(100, Size.Unit.MEGABYTE), new Size(200, Size.Unit.MEGABYTE), 1,
|
||||||
|
new Size(100, Size.Unit.MEGABYTE), 0.1f, 100, 100, "2019-07-22 00:00:00"));
|
||||||
|
regionMetricsList.add(createRegionMetrics(
|
||||||
|
"table2,1,2.00000000000000000000000000000001.",
|
||||||
|
200, 100, 200,
|
||||||
|
new Size(200, Size.Unit.MEGABYTE), new Size(400, Size.Unit.MEGABYTE), 2,
|
||||||
|
new Size(200, Size.Unit.MEGABYTE), 0.2f, 50, 200, "2019-07-22 00:00:01"));
|
||||||
|
regionMetricsList.add(createRegionMetrics(
|
||||||
|
"namespace:table3,,3_0001.00000000000000000000000000000002.",
|
||||||
|
300, 150, 300,
|
||||||
|
new Size(300, Size.Unit.MEGABYTE), new Size(600, Size.Unit.MEGABYTE), 3,
|
||||||
|
new Size(300, Size.Unit.MEGABYTE), 0.3f, 100, 300, "2019-07-22 00:00:02"));
|
||||||
|
|
||||||
|
ServerName host1 = ServerName.valueOf("host1.apache.com", 1000, 1);
|
||||||
|
serverMetricsMap.put(host1, createServerMetrics(host1, 100,
|
||||||
|
new Size(100, Size.Unit.MEGABYTE), new Size(200, Size.Unit.MEGABYTE), 100,
|
||||||
|
regionMetricsList));
|
||||||
|
|
||||||
|
// host2
|
||||||
|
regionMetricsList.clear();
|
||||||
|
regionMetricsList.add(createRegionMetrics(
|
||||||
|
"table1,1,4.00000000000000000000000000000003.",
|
||||||
|
100, 50, 100,
|
||||||
|
new Size(100, Size.Unit.MEGABYTE), new Size(200, Size.Unit.MEGABYTE), 1,
|
||||||
|
new Size(100, Size.Unit.MEGABYTE), 0.4f, 50, 100, "2019-07-22 00:00:03"));
|
||||||
|
regionMetricsList.add(createRegionMetrics(
|
||||||
|
"table2,,5.00000000000000000000000000000004.",
|
||||||
|
200, 100, 200,
|
||||||
|
new Size(200, Size.Unit.MEGABYTE), new Size(400, Size.Unit.MEGABYTE), 2,
|
||||||
|
new Size(200, Size.Unit.MEGABYTE), 0.5f, 150, 200, "2019-07-22 00:00:04"));
|
||||||
|
regionMetricsList.add(createRegionMetrics(
|
||||||
|
"namespace:table3,,6.00000000000000000000000000000005.",
|
||||||
|
300, 150, 300,
|
||||||
|
new Size(300, Size.Unit.MEGABYTE), new Size(600, Size.Unit.MEGABYTE), 3,
|
||||||
|
new Size(300, Size.Unit.MEGABYTE), 0.6f, 200, 300, "2019-07-22 00:00:05"));
|
||||||
|
|
||||||
|
ServerName host2 = ServerName.valueOf("host2.apache.com", 1001, 2);
|
||||||
|
serverMetricsMap.put(host2, createServerMetrics(host2, 200,
|
||||||
|
new Size(16, Size.Unit.GIGABYTE), new Size(32, Size.Unit.GIGABYTE), 200,
|
||||||
|
regionMetricsList));
|
||||||
|
|
||||||
|
ServerName host3 = ServerName.valueOf("host3.apache.com", 1002, 3);
|
||||||
|
return ClusterMetricsBuilder.newBuilder()
|
||||||
|
.setHBaseVersion("3.0.0-SNAPSHOT")
|
||||||
|
.setClusterId("01234567-89ab-cdef-0123-456789abcdef")
|
||||||
|
.setLiveServerMetrics(serverMetricsMap)
|
||||||
|
.setDeadServerNames(Collections.singletonList(host3))
|
||||||
|
.setRegionsInTransition(Collections.singletonList(
|
||||||
|
new RegionState(RegionInfoBuilder.newBuilder(TableName.valueOf("table4"))
|
||||||
|
.setStartKey(new byte [0])
|
||||||
|
.setEndKey(new byte [0])
|
||||||
|
.setOffline(true)
|
||||||
|
.setReplicaId(0)
|
||||||
|
.setRegionId(0)
|
||||||
|
.setSplit(false)
|
||||||
|
.build(),
|
||||||
|
RegionState.State.OFFLINE, host3)))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RegionMetrics createRegionMetrics(String regionName, long readRequestCount,
|
||||||
|
long filteredReadRequestCount, long writeRequestCount, Size storeFileSize,
|
||||||
|
Size uncompressedStoreFileSize, int storeFileCount, Size memStoreSize, float locality,
|
||||||
|
long compactedCellCount, long compactingCellCount, String lastMajorCompactionTime) {
|
||||||
|
|
||||||
|
FastDateFormat df = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
|
||||||
|
try {
|
||||||
|
return RegionMetricsBuilder.newBuilder(Bytes.toBytes(regionName))
|
||||||
|
.setReadRequestCount(readRequestCount)
|
||||||
|
.setFilteredReadRequestCount(filteredReadRequestCount)
|
||||||
|
.setWriteRequestCount(writeRequestCount).setStoreFileSize(storeFileSize)
|
||||||
|
.setUncompressedStoreFileSize(uncompressedStoreFileSize).setStoreFileCount(storeFileCount)
|
||||||
|
.setMemStoreSize(memStoreSize).setDataLocality(locality)
|
||||||
|
.setCompactedCellCount(compactedCellCount).setCompactingCellCount(compactingCellCount)
|
||||||
|
.setLastMajorCompactionTimestamp(df.parse(lastMajorCompactionTime).getTime()).build();
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServerMetrics createServerMetrics(ServerName serverName, long reportTimestamp,
|
||||||
|
Size usedHeapSize, Size maxHeapSize, long requestCountPerSecond,
|
||||||
|
List<RegionMetrics> regionMetricsList) {
|
||||||
|
|
||||||
|
return ServerMetricsBuilder.newBuilder(serverName)
|
||||||
|
.setReportTimestamp(reportTimestamp)
|
||||||
|
.setUsedHeapSize(usedHeapSize)
|
||||||
|
.setMaxHeapSize(maxHeapSize)
|
||||||
|
.setRequestCountPerSecond(requestCountPerSecond)
|
||||||
|
.setRegionMetrics(regionMetricsList).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertRecordsInRegionMode(List<Record> records) {
|
||||||
|
assertThat(records.size(), is(6));
|
||||||
|
|
||||||
|
for (Record record : records) {
|
||||||
|
switch (record.get(Field.REGION_NAME).asString()) {
|
||||||
|
case "table1,,1.00000000000000000000000000000000.":
|
||||||
|
assertRecordInRegionMode(record, "default", "1", "", "table1",
|
||||||
|
"00000000000000000000000000000000", "host1:1000", "host1.apache.com,1000,1",0L,
|
||||||
|
0L, 0L, 0L, new Size(100, Size.Unit.MEGABYTE), new Size(200, Size.Unit.MEGABYTE), 1,
|
||||||
|
new Size(100, Size.Unit.MEGABYTE), 0.1f, "", 100L, 100L, 100f,
|
||||||
|
"2019-07-22 00:00:00");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "table1,1,4.00000000000000000000000000000003.":
|
||||||
|
assertRecordInRegionMode(record, "default", "4", "", "table1",
|
||||||
|
"00000000000000000000000000000003", "host2:1001", "host2.apache.com,1001,2",0L,
|
||||||
|
0L, 0L, 0L, new Size(100, Size.Unit.MEGABYTE), new Size(200, Size.Unit.MEGABYTE), 1,
|
||||||
|
new Size(100, Size.Unit.MEGABYTE), 0.4f, "1", 100L, 50L, 50f,
|
||||||
|
"2019-07-22 00:00:03");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "table2,,5.00000000000000000000000000000004.":
|
||||||
|
assertRecordInRegionMode(record, "default", "5", "", "table2",
|
||||||
|
"00000000000000000000000000000004", "host2:1001", "host2.apache.com,1001,2",0L,
|
||||||
|
0L, 0L, 0L, new Size(200, Size.Unit.MEGABYTE), new Size(400, Size.Unit.MEGABYTE), 2,
|
||||||
|
new Size(200, Size.Unit.MEGABYTE), 0.5f, "", 200L, 150L, 75f,
|
||||||
|
"2019-07-22 00:00:04");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "table2,1,2.00000000000000000000000000000001.":
|
||||||
|
assertRecordInRegionMode(record, "default", "2", "", "table2",
|
||||||
|
"00000000000000000000000000000001", "host1:1000", "host1.apache.com,1000,1",0L,
|
||||||
|
0L, 0L, 0L, new Size(200, Size.Unit.MEGABYTE), new Size(400, Size.Unit.MEGABYTE), 2,
|
||||||
|
new Size(200, Size.Unit.MEGABYTE), 0.2f, "1", 200L, 50L, 25f,
|
||||||
|
"2019-07-22 00:00:01");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "namespace:table3,,6.00000000000000000000000000000005.":
|
||||||
|
assertRecordInRegionMode(record, "namespace", "6", "", "table3",
|
||||||
|
"00000000000000000000000000000005", "host2:1001", "host2.apache.com,1001,2",0L,
|
||||||
|
0L, 0L, 0L, new Size(300, Size.Unit.MEGABYTE), new Size(600, Size.Unit.MEGABYTE), 3,
|
||||||
|
new Size(300, Size.Unit.MEGABYTE), 0.6f, "", 300L, 200L, 66.66667f,
|
||||||
|
"2019-07-22 00:00:05");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "namespace:table3,,3_0001.00000000000000000000000000000002.":
|
||||||
|
assertRecordInRegionMode(record, "namespace", "3", "1", "table3",
|
||||||
|
"00000000000000000000000000000002", "host1:1000", "host1.apache.com,1000,1",0L,
|
||||||
|
0L, 0L, 0L, new Size(300, Size.Unit.MEGABYTE), new Size(600, Size.Unit.MEGABYTE), 3,
|
||||||
|
new Size(300, Size.Unit.MEGABYTE), 0.3f, "", 300L, 100L, 33.333336f,
|
||||||
|
"2019-07-22 00:00:02");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertRecordInRegionMode(Record record, String namespace, String startCode,
|
||||||
|
String replicaId, String table, String region, String regionServer, String longRegionServer,
|
||||||
|
long requestCountPerSecond, long readRequestCountPerSecond,
|
||||||
|
long filteredReadRequestCountPerSecond, long writeCountRequestPerSecond,
|
||||||
|
Size storeFileSize, Size uncompressedStoreFileSize, int numStoreFiles,
|
||||||
|
Size memStoreSize, float Locality, String startKey, long compactingCellCount,
|
||||||
|
long compactedCellCount, float compactionProgress, String lastMajorCompactionTime) {
|
||||||
|
assertThat(record.size(), is(22));
|
||||||
|
assertThat(record.get(Field.NAMESPACE).asString(), is(namespace));
|
||||||
|
assertThat(record.get(Field.START_CODE).asString(), is(startCode));
|
||||||
|
assertThat(record.get(Field.REPLICA_ID).asString(), is(replicaId));
|
||||||
|
assertThat(record.get(Field.TABLE).asString(), is(table));
|
||||||
|
assertThat(record.get(Field.REGION).asString(), is(region));
|
||||||
|
assertThat(record.get(Field.REGION_SERVER).asString(), is(regionServer));
|
||||||
|
assertThat(record.get(Field.LONG_REGION_SERVER).asString(), is(longRegionServer));
|
||||||
|
assertThat(record.get(Field.REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(requestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.READ_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(readRequestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(filteredReadRequestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.WRITE_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(writeCountRequestPerSecond));
|
||||||
|
assertThat(record.get(Field.STORE_FILE_SIZE).asSize(), is(storeFileSize));
|
||||||
|
assertThat(record.get(Field.UNCOMPRESSED_STORE_FILE_SIZE).asSize(),
|
||||||
|
is(uncompressedStoreFileSize));
|
||||||
|
assertThat(record.get(Field.NUM_STORE_FILES).asInt(), is(numStoreFiles));
|
||||||
|
assertThat(record.get(Field.MEM_STORE_SIZE).asSize(), is(memStoreSize));
|
||||||
|
assertThat(record.get(Field.LOCALITY).asFloat(), is(Locality));
|
||||||
|
assertThat(record.get(Field.START_KEY).asString(), is(startKey));
|
||||||
|
assertThat(record.get(Field.COMPACTING_CELL_COUNT).asLong(), is(compactingCellCount));
|
||||||
|
assertThat(record.get(Field.COMPACTED_CELL_COUNT).asLong(), is(compactedCellCount));
|
||||||
|
assertThat(record.get(Field.COMPACTION_PROGRESS).asFloat(), is(compactionProgress));
|
||||||
|
assertThat(record.get(Field.LAST_MAJOR_COMPACTION_TIME).asString(),
|
||||||
|
is(lastMajorCompactionTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertRecordsInNamespaceMode(List<Record> records) {
|
||||||
|
assertThat(records.size(), is(2));
|
||||||
|
|
||||||
|
for (Record record : records) {
|
||||||
|
switch (record.get(Field.NAMESPACE).asString()) {
|
||||||
|
case "default":
|
||||||
|
assertRecordInNamespaceMode(record, 0L, 0L, 0L, 0L, new Size(600, Size.Unit.MEGABYTE),
|
||||||
|
new Size(1200, Size.Unit.MEGABYTE), 6, new Size(600, Size.Unit.MEGABYTE), 4);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "namespace":
|
||||||
|
assertRecordInNamespaceMode(record, 0L, 0L, 0L, 0L, new Size(600, Size.Unit.MEGABYTE),
|
||||||
|
new Size(1200, Size.Unit.MEGABYTE), 6, new Size(600, Size.Unit.MEGABYTE), 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertRecordInNamespaceMode(Record record, long requestCountPerSecond,
|
||||||
|
long readRequestCountPerSecond, long filteredReadRequestCountPerSecond,
|
||||||
|
long writeCountRequestPerSecond, Size storeFileSize, Size uncompressedStoreFileSize,
|
||||||
|
int numStoreFiles, Size memStoreSize, int regionCount) {
|
||||||
|
assertThat(record.size(), is(10));
|
||||||
|
assertThat(record.get(Field.REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(requestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.READ_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(readRequestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(filteredReadRequestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.WRITE_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(writeCountRequestPerSecond));
|
||||||
|
assertThat(record.get(Field.STORE_FILE_SIZE).asSize(), is(storeFileSize));
|
||||||
|
assertThat(record.get(Field.UNCOMPRESSED_STORE_FILE_SIZE).asSize(),
|
||||||
|
is(uncompressedStoreFileSize));
|
||||||
|
assertThat(record.get(Field.NUM_STORE_FILES).asInt(), is(numStoreFiles));
|
||||||
|
assertThat(record.get(Field.MEM_STORE_SIZE).asSize(), is(memStoreSize));
|
||||||
|
assertThat(record.get(Field.REGION_COUNT).asInt(), is(regionCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertRecordsInTableMode(List<Record> records) {
|
||||||
|
assertThat(records.size(), is(3));
|
||||||
|
|
||||||
|
for (Record record : records) {
|
||||||
|
String tableName = String.format("%s:%s", record.get(Field.NAMESPACE).asString(),
|
||||||
|
record.get(Field.TABLE).asString());
|
||||||
|
|
||||||
|
switch (tableName) {
|
||||||
|
case "default:table1":
|
||||||
|
assertRecordInTableMode(record, 0L, 0L, 0L, 0L, new Size(200, Size.Unit.MEGABYTE),
|
||||||
|
new Size(400, Size.Unit.MEGABYTE), 2, new Size(200, Size.Unit.MEGABYTE), 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "default:table2":
|
||||||
|
assertRecordInTableMode(record, 0L, 0L, 0L, 0L, new Size(400, Size.Unit.MEGABYTE),
|
||||||
|
new Size(800, Size.Unit.MEGABYTE), 4, new Size(400, Size.Unit.MEGABYTE), 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "namespace:table3":
|
||||||
|
assertRecordInTableMode(record, 0L, 0L, 0L, 0L, new Size(600, Size.Unit.MEGABYTE),
|
||||||
|
new Size(1200, Size.Unit.MEGABYTE), 6, new Size(600, Size.Unit.MEGABYTE), 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertRecordInTableMode(Record record, long requestCountPerSecond,
|
||||||
|
long readRequestCountPerSecond, long filteredReadRequestCountPerSecond,
|
||||||
|
long writeCountRequestPerSecond, Size storeFileSize, Size uncompressedStoreFileSize,
|
||||||
|
int numStoreFiles, Size memStoreSize, int regionCount) {
|
||||||
|
assertThat(record.size(), is(11));
|
||||||
|
assertThat(record.get(Field.REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(requestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.READ_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(readRequestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(filteredReadRequestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.WRITE_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(writeCountRequestPerSecond));
|
||||||
|
assertThat(record.get(Field.STORE_FILE_SIZE).asSize(), is(storeFileSize));
|
||||||
|
assertThat(record.get(Field.UNCOMPRESSED_STORE_FILE_SIZE).asSize(),
|
||||||
|
is(uncompressedStoreFileSize));
|
||||||
|
assertThat(record.get(Field.NUM_STORE_FILES).asInt(), is(numStoreFiles));
|
||||||
|
assertThat(record.get(Field.MEM_STORE_SIZE).asSize(), is(memStoreSize));
|
||||||
|
assertThat(record.get(Field.REGION_COUNT).asInt(), is(regionCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertRecordsInRegionServerMode(List<Record> records) {
|
||||||
|
assertThat(records.size(), is(2));
|
||||||
|
|
||||||
|
for (Record record : records) {
|
||||||
|
switch (record.get(Field.REGION_SERVER).asString()) {
|
||||||
|
case "host1:1000":
|
||||||
|
assertRecordInRegionServerMode(record, "host1.apache.com,1000,1", 0L, 0L, 0L, 0L,
|
||||||
|
new Size(600, Size.Unit.MEGABYTE), new Size(1200, Size.Unit.MEGABYTE), 6,
|
||||||
|
new Size(600, Size.Unit.MEGABYTE), 3, new Size(100, Size.Unit.MEGABYTE),
|
||||||
|
new Size(200, Size.Unit.MEGABYTE));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "host2:1001":
|
||||||
|
assertRecordInRegionServerMode(record, "host2.apache.com,1001,2", 0L, 0L, 0L, 0L,
|
||||||
|
new Size(600, Size.Unit.MEGABYTE), new Size(1200, Size.Unit.MEGABYTE), 6,
|
||||||
|
new Size(600, Size.Unit.MEGABYTE), 3, new Size(16, Size.Unit.GIGABYTE),
|
||||||
|
new Size(32, Size.Unit.GIGABYTE));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertRecordInRegionServerMode(Record record, String longRegionServer,
|
||||||
|
long requestCountPerSecond, long readRequestCountPerSecond,
|
||||||
|
long filteredReadRequestCountPerSecond, long writeCountRequestPerSecond,
|
||||||
|
Size storeFileSize, Size uncompressedStoreFileSize, int numStoreFiles,
|
||||||
|
Size memStoreSize, int regionCount, Size usedHeapSize, Size maxHeapSize) {
|
||||||
|
assertThat(record.size(), is(13));
|
||||||
|
assertThat(record.get(Field.LONG_REGION_SERVER).asString(),
|
||||||
|
is(longRegionServer));
|
||||||
|
assertThat(record.get(Field.REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(requestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.READ_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(readRequestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.FILTERED_READ_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(filteredReadRequestCountPerSecond));
|
||||||
|
assertThat(record.get(Field.WRITE_REQUEST_COUNT_PER_SECOND).asLong(),
|
||||||
|
is(writeCountRequestPerSecond));
|
||||||
|
assertThat(record.get(Field.STORE_FILE_SIZE).asSize(), is(storeFileSize));
|
||||||
|
assertThat(record.get(Field.UNCOMPRESSED_STORE_FILE_SIZE).asSize(),
|
||||||
|
is(uncompressedStoreFileSize));
|
||||||
|
assertThat(record.get(Field.NUM_STORE_FILES).asInt(), is(numStoreFiles));
|
||||||
|
assertThat(record.get(Field.MEM_STORE_SIZE).asSize(), is(memStoreSize));
|
||||||
|
assertThat(record.get(Field.REGION_COUNT).asInt(), is(regionCount));
|
||||||
|
assertThat(record.get(Field.USED_HEAP_SIZE).asSize(), is(usedHeapSize));
|
||||||
|
assertThat(record.get(Field.MAX_HEAP_SIZE).asSize(), is(maxHeapSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertSummary(Summary summary) {
|
||||||
|
assertThat(summary.getVersion(), is("3.0.0-SNAPSHOT"));
|
||||||
|
assertThat(summary.getClusterId(), is("01234567-89ab-cdef-0123-456789abcdef"));
|
||||||
|
assertThat(summary.getServers(), is(3));
|
||||||
|
assertThat(summary.getLiveServers(), is(2));
|
||||||
|
assertThat(summary.getDeadServers(), is(1));
|
||||||
|
assertThat(summary.getRegionCount(), is(6));
|
||||||
|
assertThat(summary.getRitCount(), is(1));
|
||||||
|
assertThat(summary.getAverageLoad(), is(3.0));
|
||||||
|
assertThat(summary.getAggregateRequestPerSecond(), is(300L));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,298 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.field;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.Size;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class FieldValueTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(FieldValueTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseAndAsSomethingMethod() {
|
||||||
|
// String
|
||||||
|
FieldValue stringFieldValue = new FieldValue("aaa", FieldValueType.STRING);
|
||||||
|
assertThat(stringFieldValue.asString(), is("aaa"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue(1, FieldValueType.STRING);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer
|
||||||
|
FieldValue integerFieldValue = new FieldValue(100, FieldValueType.INTEGER);
|
||||||
|
assertThat(integerFieldValue.asInt(), is(100));
|
||||||
|
|
||||||
|
integerFieldValue = new FieldValue("100", FieldValueType.INTEGER);
|
||||||
|
assertThat(integerFieldValue.asInt(), is(100));
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue("aaa", FieldValueType.INTEGER);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Long
|
||||||
|
FieldValue longFieldValue = new FieldValue(100L, FieldValueType.LONG);
|
||||||
|
assertThat(longFieldValue.asLong(), is(100L));
|
||||||
|
|
||||||
|
longFieldValue = new FieldValue("100", FieldValueType.LONG);
|
||||||
|
assertThat(longFieldValue.asLong(), is(100L));
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue("aaa", FieldValueType.LONG);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue(100, FieldValueType.LONG);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float
|
||||||
|
FieldValue floatFieldValue = new FieldValue(1.0f, FieldValueType.FLOAT);
|
||||||
|
assertThat(floatFieldValue.asFloat(), is(1.0f));
|
||||||
|
|
||||||
|
floatFieldValue = new FieldValue("1", FieldValueType.FLOAT);
|
||||||
|
assertThat(floatFieldValue.asFloat(), is(1.0f));
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue("aaa", FieldValueType.FLOAT);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue(1, FieldValueType.FLOAT);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size
|
||||||
|
FieldValue sizeFieldValue =
|
||||||
|
new FieldValue(new Size(100, Size.Unit.MEGABYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("100.0MB"));
|
||||||
|
assertThat(sizeFieldValue.asSize(), is(new Size(100, Size.Unit.MEGABYTE)));
|
||||||
|
|
||||||
|
sizeFieldValue = new FieldValue("100MB", FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("100.0MB"));
|
||||||
|
assertThat(sizeFieldValue.asSize(), is(new Size(100, Size.Unit.MEGABYTE)));
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue("100", FieldValueType.SIZE);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue(100, FieldValueType.SIZE);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Percent
|
||||||
|
FieldValue percentFieldValue =
|
||||||
|
new FieldValue(100f, FieldValueType.PERCENT);
|
||||||
|
assertThat(percentFieldValue.asString(), is("100.00%"));
|
||||||
|
assertThat(percentFieldValue.asFloat(), is(100f));
|
||||||
|
|
||||||
|
percentFieldValue = new FieldValue("100%", FieldValueType.PERCENT);
|
||||||
|
assertThat(percentFieldValue.asString(), is("100.00%"));
|
||||||
|
assertThat(percentFieldValue.asFloat(), is(100f));
|
||||||
|
|
||||||
|
percentFieldValue = new FieldValue("100", FieldValueType.PERCENT);
|
||||||
|
assertThat(percentFieldValue.asString(), is("100.00%"));
|
||||||
|
assertThat(percentFieldValue.asFloat(), is(100f));
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FieldValue(100, FieldValueType.PERCENT);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompareTo() {
|
||||||
|
// String
|
||||||
|
FieldValue stringAFieldValue = new FieldValue("a", FieldValueType.STRING);
|
||||||
|
FieldValue stringAFieldValue2 = new FieldValue("a", FieldValueType.STRING);
|
||||||
|
FieldValue stringBFieldValue = new FieldValue("b", FieldValueType.STRING);
|
||||||
|
FieldValue stringCapitalAFieldValue = new FieldValue("A", FieldValueType.STRING);
|
||||||
|
|
||||||
|
assertThat(stringAFieldValue.compareTo(stringAFieldValue2), is(0));
|
||||||
|
assertThat(stringBFieldValue.compareTo(stringAFieldValue), is(1));
|
||||||
|
assertThat(stringAFieldValue.compareTo(stringBFieldValue), is(-1));
|
||||||
|
assertThat(stringAFieldValue.compareTo(stringCapitalAFieldValue), is(32));
|
||||||
|
|
||||||
|
// Integer
|
||||||
|
FieldValue integer1FieldValue = new FieldValue(1, FieldValueType.INTEGER);
|
||||||
|
FieldValue integer1FieldValue2 = new FieldValue(1, FieldValueType.INTEGER);
|
||||||
|
FieldValue integer2FieldValue = new FieldValue(2, FieldValueType.INTEGER);
|
||||||
|
|
||||||
|
assertThat(integer1FieldValue.compareTo(integer1FieldValue2), is(0));
|
||||||
|
assertThat(integer2FieldValue.compareTo(integer1FieldValue), is(1));
|
||||||
|
assertThat(integer1FieldValue.compareTo(integer2FieldValue), is(-1));
|
||||||
|
|
||||||
|
// Long
|
||||||
|
FieldValue long1FieldValue = new FieldValue(1L, FieldValueType.LONG);
|
||||||
|
FieldValue long1FieldValue2 = new FieldValue(1L, FieldValueType.LONG);
|
||||||
|
FieldValue long2FieldValue = new FieldValue(2L, FieldValueType.LONG);
|
||||||
|
|
||||||
|
assertThat(long1FieldValue.compareTo(long1FieldValue2), is(0));
|
||||||
|
assertThat(long2FieldValue.compareTo(long1FieldValue), is(1));
|
||||||
|
assertThat(long1FieldValue.compareTo(long2FieldValue), is(-1));
|
||||||
|
|
||||||
|
// Float
|
||||||
|
FieldValue float1FieldValue = new FieldValue(1.0f, FieldValueType.FLOAT);
|
||||||
|
FieldValue float1FieldValue2 = new FieldValue(1.0f, FieldValueType.FLOAT);
|
||||||
|
FieldValue float2FieldValue = new FieldValue(2.0f, FieldValueType.FLOAT);
|
||||||
|
|
||||||
|
assertThat(float1FieldValue.compareTo(float1FieldValue2), is(0));
|
||||||
|
assertThat(float2FieldValue.compareTo(float1FieldValue), is(1));
|
||||||
|
assertThat(float1FieldValue.compareTo(float2FieldValue), is(-1));
|
||||||
|
|
||||||
|
// Size
|
||||||
|
FieldValue size100MBFieldValue =
|
||||||
|
new FieldValue(new Size(100, Size.Unit.MEGABYTE), FieldValueType.SIZE);
|
||||||
|
FieldValue size100MBFieldValue2 =
|
||||||
|
new FieldValue(new Size(100, Size.Unit.MEGABYTE), FieldValueType.SIZE);
|
||||||
|
FieldValue size200MBFieldValue =
|
||||||
|
new FieldValue(new Size(200, Size.Unit.MEGABYTE), FieldValueType.SIZE);
|
||||||
|
|
||||||
|
assertThat(size100MBFieldValue.compareTo(size100MBFieldValue2), is(0));
|
||||||
|
assertThat(size200MBFieldValue.compareTo(size100MBFieldValue), is(1));
|
||||||
|
assertThat(size100MBFieldValue.compareTo(size200MBFieldValue), is(-1));
|
||||||
|
|
||||||
|
// Percent
|
||||||
|
FieldValue percent50FieldValue = new FieldValue(50.0f, FieldValueType.PERCENT);
|
||||||
|
FieldValue percent50FieldValue2 = new FieldValue(50.0f, FieldValueType.PERCENT);
|
||||||
|
FieldValue percent100FieldValue = new FieldValue(100.0f, FieldValueType.PERCENT);
|
||||||
|
|
||||||
|
assertThat(percent50FieldValue.compareTo(percent50FieldValue2), is(0));
|
||||||
|
assertThat(percent100FieldValue.compareTo(percent50FieldValue), is(1));
|
||||||
|
assertThat(percent50FieldValue.compareTo(percent100FieldValue), is(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPlus() {
|
||||||
|
// String
|
||||||
|
FieldValue stringFieldValue = new FieldValue("a", FieldValueType.STRING);
|
||||||
|
FieldValue stringFieldValue2 = new FieldValue("b", FieldValueType.STRING);
|
||||||
|
assertThat(stringFieldValue.plus(stringFieldValue2).asString(), is("ab"));
|
||||||
|
|
||||||
|
// Integer
|
||||||
|
FieldValue integerFieldValue = new FieldValue(1, FieldValueType.INTEGER);
|
||||||
|
FieldValue integerFieldValue2 = new FieldValue(2, FieldValueType.INTEGER);
|
||||||
|
assertThat(integerFieldValue.plus(integerFieldValue2).asInt(), is(3));
|
||||||
|
|
||||||
|
// Long
|
||||||
|
FieldValue longFieldValue = new FieldValue(1L, FieldValueType.LONG);
|
||||||
|
FieldValue longFieldValue2 = new FieldValue(2L, FieldValueType.LONG);
|
||||||
|
assertThat(longFieldValue.plus(longFieldValue2).asLong(), is(3L));
|
||||||
|
|
||||||
|
// Float
|
||||||
|
FieldValue floatFieldValue = new FieldValue(1.2f, FieldValueType.FLOAT);
|
||||||
|
FieldValue floatFieldValue2 = new FieldValue(2.2f, FieldValueType.FLOAT);
|
||||||
|
assertThat(floatFieldValue.plus(floatFieldValue2).asFloat(), is(3.4f));
|
||||||
|
|
||||||
|
// Size
|
||||||
|
FieldValue sizeFieldValue =
|
||||||
|
new FieldValue(new Size(100, Size.Unit.MEGABYTE), FieldValueType.SIZE);
|
||||||
|
FieldValue sizeFieldValue2 =
|
||||||
|
new FieldValue(new Size(200, Size.Unit.MEGABYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.plus(sizeFieldValue2).asString(), is("300.0MB"));
|
||||||
|
assertThat(sizeFieldValue.plus(sizeFieldValue2).asSize(),
|
||||||
|
is(new Size(300, Size.Unit.MEGABYTE)));
|
||||||
|
|
||||||
|
// Percent
|
||||||
|
FieldValue percentFieldValue = new FieldValue(30f, FieldValueType.PERCENT);
|
||||||
|
FieldValue percentFieldValue2 = new FieldValue(60f, FieldValueType.PERCENT);
|
||||||
|
assertThat(percentFieldValue.plus(percentFieldValue2).asString(), is("90.00%"));
|
||||||
|
assertThat(percentFieldValue.plus(percentFieldValue2).asFloat(), is(90f));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompareToIgnoreCase() {
|
||||||
|
FieldValue stringAFieldValue = new FieldValue("a", FieldValueType.STRING);
|
||||||
|
FieldValue stringCapitalAFieldValue = new FieldValue("A", FieldValueType.STRING);
|
||||||
|
FieldValue stringCapitalBFieldValue = new FieldValue("B", FieldValueType.STRING);
|
||||||
|
|
||||||
|
assertThat(stringAFieldValue.compareToIgnoreCase(stringCapitalAFieldValue), is(0));
|
||||||
|
assertThat(stringCapitalBFieldValue.compareToIgnoreCase(stringAFieldValue), is(1));
|
||||||
|
assertThat(stringAFieldValue.compareToIgnoreCase(stringCapitalBFieldValue), is(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptimizeSize() {
|
||||||
|
FieldValue sizeFieldValue =
|
||||||
|
new FieldValue(new Size(1, Size.Unit.BYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("1.0B"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(1024, Size.Unit.BYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("1.0KB"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(2 * 1024, Size.Unit.BYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("2.0KB"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(2 * 1024, Size.Unit.KILOBYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("2.0MB"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(1024 * 1024, Size.Unit.KILOBYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("1.0GB"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(2 * 1024 * 1024, Size.Unit.MEGABYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("2.0TB"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(2 * 1024, Size.Unit.TERABYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("2.0PB"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(1024 * 1024, Size.Unit.TERABYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("1024.0PB"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(1, Size.Unit.PETABYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("1.0PB"));
|
||||||
|
|
||||||
|
sizeFieldValue =
|
||||||
|
new FieldValue(new Size(1024, Size.Unit.PETABYTE), FieldValueType.SIZE);
|
||||||
|
assertThat(sizeFieldValue.asString(), is("1024.0PB"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.TestUtils;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class ModeTestBase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetRecords() {
|
||||||
|
List<Record> records = getMode().getRecords(TestUtils.createDummyClusterMetrics());
|
||||||
|
assertRecords(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Mode getMode();
|
||||||
|
protected abstract void assertRecords(List<Record> records);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDrillDown() {
|
||||||
|
List<Record> records = getMode().getRecords(TestUtils.createDummyClusterMetrics());
|
||||||
|
for (Record record : records) {
|
||||||
|
assertDrillDown(record, getMode().drillDown(record));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void assertDrillDown(Record currentRecord, DrillDownInfo drillDownInfo);
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.TestUtils;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class NamespaceModeTest extends ModeTestBase {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(NamespaceModeTest.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Mode getMode() {
|
||||||
|
return Mode.NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertRecords(List<Record> records) {
|
||||||
|
TestUtils.assertRecordsInNamespaceMode(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertDrillDown(Record currentRecord, DrillDownInfo drillDownInfo) {
|
||||||
|
assertThat(drillDownInfo.getNextMode(), is(Mode.TABLE));
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().size(), is(1));
|
||||||
|
|
||||||
|
switch (currentRecord.get(Field.NAMESPACE).asString()) {
|
||||||
|
case "default":
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(0).toString(), is("NAMESPACE==default"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "namespace":
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(0).toString(),
|
||||||
|
is("NAMESPACE==namespace"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.TestUtils;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class RegionModeTest extends ModeTestBase {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(RegionModeTest.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Mode getMode() {
|
||||||
|
return Mode.REGION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertRecords(List<Record> records) {
|
||||||
|
TestUtils.assertRecordsInRegionMode(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertDrillDown(Record currentRecord, DrillDownInfo drillDownInfo) {
|
||||||
|
assertThat(drillDownInfo, is(nullValue()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.TestUtils;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class RegionServerModeTest extends ModeTestBase {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(RegionServerModeTest.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Mode getMode() {
|
||||||
|
return Mode.REGION_SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertRecords(List<Record> records) {
|
||||||
|
TestUtils.assertRecordsInRegionServerMode(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertDrillDown(Record currentRecord, DrillDownInfo drillDownInfo) {
|
||||||
|
assertThat(drillDownInfo.getNextMode(), is(Mode.REGION));
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().size(), is(1));
|
||||||
|
|
||||||
|
switch (currentRecord.get(Field.REGION_SERVER).asString()) {
|
||||||
|
case "host1:1000":
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(0).toString(), is("RS==host1:1000"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "host2:1001":
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(0).toString(), is("RS==host2:1001"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class RequestCountPerSecondTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(RequestCountPerSecondTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
RequestCountPerSecond requestCountPerSecond = new RequestCountPerSecond();
|
||||||
|
|
||||||
|
requestCountPerSecond.refresh(1000, 300, 100, 200);
|
||||||
|
assertThat(requestCountPerSecond.getRequestCountPerSecond(), is(0L));
|
||||||
|
assertThat(requestCountPerSecond.getReadRequestCountPerSecond(), is(0L));
|
||||||
|
assertThat(requestCountPerSecond.getWriteRequestCountPerSecond(), is(0L));
|
||||||
|
assertThat(requestCountPerSecond.getFilteredReadRequestCountPerSecond(), is(0L));
|
||||||
|
|
||||||
|
requestCountPerSecond.refresh(2000, 1300, 1100, 1200);
|
||||||
|
assertThat(requestCountPerSecond.getRequestCountPerSecond(), is(2000L));
|
||||||
|
assertThat(requestCountPerSecond.getReadRequestCountPerSecond(), is(1000L));
|
||||||
|
assertThat(requestCountPerSecond.getFilteredReadRequestCountPerSecond(), is(1000L));
|
||||||
|
assertThat(requestCountPerSecond.getWriteRequestCountPerSecond(), is(1000L));
|
||||||
|
|
||||||
|
requestCountPerSecond.refresh(12000, 5300, 3100, 2200);
|
||||||
|
assertThat(requestCountPerSecond.getRequestCountPerSecond(), is(500L));
|
||||||
|
assertThat(requestCountPerSecond.getReadRequestCountPerSecond(), is(400L));
|
||||||
|
assertThat(requestCountPerSecond.getFilteredReadRequestCountPerSecond(), is(200L));
|
||||||
|
assertThat(requestCountPerSecond.getWriteRequestCountPerSecond(), is(100L));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.mode;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.TestUtils;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class TableModeTest extends ModeTestBase {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(TableModeTest.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Mode getMode() {
|
||||||
|
return Mode.TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertRecords(List<Record> records) {
|
||||||
|
TestUtils.assertRecordsInTableMode(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assertDrillDown(Record currentRecord, DrillDownInfo drillDownInfo) {
|
||||||
|
assertThat(drillDownInfo.getNextMode(), is(Mode.REGION));
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().size(), is(2));
|
||||||
|
|
||||||
|
String tableName = String.format("%s:%s", currentRecord.get(Field.NAMESPACE).asString(),
|
||||||
|
currentRecord.get(Field.TABLE).asString());
|
||||||
|
|
||||||
|
switch (tableName) {
|
||||||
|
case "default:table1":
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(0).toString(), is("NAMESPACE==default"));
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(1).toString(), is("TABLE==table1"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "default:table2":
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(0).toString(), is("NAMESPACE==default"));
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(1).toString(), is("TABLE==table2"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "namespace:table3":
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(0).toString(),
|
||||||
|
is("NAMESPACE==namespace"));
|
||||||
|
assertThat(drillDownInfo.getInitialFilters().get(1).toString(), is("TABLE==table3"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.field;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.top.TopScreenView;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class FieldScreenPresenterTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(FieldScreenPresenterTest.class);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private FieldScreenView fieldScreenView;
|
||||||
|
|
||||||
|
private int sortFieldPosition = -1;
|
||||||
|
private List<Field> fields;
|
||||||
|
private EnumMap<Field, Boolean> fieldDisplayMap;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private FieldScreenPresenter.ResultListener resultListener;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TopScreenView topScreenView;
|
||||||
|
|
||||||
|
private FieldScreenPresenter fieldScreenPresenter;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
Field sortField = Mode.REGION.getDefaultSortField();
|
||||||
|
fields = Mode.REGION.getFieldInfos().stream()
|
||||||
|
.map(FieldInfo::getField)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
fieldDisplayMap = Mode.REGION.getFieldInfos().stream()
|
||||||
|
.collect(() -> new EnumMap<>(Field.class),
|
||||||
|
(r, fi) -> r.put(fi.getField(), fi.isDisplayByDefault()), (r1, r2) -> {});
|
||||||
|
|
||||||
|
fieldScreenPresenter =
|
||||||
|
new FieldScreenPresenter(fieldScreenView, sortField, fields, fieldDisplayMap, resultListener,
|
||||||
|
topScreenView);
|
||||||
|
|
||||||
|
for (int i = 0; i < fields.size(); i++) {
|
||||||
|
Field field = fields.get(i);
|
||||||
|
if (field == sortField) {
|
||||||
|
sortFieldPosition = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInit() {
|
||||||
|
fieldScreenPresenter.init();
|
||||||
|
|
||||||
|
int modeHeaderMaxLength = "#COMPingCell".length();
|
||||||
|
int modeDescriptionMaxLength = "Filtered Read Request Count per second".length();
|
||||||
|
|
||||||
|
verify(fieldScreenView).showFieldScreen(eq("#REQ/S"), eq(fields), eq(fieldDisplayMap),
|
||||||
|
eq(sortFieldPosition), eq(modeHeaderMaxLength), eq(modeDescriptionMaxLength), eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangeSortField() {
|
||||||
|
fieldScreenPresenter.arrowUp();
|
||||||
|
fieldScreenPresenter.setSortField();
|
||||||
|
|
||||||
|
fieldScreenPresenter.arrowDown();
|
||||||
|
fieldScreenPresenter.arrowDown();
|
||||||
|
fieldScreenPresenter.setSortField();
|
||||||
|
|
||||||
|
fieldScreenPresenter.pageUp();
|
||||||
|
fieldScreenPresenter.setSortField();
|
||||||
|
|
||||||
|
fieldScreenPresenter.pageDown();
|
||||||
|
fieldScreenPresenter.setSortField();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(fieldScreenView);
|
||||||
|
inOrder.verify(fieldScreenView).showScreenDescription(eq("LRS"));
|
||||||
|
inOrder.verify(fieldScreenView).showScreenDescription(eq("#READ/S"));
|
||||||
|
inOrder.verify(fieldScreenView).showScreenDescription(eq(fields.get(0).getHeader()));
|
||||||
|
inOrder.verify(fieldScreenView).showScreenDescription(
|
||||||
|
eq(fields.get(fields.size() - 1).getHeader()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSwitchFieldDisplay() {
|
||||||
|
fieldScreenPresenter.switchFieldDisplay();
|
||||||
|
fieldScreenPresenter.switchFieldDisplay();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(fieldScreenView);
|
||||||
|
inOrder.verify(fieldScreenView).showField(anyInt(), any(), eq(false), anyBoolean(), anyInt(),
|
||||||
|
anyInt(), anyBoolean());
|
||||||
|
inOrder.verify(fieldScreenView).showField(anyInt(), any(), eq(true), anyBoolean(), anyInt(),
|
||||||
|
anyInt(), anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangeFieldsOrder() {
|
||||||
|
fieldScreenPresenter.turnOnMoveMode();
|
||||||
|
fieldScreenPresenter.arrowUp();
|
||||||
|
fieldScreenPresenter.turnOffMoveMode();
|
||||||
|
|
||||||
|
Field removed = fields.remove(sortFieldPosition);
|
||||||
|
fields.add(sortFieldPosition - 1, removed);
|
||||||
|
|
||||||
|
assertThat(fieldScreenPresenter.transitionToNextScreen(), is(topScreenView));
|
||||||
|
verify(resultListener).accept(any(), eq(fields), any());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.help;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.top.TopScreenView;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class HelpScreenPresenterTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(HelpScreenPresenterTest.class);
|
||||||
|
|
||||||
|
private static final long TEST_REFRESH_DELAY = 5;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HelpScreenView helpScreenView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TopScreenView topScreenView;
|
||||||
|
|
||||||
|
private HelpScreenPresenter helpScreenPresenter;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
helpScreenPresenter = new HelpScreenPresenter(helpScreenView, TEST_REFRESH_DELAY,
|
||||||
|
topScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInit() {
|
||||||
|
helpScreenPresenter.init();
|
||||||
|
verify(helpScreenView).showHelpScreen(eq(TEST_REFRESH_DELAY), argThat(cds -> cds.length == 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransitionToTopScreen() {
|
||||||
|
assertThat(helpScreenPresenter.transitionToNextScreen(), is(topScreenView));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.mode;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.top.TopScreenView;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class ModeScreenPresenterTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(ModeScreenPresenterTest.class);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ModeScreenView modeScreenView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TopScreenView topScreenView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Consumer<Mode> resultListener;
|
||||||
|
|
||||||
|
private ModeScreenPresenter createModeScreenPresenter(Mode currentMode) {
|
||||||
|
return new ModeScreenPresenter(modeScreenView, currentMode, resultListener, topScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInit() {
|
||||||
|
ModeScreenPresenter modeScreenPresenter = createModeScreenPresenter(Mode.REGION);
|
||||||
|
|
||||||
|
modeScreenPresenter.init();
|
||||||
|
|
||||||
|
int modeHeaderMaxLength = Mode.REGION_SERVER.getHeader().length();
|
||||||
|
int modeDescriptionMaxLength = Mode.REGION_SERVER.getDescription().length();
|
||||||
|
|
||||||
|
verify(modeScreenView).showModeScreen(eq(Mode.REGION), eq(Arrays.asList(Mode.values())),
|
||||||
|
eq(Mode.REGION.ordinal()) , eq(modeHeaderMaxLength), eq(modeDescriptionMaxLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelectNamespaceMode() {
|
||||||
|
ModeScreenPresenter modeScreenPresenter = createModeScreenPresenter(Mode.REGION);
|
||||||
|
|
||||||
|
modeScreenPresenter.arrowUp();
|
||||||
|
modeScreenPresenter.arrowUp();
|
||||||
|
|
||||||
|
assertThat(modeScreenPresenter.transitionToNextScreen(true), is(topScreenView));
|
||||||
|
verify(resultListener).accept(eq(Mode.NAMESPACE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelectTableMode() {
|
||||||
|
ModeScreenPresenter modeScreenPresenter = createModeScreenPresenter(Mode.REGION);
|
||||||
|
|
||||||
|
modeScreenPresenter.arrowUp();
|
||||||
|
assertThat(modeScreenPresenter.transitionToNextScreen(true), is(topScreenView));
|
||||||
|
verify(resultListener).accept(eq(Mode.TABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelectRegionMode() {
|
||||||
|
ModeScreenPresenter modeScreenPresenter = createModeScreenPresenter(Mode.NAMESPACE);
|
||||||
|
|
||||||
|
modeScreenPresenter.arrowDown();
|
||||||
|
modeScreenPresenter.arrowDown();
|
||||||
|
|
||||||
|
assertThat(modeScreenPresenter.transitionToNextScreen(true), is(topScreenView));
|
||||||
|
verify(resultListener).accept(eq(Mode.REGION));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelectRegionServerMode() {
|
||||||
|
ModeScreenPresenter modeScreenPresenter = createModeScreenPresenter(Mode.REGION);
|
||||||
|
|
||||||
|
modeScreenPresenter.arrowDown();
|
||||||
|
|
||||||
|
assertThat(modeScreenPresenter.transitionToNextScreen(true), is(topScreenView));
|
||||||
|
verify(resultListener).accept(eq(Mode.REGION_SERVER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCancelSelectingMode() {
|
||||||
|
ModeScreenPresenter modeScreenPresenter = createModeScreenPresenter(Mode.REGION);
|
||||||
|
|
||||||
|
modeScreenPresenter.arrowDown();
|
||||||
|
modeScreenPresenter.arrowDown();
|
||||||
|
|
||||||
|
assertThat(modeScreenPresenter.transitionToNextScreen(false), is(topScreenView));
|
||||||
|
verify(resultListener, never()).accept(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPageUp() {
|
||||||
|
ModeScreenPresenter modeScreenPresenter = createModeScreenPresenter(Mode.REGION);
|
||||||
|
|
||||||
|
modeScreenPresenter.pageUp();
|
||||||
|
|
||||||
|
assertThat(modeScreenPresenter.transitionToNextScreen(true), is(topScreenView));
|
||||||
|
verify(resultListener).accept(eq(Mode.values()[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPageDown() {
|
||||||
|
ModeScreenPresenter modeScreenPresenter = createModeScreenPresenter(Mode.REGION);
|
||||||
|
|
||||||
|
modeScreenPresenter.pageDown();
|
||||||
|
|
||||||
|
assertThat(modeScreenPresenter.transitionToNextScreen(true), is(topScreenView));
|
||||||
|
Mode[] modes = Mode.values();
|
||||||
|
verify(resultListener).accept(eq(modes[modes.length - 1]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class FilterDisplayModeScreenPresenterTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(FilterDisplayModeScreenPresenterTest.class);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private FilterDisplayModeScreenView filterDisplayModeScreenView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TopScreenView topScreenView;
|
||||||
|
|
||||||
|
private FilterDisplayModeScreenPresenter filterDisplayModeScreenPresenter;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
List<Field> fields = Mode.REGION.getFieldInfos().stream()
|
||||||
|
.map(FieldInfo::getField)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<RecordFilter> filters = new ArrayList<>();
|
||||||
|
filters.add(RecordFilter.parse("NAMESPACE==namespace", fields, true));
|
||||||
|
filters.add(RecordFilter.parse("TABLE==table", fields, true));
|
||||||
|
|
||||||
|
filterDisplayModeScreenPresenter = new FilterDisplayModeScreenPresenter(
|
||||||
|
filterDisplayModeScreenView, filters, topScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInit() {
|
||||||
|
filterDisplayModeScreenPresenter.init();
|
||||||
|
verify(filterDisplayModeScreenView).showFilters(argThat(filters -> filters.size() == 2
|
||||||
|
&& filters.get(0).toString().equals("NAMESPACE==namespace")
|
||||||
|
&& filters.get(1).toString().equals("TABLE==table")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReturnToTopScreen() {
|
||||||
|
assertThat(filterDisplayModeScreenPresenter.returnToNextScreen(), is(topScreenView));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class InputModeScreenPresenterTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(InputModeScreenPresenterTest.class);
|
||||||
|
|
||||||
|
private static final String TEST_INPUT_MESSAGE = "test input message";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private InputModeScreenView inputModeScreenView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TopScreenView topScreenView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Function<String, ScreenView> resultListener;
|
||||||
|
|
||||||
|
private InputModeScreenPresenter inputModeScreenPresenter;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
List<String> histories = new ArrayList<>();
|
||||||
|
histories.add("history1");
|
||||||
|
histories.add("history2");
|
||||||
|
|
||||||
|
inputModeScreenPresenter = new InputModeScreenPresenter(inputModeScreenView,
|
||||||
|
TEST_INPUT_MESSAGE, histories, resultListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInit() {
|
||||||
|
inputModeScreenPresenter.init();
|
||||||
|
|
||||||
|
verify(inputModeScreenView).showInput(eq(TEST_INPUT_MESSAGE), eq(""), eq(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCharacter() {
|
||||||
|
inputModeScreenPresenter.character('a');
|
||||||
|
inputModeScreenPresenter.character('b');
|
||||||
|
inputModeScreenPresenter.character('c');
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(inputModeScreenView);
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("a"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("ab"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArrowLeftAndRight() {
|
||||||
|
inputModeScreenPresenter.character('a');
|
||||||
|
inputModeScreenPresenter.character('b');
|
||||||
|
inputModeScreenPresenter.character('c');
|
||||||
|
inputModeScreenPresenter.arrowLeft();
|
||||||
|
inputModeScreenPresenter.arrowLeft();
|
||||||
|
inputModeScreenPresenter.arrowLeft();
|
||||||
|
inputModeScreenPresenter.arrowLeft();
|
||||||
|
inputModeScreenPresenter.arrowRight();
|
||||||
|
inputModeScreenPresenter.arrowRight();
|
||||||
|
inputModeScreenPresenter.arrowRight();
|
||||||
|
inputModeScreenPresenter.arrowRight();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(inputModeScreenView);
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("a"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("ab"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(3));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(0));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHomeAndEnd() {
|
||||||
|
inputModeScreenPresenter.character('a');
|
||||||
|
inputModeScreenPresenter.character('b');
|
||||||
|
inputModeScreenPresenter.character('c');
|
||||||
|
inputModeScreenPresenter.home();
|
||||||
|
inputModeScreenPresenter.home();
|
||||||
|
inputModeScreenPresenter.end();
|
||||||
|
inputModeScreenPresenter.end();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(inputModeScreenView);
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("a"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("ab"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(3));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(0));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBackspace() {
|
||||||
|
inputModeScreenPresenter.character('a');
|
||||||
|
inputModeScreenPresenter.character('b');
|
||||||
|
inputModeScreenPresenter.character('c');
|
||||||
|
inputModeScreenPresenter.backspace();
|
||||||
|
inputModeScreenPresenter.backspace();
|
||||||
|
inputModeScreenPresenter.backspace();
|
||||||
|
inputModeScreenPresenter.backspace();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(inputModeScreenView);
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("a"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("ab"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(3));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("ab"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("a"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq(""), eq(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDelete() {
|
||||||
|
inputModeScreenPresenter.character('a');
|
||||||
|
inputModeScreenPresenter.character('b');
|
||||||
|
inputModeScreenPresenter.character('c');
|
||||||
|
inputModeScreenPresenter.delete();
|
||||||
|
inputModeScreenPresenter.arrowLeft();
|
||||||
|
inputModeScreenPresenter.delete();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(inputModeScreenView);
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("a"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("ab"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(3));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("ab"), eq(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistories() {
|
||||||
|
inputModeScreenPresenter.character('a');
|
||||||
|
inputModeScreenPresenter.character('b');
|
||||||
|
inputModeScreenPresenter.character('c');
|
||||||
|
inputModeScreenPresenter.arrowUp();
|
||||||
|
inputModeScreenPresenter.arrowUp();
|
||||||
|
inputModeScreenPresenter.arrowUp();
|
||||||
|
inputModeScreenPresenter.arrowDown();
|
||||||
|
inputModeScreenPresenter.arrowDown();
|
||||||
|
inputModeScreenPresenter.arrowDown();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(inputModeScreenView);
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("a"), eq(1));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("ab"), eq(2));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("abc"), eq(3));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("history2"), eq(8));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("history1"), eq(8));
|
||||||
|
inOrder.verify(inputModeScreenView).showInput(any(), eq("history2"), eq(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReturnToTopScreen() {
|
||||||
|
when(resultListener.apply(any())).thenReturn(topScreenView);
|
||||||
|
|
||||||
|
inputModeScreenPresenter.character('a');
|
||||||
|
inputModeScreenPresenter.character('b');
|
||||||
|
inputModeScreenPresenter.character('c');
|
||||||
|
|
||||||
|
assertThat(inputModeScreenPresenter.returnToNextScreen(), is(topScreenView));
|
||||||
|
verify(resultListener).apply(eq("abc"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class MessageModeScreenPresenterTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(MessageModeScreenPresenterTest.class);
|
||||||
|
|
||||||
|
private static final String TEST_MESSAGE = "test message";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MessageModeScreenView messageModeScreenView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TopScreenView topScreenView;
|
||||||
|
|
||||||
|
private MessageModeScreenPresenter messageModeScreenPresenter;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
messageModeScreenPresenter = new MessageModeScreenPresenter(messageModeScreenView,
|
||||||
|
TEST_MESSAGE, topScreenView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInit() {
|
||||||
|
messageModeScreenPresenter.init();
|
||||||
|
|
||||||
|
verify(messageModeScreenView).showMessage(eq(TEST_MESSAGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReturnToTopScreen() {
|
||||||
|
assertThat(messageModeScreenPresenter.returnToNextScreen(), is(topScreenView));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,300 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
public class PagingTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(PagingTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArrowUpAndArrowDown() {
|
||||||
|
Paging paging = new Paging();
|
||||||
|
paging.updatePageSize(3);
|
||||||
|
paging.updateRecordsSize(5);
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 1, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 2, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 3, 1, 4);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 4, 2, 5);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 4, 2, 5);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 3, 2, 5);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 2, 2, 5);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 1, 1, 4);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPageUpAndPageDown() {
|
||||||
|
Paging paging = new Paging();
|
||||||
|
paging.updatePageSize(3);
|
||||||
|
paging.updateRecordsSize(8);
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 3, 3, 6);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 6, 5, 8);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 7, 5, 8);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 7, 5, 8);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 4, 4, 7);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 1, 1, 4);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInit() {
|
||||||
|
Paging paging = new Paging();
|
||||||
|
paging.updatePageSize(3);
|
||||||
|
paging.updateRecordsSize(5);
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
paging.pageDown();
|
||||||
|
paging.pageDown();
|
||||||
|
paging.pageDown();
|
||||||
|
paging.init();
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenPageSizeGraterThanRecordsSize() {
|
||||||
|
Paging paging = new Paging();
|
||||||
|
paging.updatePageSize(5);
|
||||||
|
paging.updateRecordsSize(3);
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 1, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 2, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 2, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 1, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 2, 0, 3);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 2, 0, 3);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenPageSizeIsZero() {
|
||||||
|
Paging paging = new Paging();
|
||||||
|
paging.updatePageSize(0);
|
||||||
|
paging.updateRecordsSize(5);
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 1, 0, 0);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenRecordsSizeIsZero() {
|
||||||
|
Paging paging = new Paging();
|
||||||
|
paging.updatePageSize(3);
|
||||||
|
paging.updateRecordsSize(0);
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenChangingPageSizeDynamically() {
|
||||||
|
Paging paging = new Paging();
|
||||||
|
paging.updatePageSize(3);
|
||||||
|
paging.updateRecordsSize(5);
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 1, 0, 3);
|
||||||
|
|
||||||
|
paging.updatePageSize(2);
|
||||||
|
assertPaging(paging, 1, 0, 2);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 2, 1, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 3, 2, 4);
|
||||||
|
|
||||||
|
paging.updatePageSize(4);
|
||||||
|
assertPaging(paging, 3, 1, 5);
|
||||||
|
|
||||||
|
paging.updatePageSize(5);
|
||||||
|
assertPaging(paging, 3, 0, 5);
|
||||||
|
|
||||||
|
paging.updatePageSize(0);
|
||||||
|
assertPaging(paging, 3, 0, 0);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 4, 0, 0);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 3, 0, 0);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 3, 0, 0);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 3, 0, 0);
|
||||||
|
|
||||||
|
paging.updatePageSize(1);
|
||||||
|
assertPaging(paging, 3, 3, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenChangingRecordsSizeDynamically() {
|
||||||
|
Paging paging = new Paging();
|
||||||
|
paging.updatePageSize(3);
|
||||||
|
paging.updateRecordsSize(5);
|
||||||
|
|
||||||
|
assertPaging(paging, 0, 0, 3);
|
||||||
|
|
||||||
|
paging.updateRecordsSize(2);
|
||||||
|
assertPaging(paging, 0, 0, 2);
|
||||||
|
assertThat(paging.getCurrentPosition(), is(0));
|
||||||
|
assertThat(paging.getPageStartPosition(), is(0));
|
||||||
|
assertThat(paging.getPageEndPosition(), is(2));
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 1, 0, 2);
|
||||||
|
|
||||||
|
paging.updateRecordsSize(3);
|
||||||
|
assertPaging(paging, 1, 0, 3);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 2, 0, 3);
|
||||||
|
|
||||||
|
paging.updateRecordsSize(1);
|
||||||
|
assertPaging(paging, 0, 0, 1);
|
||||||
|
|
||||||
|
paging.updateRecordsSize(0);
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.arrowDown();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.arrowUp();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.pageDown();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
|
||||||
|
paging.pageUp();
|
||||||
|
assertPaging(paging, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertPaging(Paging paging, int currentPosition, int pageStartPosition,
|
||||||
|
int pageEndPosition) {
|
||||||
|
assertThat(paging.getCurrentPosition(), is(currentPosition));
|
||||||
|
assertThat(paging.getPageStartPosition(), is(pageStartPosition));
|
||||||
|
assertThat(paging.getPageEndPosition(), is(pageEndPosition));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.client.Admin;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.RecordFilter;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.TestUtils;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldValue;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.mode.Mode;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class TopScreenModelTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(TopScreenModelTest.class);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Admin admin;
|
||||||
|
|
||||||
|
private TopScreenModel topScreenModel;
|
||||||
|
|
||||||
|
private List<Field> fields;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws IOException {
|
||||||
|
when(admin.getClusterMetrics()).thenReturn(TestUtils.createDummyClusterMetrics());
|
||||||
|
topScreenModel = new TopScreenModel(admin, Mode.REGION);
|
||||||
|
|
||||||
|
fields = Mode.REGION.getFieldInfos().stream()
|
||||||
|
.map(FieldInfo::getField)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSummary() {
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
Summary summary = topScreenModel.getSummary();
|
||||||
|
TestUtils.assertSummary(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRecords() {
|
||||||
|
// Region Mode
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
TestUtils.assertRecordsInRegionMode(topScreenModel.getRecords());
|
||||||
|
|
||||||
|
// Namespace Mode
|
||||||
|
topScreenModel.switchMode(Mode.NAMESPACE, null, false);
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
TestUtils.assertRecordsInNamespaceMode(topScreenModel.getRecords());
|
||||||
|
|
||||||
|
// Table Mode
|
||||||
|
topScreenModel.switchMode(Mode.TABLE, null, false);
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
TestUtils.assertRecordsInTableMode(topScreenModel.getRecords());
|
||||||
|
|
||||||
|
// Namespace Mode
|
||||||
|
topScreenModel.switchMode(Mode.REGION_SERVER, null, false);
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
TestUtils.assertRecordsInRegionServerMode(topScreenModel.getRecords());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSort() {
|
||||||
|
// The sort key is LOCALITY
|
||||||
|
topScreenModel.setSortFieldAndFields(Field.LOCALITY, fields);
|
||||||
|
|
||||||
|
FieldValue previous = null;
|
||||||
|
|
||||||
|
// Test for ascending sort
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
|
||||||
|
for (Record record : topScreenModel.getRecords()) {
|
||||||
|
FieldValue current = record.get(Field.LOCALITY);
|
||||||
|
if (previous != null) {
|
||||||
|
assertTrue(current.compareTo(previous) < 0);
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for descending sort
|
||||||
|
topScreenModel.switchSortOrder();
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
|
||||||
|
previous = null;
|
||||||
|
for (Record record : topScreenModel.getRecords()) {
|
||||||
|
FieldValue current = record.get(Field.LOCALITY);
|
||||||
|
if (previous != null) {
|
||||||
|
assertTrue(current.compareTo(previous) > 0);
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilters() {
|
||||||
|
topScreenModel.addFilter("TABLE==table1", false);
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
for (Record record : topScreenModel.getRecords()) {
|
||||||
|
FieldValue value = record.get(Field.TABLE);
|
||||||
|
assertThat(value.asString(), is("table1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
topScreenModel.clearFilters();
|
||||||
|
topScreenModel.addFilter("TABLE==TABLE1", false);
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
assertThat(topScreenModel.getRecords().size(), is(0));
|
||||||
|
|
||||||
|
// Test for ignore case
|
||||||
|
topScreenModel.clearFilters();
|
||||||
|
topScreenModel.addFilter("TABLE==TABLE1", true);
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
for (Record record : topScreenModel.getRecords()) {
|
||||||
|
FieldValue value = record.get(Field.TABLE);
|
||||||
|
assertThat(value.asString(), is("table1"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterHistories() {
|
||||||
|
topScreenModel.addFilter("TABLE==table1", false);
|
||||||
|
topScreenModel.addFilter("TABLE==table2", false);
|
||||||
|
topScreenModel.addFilter("TABLE==table3", false);
|
||||||
|
|
||||||
|
assertThat(topScreenModel.getFilterHistories().get(0), is("TABLE==table1"));
|
||||||
|
assertThat(topScreenModel.getFilterHistories().get(1), is("TABLE==table2"));
|
||||||
|
assertThat(topScreenModel.getFilterHistories().get(2), is("TABLE==table3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSwitchMode() {
|
||||||
|
topScreenModel.switchMode(Mode.TABLE, null, false);
|
||||||
|
assertThat(topScreenModel.getCurrentMode(), is(Mode.TABLE));
|
||||||
|
|
||||||
|
// Test for initialFilters
|
||||||
|
List<RecordFilter> initialFilters = Arrays.asList(
|
||||||
|
RecordFilter.parse("TABLE==table1", fields, true),
|
||||||
|
RecordFilter.parse("TABLE==table2", fields, true));
|
||||||
|
|
||||||
|
topScreenModel.switchMode(Mode.TABLE, initialFilters, false);
|
||||||
|
|
||||||
|
assertThat(topScreenModel.getFilters().size(), is(initialFilters.size()));
|
||||||
|
for (int i = 0; i < topScreenModel.getFilters().size(); i++) {
|
||||||
|
assertThat(topScreenModel.getFilters().get(i).toString(),
|
||||||
|
is(initialFilters.get(i).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test when keepSortFieldAndSortOrderIfPossible is true
|
||||||
|
topScreenModel.setSortFieldAndFields(Field.NAMESPACE, fields);
|
||||||
|
topScreenModel.switchMode(Mode.NAMESPACE, null, true);
|
||||||
|
assertThat(topScreenModel.getCurrentSortField(), is(Field.NAMESPACE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDrillDown() {
|
||||||
|
topScreenModel.switchMode(Mode.TABLE, null, false);
|
||||||
|
topScreenModel.setSortFieldAndFields(Field.NAMESPACE, fields);
|
||||||
|
topScreenModel.refreshMetricsData();
|
||||||
|
|
||||||
|
boolean success = topScreenModel.drillDown(topScreenModel.getRecords().get(0));
|
||||||
|
assertThat(success, is(true));
|
||||||
|
|
||||||
|
assertThat(topScreenModel.getFilters().get(0).toString(), is("NAMESPACE==namespace"));
|
||||||
|
assertThat(topScreenModel.getFilters().get(1).toString(), is("TABLE==table3"));
|
||||||
|
assertThat(topScreenModel.getCurrentSortField(), is(Field.NAMESPACE));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,256 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.screen.top;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hbase.hbtop.Record.entry;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.hadoop.hbase.HBaseClassTestRule;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.Record;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.Field;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
|
||||||
|
@Category(SmallTests.class)
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class TopScreenPresenterTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static final HBaseClassTestRule CLASS_RULE =
|
||||||
|
HBaseClassTestRule.forClass(TopScreenPresenterTest.class);
|
||||||
|
|
||||||
|
private static final List<FieldInfo> TEST_FIELD_INFOS = Arrays.asList(
|
||||||
|
new FieldInfo(Field.REGION, 10, true),
|
||||||
|
new FieldInfo(Field.REQUEST_COUNT_PER_SECOND, 10, true),
|
||||||
|
new FieldInfo(Field.LOCALITY, 10, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final List<Record> TEST_RECORDS = Arrays.asList(
|
||||||
|
Record.ofEntries(
|
||||||
|
entry(Field.REGION, "region1"),
|
||||||
|
entry(Field.REQUEST_COUNT_PER_SECOND, 1L),
|
||||||
|
entry(Field.LOCALITY, 0.3f)),
|
||||||
|
Record.ofEntries(
|
||||||
|
entry(Field.REGION, "region2"),
|
||||||
|
entry(Field.REQUEST_COUNT_PER_SECOND, 2L),
|
||||||
|
entry(Field.LOCALITY, 0.2f)),
|
||||||
|
Record.ofEntries(
|
||||||
|
entry(Field.REGION, "region3"),
|
||||||
|
entry(Field.REQUEST_COUNT_PER_SECOND, 3L),
|
||||||
|
entry(Field.LOCALITY, 0.1f))
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final Summary TEST_SUMMARY = new Summary(
|
||||||
|
"00:00:01", "3.0.0-SNAPSHOT", "01234567-89ab-cdef-0123-456789abcdef",
|
||||||
|
3, 2, 1, 6, 1, 3.0, 300);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TopScreenView topScreenView;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TopScreenModel topScreenModel;
|
||||||
|
|
||||||
|
private TopScreenPresenter topScreenPresenter;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
when(topScreenView.getTerminalSize()).thenReturn(new TerminalSize(100, 100));
|
||||||
|
when(topScreenView.getPageSize()).thenReturn(100);
|
||||||
|
|
||||||
|
when(topScreenModel.getFieldInfos()).thenReturn(TEST_FIELD_INFOS);
|
||||||
|
when(topScreenModel.getFields()).thenReturn(TEST_FIELD_INFOS.stream()
|
||||||
|
.map(FieldInfo::getField).collect(Collectors.toList()));
|
||||||
|
when(topScreenModel.getRecords()).thenReturn(TEST_RECORDS);
|
||||||
|
when(topScreenModel.getSummary()).thenReturn(TEST_SUMMARY);
|
||||||
|
|
||||||
|
topScreenPresenter = new TopScreenPresenter(topScreenView, 3000, topScreenModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRefresh() {
|
||||||
|
topScreenPresenter.init();
|
||||||
|
topScreenPresenter.refresh(true);
|
||||||
|
|
||||||
|
verify(topScreenView).showTopScreen(argThat(this::assertSummary),
|
||||||
|
argThat(this::assertHeaders), argThat(this::assertRecords),
|
||||||
|
argThat(selectedRecord -> assertSelectedRecord(selectedRecord, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerticalScrolling() {
|
||||||
|
topScreenPresenter.init();
|
||||||
|
topScreenPresenter.refresh(true);
|
||||||
|
|
||||||
|
topScreenPresenter.arrowDown();
|
||||||
|
topScreenPresenter.arrowDown();
|
||||||
|
topScreenPresenter.arrowDown();
|
||||||
|
|
||||||
|
topScreenPresenter.arrowDown();
|
||||||
|
topScreenPresenter.arrowDown();
|
||||||
|
topScreenPresenter.arrowDown();
|
||||||
|
|
||||||
|
topScreenPresenter.arrowUp();
|
||||||
|
topScreenPresenter.arrowUp();
|
||||||
|
topScreenPresenter.arrowUp();
|
||||||
|
|
||||||
|
topScreenPresenter.pageDown();
|
||||||
|
topScreenPresenter.pageDown();
|
||||||
|
|
||||||
|
topScreenPresenter.pageUp();
|
||||||
|
topScreenPresenter.pageUp();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(topScreenView);
|
||||||
|
verifyVerticalScrolling(inOrder, 0);
|
||||||
|
|
||||||
|
verifyVerticalScrolling(inOrder, 1);
|
||||||
|
verifyVerticalScrolling(inOrder, 2);
|
||||||
|
verifyVerticalScrolling(inOrder, 2);
|
||||||
|
|
||||||
|
verifyVerticalScrolling(inOrder, 1);
|
||||||
|
verifyVerticalScrolling(inOrder, 0);
|
||||||
|
verifyVerticalScrolling(inOrder, 0);
|
||||||
|
|
||||||
|
verifyVerticalScrolling(inOrder, 2);
|
||||||
|
verifyVerticalScrolling(inOrder, 2);
|
||||||
|
|
||||||
|
verifyVerticalScrolling(inOrder, 0);
|
||||||
|
verifyVerticalScrolling(inOrder, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyVerticalScrolling(InOrder inOrder, int expectedSelectedRecodeIndex) {
|
||||||
|
inOrder.verify(topScreenView).showTopScreen(any(), any(), any(),
|
||||||
|
argThat(selectedRecord -> assertSelectedRecord(selectedRecord, expectedSelectedRecodeIndex)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHorizontalScrolling() {
|
||||||
|
topScreenPresenter.init();
|
||||||
|
topScreenPresenter.refresh(true);
|
||||||
|
|
||||||
|
topScreenPresenter.arrowRight();
|
||||||
|
topScreenPresenter.arrowRight();
|
||||||
|
topScreenPresenter.arrowRight();
|
||||||
|
|
||||||
|
topScreenPresenter.arrowLeft();
|
||||||
|
topScreenPresenter.arrowLeft();
|
||||||
|
topScreenPresenter.arrowLeft();
|
||||||
|
|
||||||
|
topScreenPresenter.end();
|
||||||
|
topScreenPresenter.end();
|
||||||
|
|
||||||
|
topScreenPresenter.home();
|
||||||
|
topScreenPresenter.home();
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(topScreenView);
|
||||||
|
verifyHorizontalScrolling(inOrder, 3);
|
||||||
|
|
||||||
|
verifyHorizontalScrolling(inOrder, 2);
|
||||||
|
verifyHorizontalScrolling(inOrder, 1);
|
||||||
|
verifyHorizontalScrolling(inOrder, 1);
|
||||||
|
|
||||||
|
verifyHorizontalScrolling(inOrder, 2);
|
||||||
|
verifyHorizontalScrolling(inOrder, 3);
|
||||||
|
verifyHorizontalScrolling(inOrder, 3);
|
||||||
|
|
||||||
|
verifyHorizontalScrolling(inOrder, 1);
|
||||||
|
verifyHorizontalScrolling(inOrder, 1);
|
||||||
|
|
||||||
|
verifyHorizontalScrolling(inOrder, 3);
|
||||||
|
verifyHorizontalScrolling(inOrder, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyHorizontalScrolling(InOrder inOrder, int expectedHeaderCount) {
|
||||||
|
inOrder.verify(topScreenView).showTopScreen(any(),
|
||||||
|
argThat(headers -> headers.size() == expectedHeaderCount), any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean assertSummary(Summary actual) {
|
||||||
|
return actual.getCurrentTime().equals(TEST_SUMMARY.getCurrentTime())
|
||||||
|
&& actual.getVersion().equals(TEST_SUMMARY.getVersion())
|
||||||
|
&& actual.getClusterId().equals(TEST_SUMMARY.getClusterId())
|
||||||
|
&& actual.getServers() == TEST_SUMMARY.getServers()
|
||||||
|
&& actual.getLiveServers() == TEST_SUMMARY.getLiveServers()
|
||||||
|
&& actual.getDeadServers() == TEST_SUMMARY.getDeadServers()
|
||||||
|
&& actual.getRegionCount() == TEST_SUMMARY.getRegionCount()
|
||||||
|
&& actual.getRitCount() == TEST_SUMMARY.getRitCount()
|
||||||
|
&& actual.getAverageLoad() == TEST_SUMMARY.getAverageLoad()
|
||||||
|
&& actual.getAggregateRequestPerSecond() == TEST_SUMMARY.getAggregateRequestPerSecond();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean assertHeaders(List<Header> actual) {
|
||||||
|
List<Header> expected =
|
||||||
|
TEST_FIELD_INFOS.stream().map(fi -> new Header(fi.getField(), fi.getDefaultLength()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (actual.size() != expected.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < actual.size(); i++) {
|
||||||
|
if (actual.get(i).getField() != expected.get(i).getField()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (actual.get(i).getLength() != expected.get(i).getLength()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean assertRecords(List<Record> actual) {
|
||||||
|
if (actual.size() != TEST_RECORDS.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < actual.size(); i++) {
|
||||||
|
if (!assertRecord(actual.get(i), TEST_RECORDS.get(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean assertSelectedRecord(Record actual, int expectedSelectedRecodeIndex) {
|
||||||
|
return assertRecord(actual, TEST_RECORDS.get(expectedSelectedRecodeIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean assertRecord(Record actual, Record expected) {
|
||||||
|
return actual.get(Field.REGION).equals(expected.get(Field.REGION)) && actual
|
||||||
|
.get(Field.REQUEST_COUNT_PER_SECOND).equals(expected.get(Field.REQUEST_COUNT_PER_SECOND))
|
||||||
|
&& actual.get(Field.LOCALITY).equals(expected.get(Field.LOCALITY));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.impl.TerminalImpl;
|
||||||
|
|
||||||
|
|
||||||
|
public final class CursorTest {
|
||||||
|
|
||||||
|
private CursorTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
try (Terminal terminal = new TerminalImpl()) {
|
||||||
|
terminal.refresh();
|
||||||
|
terminal.setCursorPosition(0, 0);
|
||||||
|
|
||||||
|
terminal.getTerminalPrinter(0).print("aaa").endOfLine();
|
||||||
|
terminal.refresh();
|
||||||
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
|
||||||
|
terminal.getTerminalPrinter(0).print("bbb").endOfLine();
|
||||||
|
terminal.refresh();
|
||||||
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
|
||||||
|
terminal.setCursorPosition(1, 0);
|
||||||
|
terminal.refresh();
|
||||||
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
|
||||||
|
terminal.setCursorPosition(2, 0);
|
||||||
|
terminal.refresh();
|
||||||
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
|
||||||
|
terminal.setCursorPosition(3, 0);
|
||||||
|
terminal.refresh();
|
||||||
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
|
||||||
|
terminal.setCursorPosition(0, 1);
|
||||||
|
terminal.refresh();
|
||||||
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
|
||||||
|
terminal.getTerminalPrinter(1).print("ccc").endOfLine();
|
||||||
|
terminal.refresh();
|
||||||
|
TimeUnit.SECONDS.sleep(1);
|
||||||
|
|
||||||
|
terminal.getTerminalPrinter(3).print("Press any key to finish").endOfLine();
|
||||||
|
terminal.refresh();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
KeyPress keyPress = terminal.pollKeyPress();
|
||||||
|
if (keyPress == null) {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(100);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.impl.TerminalImpl;
|
||||||
|
|
||||||
|
|
||||||
|
public final class KeyPressTest {
|
||||||
|
|
||||||
|
private KeyPressTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
try (Terminal terminal = new TerminalImpl()) {
|
||||||
|
terminal.hideCursor();
|
||||||
|
terminal.refresh();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
KeyPress keyPress = terminal.pollKeyPress();
|
||||||
|
if (keyPress == null) {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(100);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
terminal.getTerminalPrinter(0).print(keyPress.toString()).endOfLine();
|
||||||
|
terminal.refresh();
|
||||||
|
|
||||||
|
if (keyPress.getType() == KeyPress.Type.F12) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 org.apache.hadoop.hbase.hbtop.terminal;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.apache.hadoop.hbase.hbtop.terminal.impl.TerminalImpl;
|
||||||
|
|
||||||
|
|
||||||
|
public final class TerminalPrinterTest {
|
||||||
|
|
||||||
|
private TerminalPrinterTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
try (Terminal terminal = new TerminalImpl()) {
|
||||||
|
terminal.hideCursor();
|
||||||
|
terminal.refresh();
|
||||||
|
|
||||||
|
TerminalPrinter printer = terminal.getTerminalPrinter(0);
|
||||||
|
printer.print("Normal string").endOfLine();
|
||||||
|
printer.startHighlight().print("Highlighted string").stopHighlight().endOfLine();
|
||||||
|
printer.startBold().print("Bold string").stopBold().endOfLine();
|
||||||
|
printer.startHighlight().startBold().print("Highlighted bold string")
|
||||||
|
.stopBold().stopHighlight().endOfLine();
|
||||||
|
printer.endOfLine();
|
||||||
|
printer.print("Press any key to finish").endOfLine();
|
||||||
|
|
||||||
|
terminal.refresh();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
KeyPress keyPress = terminal.pollKeyPress();
|
||||||
|
if (keyPress == null) {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(100);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
pom.xml
|
@ -89,6 +89,7 @@
|
||||||
<module>hbase-metrics-api</module>
|
<module>hbase-metrics-api</module>
|
||||||
<module>hbase-metrics</module>
|
<module>hbase-metrics</module>
|
||||||
<module>hbase-zookeeper</module>
|
<module>hbase-zookeeper</module>
|
||||||
|
<module>hbase-hbtop</module>
|
||||||
</modules>
|
</modules>
|
||||||
<scm>
|
<scm>
|
||||||
<connection>scm:git:git://gitbox.apache.org/repos/asf/hbase.git</connection>
|
<connection>scm:git:git://gitbox.apache.org/repos/asf/hbase.git</connection>
|
||||||
|
@ -1655,6 +1656,11 @@
|
||||||
<type>test-jar</type>
|
<type>test-jar</type>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<artifactId>hbase-hbtop</artifactId>
|
||||||
|
<groupId>org.apache.hbase</groupId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.hbase</groupId>
|
<groupId>org.apache.hbase</groupId>
|
||||||
<artifactId>hbase-shaded-client</artifactId>
|
<artifactId>hbase-shaded-client</artifactId>
|
||||||
|
|