1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.hbtop.screen.top;
19
20 import edu.umd.cs.findbugs.annotations.Nullable;
21 import java.util.ArrayList;
22 import java.util.EnumMap;
23 import java.util.List;
24 import java.util.Objects;
25 import java.util.concurrent.atomic.AtomicBoolean;
26 import java.util.concurrent.atomic.AtomicLong;
27 import org.apache.hadoop.hbase.classification.InterfaceAudience;
28 import org.apache.hadoop.hbase.hbtop.Record;
29 import org.apache.hadoop.hbase.hbtop.field.Field;
30 import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
31 import org.apache.hadoop.hbase.hbtop.mode.Mode;
32 import org.apache.hadoop.hbase.hbtop.screen.Screen;
33 import org.apache.hadoop.hbase.hbtop.screen.ScreenView;
34 import org.apache.hadoop.hbase.hbtop.screen.field.FieldScreenPresenter;
35 import org.apache.hadoop.hbase.hbtop.screen.field.FieldScreenView;
36 import org.apache.hadoop.hbase.hbtop.screen.help.HelpScreenView;
37 import org.apache.hadoop.hbase.hbtop.screen.mode.ModeScreenPresenter;
38 import org.apache.hadoop.hbase.hbtop.screen.mode.ModeScreenView;
39 import org.apache.hadoop.hbase.hbtop.terminal.Terminal;
40 import org.apache.hadoop.hbase.hbtop.terminal.TerminalSize;
41
42
43
44
45 @InterfaceAudience.Private
46 public class TopScreenPresenter {
47 private final TopScreenView topScreenView;
48 private final AtomicLong refreshDelay;
49 private long lastRefreshTimestamp;
50
51 private final AtomicBoolean adjustFieldLength = new AtomicBoolean(true);
52 private final TopScreenModel topScreenModel;
53 private int terminalLength;
54 private int horizontalScroll;
55 private final Paging paging = new Paging();
56
57 private final EnumMap<Field, Boolean> fieldDisplayMap = new EnumMap<>(Field.class);
58 private final EnumMap<Field, Integer> fieldLengthMap = new EnumMap<>(Field.class);
59
60 private final long numberOfIterations;
61 private long iterations;
62
63 public TopScreenPresenter(TopScreenView topScreenView, long initialRefreshDelay,
64 TopScreenModel topScreenModel, @Nullable List<Field> initialFields, long numberOfIterations) {
65 this.topScreenView = Objects.requireNonNull(topScreenView);
66 this.refreshDelay = new AtomicLong(initialRefreshDelay);
67 this.topScreenModel = Objects.requireNonNull(topScreenModel);
68 this.numberOfIterations = numberOfIterations;
69
70 initFieldDisplayMapAndFieldLengthMap(initialFields);
71 }
72
73 public void init() {
74 updateTerminalLengthAndPageSize(topScreenView.getTerminalSize(), topScreenView.getPageSize());
75 topScreenView.hideCursor();
76 }
77
78 private void updateTerminalLengthAndPageSize(@Nullable TerminalSize terminalSize,
79 @Nullable Integer pageSize) {
80 if (terminalSize != null) {
81 terminalLength = terminalSize.getColumns();
82 } else {
83 terminalLength = Integer.MAX_VALUE;
84 }
85 if (pageSize != null) {
86 paging.updatePageSize(pageSize);
87 } else {
88 paging.updatePageSize(Integer.MAX_VALUE);
89 }
90 }
91
92 public long refresh(boolean force) {
93 if (!force) {
94 long delay = System.currentTimeMillis() - lastRefreshTimestamp;
95 if (delay < refreshDelay.get()) {
96 return refreshDelay.get() - delay;
97 }
98 }
99
100 TerminalSize newTerminalSize = topScreenView.doResizeIfNecessary();
101 if (newTerminalSize != null) {
102 updateTerminalLengthAndPageSize(newTerminalSize, topScreenView.getPageSize());
103 topScreenView.clearTerminal();
104 }
105
106 topScreenModel.refreshMetricsData();
107 paging.updateRecordsSize(topScreenModel.getRecords().size());
108
109 adjustFieldLengthIfNeeded();
110
111 topScreenView.showTopScreen(topScreenModel.getSummary(), getDisplayedHeaders(),
112 getDisplayedRecords(), getSelectedRecord());
113
114 topScreenView.refreshTerminal();
115
116 lastRefreshTimestamp = System.currentTimeMillis();
117 iterations++;
118 return refreshDelay.get();
119 }
120
121 public void adjustFieldLength() {
122 adjustFieldLength.set(true);
123 refresh(true);
124 }
125
126 private void adjustFieldLengthIfNeeded() {
127 if (adjustFieldLength.get()) {
128 adjustFieldLength.set(false);
129
130 for (Field f : topScreenModel.getFields()) {
131 if (f.isAutoAdjust()) {
132 int maxLength = 0;
133 for (Record record : topScreenModel.getRecords()) {
134 int length = record.get(f).asString().length();
135 maxLength = Math.max(length, maxLength);
136 }
137 fieldLengthMap.put(f, Math.max(maxLength, f.getHeader().length()));
138 }
139 }
140 }
141 }
142
143 private List<Header> getDisplayedHeaders() {
144 List<Field> displayFields = new ArrayList<>();
145 for (Field field : topScreenModel.getFields()) {
146 if (fieldDisplayMap.get(field)) {
147 displayFields.add(field);
148 }
149 }
150
151 if (displayFields.isEmpty()) {
152 horizontalScroll = 0;
153 } else if (horizontalScroll > displayFields.size() - 1) {
154 horizontalScroll = displayFields.size() - 1;
155 }
156
157 List<Header> ret = new ArrayList<>();
158
159 int length = 0;
160 for (int i = horizontalScroll; i < displayFields.size(); i++) {
161 Field field = displayFields.get(i);
162 int fieldLength = fieldLengthMap.get(field);
163
164 length += fieldLength + 1;
165 if (length > terminalLength) {
166 break;
167 }
168 ret.add(new Header(field, fieldLength));
169 }
170
171 return ret;
172 }
173
174 private List<Record> getDisplayedRecords() {
175 List<Record> ret = new ArrayList<>();
176 for (int i = paging.getPageStartPosition(); i < paging.getPageEndPosition(); i++) {
177 ret.add(topScreenModel.getRecords().get(i));
178 }
179 return ret;
180 }
181
182 private Record getSelectedRecord() {
183 if (topScreenModel.getRecords().isEmpty()) {
184 return null;
185 }
186 return topScreenModel.getRecords().get(paging.getCurrentPosition());
187 }
188
189 public void arrowUp() {
190 paging.arrowUp();
191 refresh(true);
192 }
193
194 public void arrowDown() {
195 paging.arrowDown();
196 refresh(true);
197 }
198
199 public void pageUp() {
200 paging.pageUp();
201 refresh(true);
202 }
203
204 public void pageDown() {
205 paging.pageDown();
206 refresh(true);
207 }
208
209 public void arrowLeft() {
210 if (horizontalScroll > 0) {
211 horizontalScroll -= 1;
212 }
213 refresh(true);
214 }
215
216 public void arrowRight() {
217 if (horizontalScroll < getHeaderSize() - 1) {
218 horizontalScroll += 1;
219 }
220 refresh(true);
221 }
222
223 public void home() {
224 if (horizontalScroll > 0) {
225 horizontalScroll = 0;
226 }
227 refresh(true);
228 }
229
230 public void end() {
231 int headerSize = getHeaderSize();
232 horizontalScroll = headerSize == 0 ? 0 : headerSize - 1;
233 refresh(true);
234 }
235
236 private int getHeaderSize() {
237 int size = 0;
238 for (Field field : topScreenModel.getFields()) {
239 if (fieldDisplayMap.get(field)) {
240 size++;
241 }
242 }
243 return size;
244 }
245
246 public void switchSortOrder() {
247 topScreenModel.switchSortOrder();
248 refresh(true);
249 }
250
251 public ScreenView transitionToHelpScreen(Screen screen, Terminal terminal) {
252 return new HelpScreenView(screen, terminal, refreshDelay.get(), topScreenView);
253 }
254
255 public ScreenView transitionToModeScreen(Screen screen, Terminal terminal) {
256 return new ModeScreenView(screen, terminal, topScreenModel.getCurrentMode(),
257 new ModeScreenPresenter.ResultListener() {
258 @Override
259 public void accept(Mode mode) {
260 switchMode(mode);
261 }
262 }, topScreenView);
263 }
264
265 public ScreenView transitionToFieldScreen(Screen screen, Terminal terminal) {
266 return new FieldScreenView(screen, terminal,
267 topScreenModel.getCurrentSortField(), topScreenModel.getFields(),
268 fieldDisplayMap,
269 new FieldScreenPresenter.ResultListener() {
270 @Override
271 public void accept(Field sortField, List<Field> fields,
272 EnumMap<Field, Boolean> fieldDisplayMap) {
273 topScreenModel.setSortFieldAndFields(sortField, fields);
274 TopScreenPresenter.this.fieldDisplayMap.clear();
275 TopScreenPresenter.this.fieldDisplayMap.putAll(fieldDisplayMap);
276 }
277 }, topScreenView);
278 }
279
280 private void switchMode(Mode nextMode) {
281 topScreenModel.switchMode(nextMode, false, null);
282 reset();
283 }
284
285 public void drillDown() {
286 Record selectedRecord = getSelectedRecord();
287 if (selectedRecord == null) {
288 return;
289 }
290 if (topScreenModel.drillDown(selectedRecord)) {
291 reset();
292 refresh(true);
293 }
294 }
295
296 private void reset() {
297 initFieldDisplayMapAndFieldLengthMap(null);
298 adjustFieldLength.set(true);
299 paging.init();
300 horizontalScroll = 0;
301 topScreenView.clearTerminal();
302 }
303
304 private void initFieldDisplayMapAndFieldLengthMap(@Nullable List<Field> initialFields) {
305 fieldDisplayMap.clear();
306 fieldLengthMap.clear();
307 for (FieldInfo fieldInfo : topScreenModel.getFieldInfos()) {
308 if (initialFields != null) {
309 fieldDisplayMap.put(fieldInfo.getField(), initialFields.contains(fieldInfo.getField()));
310 } else {
311 fieldDisplayMap.put(fieldInfo.getField(), fieldInfo.isDisplayByDefault());
312 }
313 fieldLengthMap.put(fieldInfo.getField(), fieldInfo.getDefaultLength());
314 }
315 }
316
317 public ScreenView goToMessageMode(Screen screen, Terminal terminal, int row, String message) {
318 return new MessageModeScreenView(screen, terminal, row, message, topScreenView);
319 }
320
321 public ScreenView goToInputModeForRefreshDelay(final Screen screen, final Terminal terminal,
322 final int row) {
323 return new InputModeScreenView(screen, terminal, row,
324 "Change refresh delay from " + (double) refreshDelay.get() / 1000 + " to", null,
325 new InputModeScreenPresenter.ResultListener() {
326 @Override
327 public ScreenView apply(String inputString) {
328 if (inputString.isEmpty()) {
329 return topScreenView;
330 }
331
332 double delay;
333 try {
334 delay = Double.parseDouble(inputString);
335 } catch (NumberFormatException e) {
336 return goToMessageMode(screen, terminal, row, "Unacceptable floating point");
337 }
338
339 refreshDelay.set((long) (delay * 1000));
340 return topScreenView;
341 }
342 });
343 }
344
345 public ScreenView goToInputModeForFilter(final Screen screen, final Terminal terminal,
346 final int row, final boolean ignoreCase) {
347 return new InputModeScreenView(screen, terminal, row,
348 "add filter #" + (topScreenModel.getFilters().size() + 1) +
349 " (" + (ignoreCase ? "ignoring case" : "case sensitive") + ") as: [!]FLD?VAL",
350 topScreenModel.getFilterHistories(),
351 new InputModeScreenPresenter.ResultListener() {
352 @Override
353 public ScreenView apply(String inputString) {
354 if (inputString.isEmpty()) {
355 return topScreenView;
356 }
357
358 if (!topScreenModel.addFilter(inputString, ignoreCase)) {
359 return goToMessageMode(screen, terminal, row, "Unacceptable filter expression");
360 }
361
362 paging.init();
363 return topScreenView;
364 }
365 });
366 }
367
368 public void clearFilters() {
369 topScreenModel.clearFilters();
370 paging.init();
371 refresh(true);
372 }
373
374 public ScreenView goToFilterDisplayMode(Screen screen, Terminal terminal, int row) {
375 return new FilterDisplayModeScreenView(screen, terminal, row, topScreenModel.getFilters(),
376 topScreenView);
377 }
378
379 public boolean isIterationFinished() {
380 return iterations >= numberOfIterations;
381 }
382 }