/* * Copyright 2012-2015 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.aerospike.aql.plugin.views; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Formatter; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.ColumnPixelData; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.IConsoleConstants; import org.eclipse.ui.console.IConsoleView; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.progress.UIJob; import org.eclipse.wb.swt.ResourceManager; import swing2swt.layout.BorderLayout; import com.aerospike.aql.IResultReporter; import com.aerospike.aql.grammar.IErrorReporter; import com.aerospike.client.AerospikeException; import com.aerospike.client.Key; import com.aerospike.client.Log.Level; import com.aerospike.client.Record; import com.aerospike.client.query.KeyRecord; import com.aerospike.client.query.RecordSet; import com.aerospike.client.query.ResultSet; import com.aerospike.core.CoreActivator; import com.aerospike.core.views.ResultsConsoleView; public class RecordView extends ViewPart implements IResultReporter, IErrorReporter{ public static final String ID = "com.aerospike.aql.plugin.views.RevordView"; //$NON-NLS-1$ protected static final int TABLE_REFRESH_PERIOD = 1000; protected static final int TABLE_BUFFER_SIZE = 100; protected final FormToolkit toolkit = new FormToolkit(Display.getCurrent()); protected Table table; protected List<KeyRecord> recordContent = new ArrayList<KeyRecord>(); protected final ArrayBlockingQueue<KeyRecord> recordBuffer; protected RecordSet recordSet; protected ResultSet resultSet; protected TableViewer tableViewer; protected TableColumnLayout tcl_container; protected Map<String, TableColumn> columnMap; protected Action textMessage; private boolean cancelled; protected RecordLabelProvider labelProvider = new RecordLabelProvider(this); private ResultsConsoleView consoleView; protected UIRefresh uiRefresh; protected Object lock = new Object(); protected MutexRule uiMutex; public RecordView() { setTitleImage(ResourceManager.getPluginImage("aerospike-core-plugin", "icons/aerospike.logo.png")); consoleView = new ResultsConsoleView(); // find the Aerospike console and display it IWorkbench wb = PlatformUI.getWorkbench(); IWorkbenchWindow win = wb.getActiveWorkbenchWindow(); IWorkbenchPage page = win.getActivePage(); IConsoleView view; try { view = (IConsoleView) page.showView(IConsoleConstants.ID_CONSOLE_VIEW); view.display(consoleView.getConsole()); } catch (PartInitException e) { CoreActivator.showError(e, e.getMessage()); } this.recordBuffer = new ArrayBlockingQueue<KeyRecord>(TABLE_BUFFER_SIZE); uiMutex = new MutexRule(); uiRefresh = new UIRefresh(); uiRefresh.setRule(uiMutex); uiRefresh.schedule(TABLE_REFRESH_PERIOD); } /** * Create contents of the view part. * @param parent */ @Override public void createPartControl(Composite parent) { parent.setLayout(new BorderLayout(0, 0)); Composite container = toolkit.createComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL); toolkit.paintBordersFor(container); tcl_container = new TableColumnLayout(); columnMap = new HashMap<String, TableColumn>(); container.setLayout(tcl_container); { tableViewer = new TableViewer(container, SWT.BORDER | SWT.FULL_SELECTION); table = tableViewer.getTable(); table.setLinesVisible(true); table.setHeaderVisible(true); toolkit.paintBordersFor(table); { TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmn = tableViewerColumn.getColumn(); tcl_container.setColumnData(tblclmn, new ColumnPixelData(150, true, true)); tblclmn.setText("Namespace"); tblclmn.pack(); } { TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmn = tableViewerColumn.getColumn(); tcl_container.setColumnData(tblclmn, new ColumnPixelData(60, true, true)); tblclmn.setText("Set"); } { TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmn = tableViewerColumn.getColumn(); tcl_container.setColumnData(tblclmn, new ColumnPixelData(100, true, true)); tblclmn.setText("Digest"); } { TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmn = tableViewerColumn.getColumn(); tcl_container.setColumnData(tblclmn, new ColumnPixelData(100, true, true)); tblclmn.setText("Generation"); tblclmn.pack(); } { TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmn = tableViewerColumn.getColumn(); tcl_container.setColumnData(tblclmn, new ColumnPixelData(100, true, true)); tblclmn.setText("Expirary"); } tableViewer.setLabelProvider(new RecordLabelProvider(this)); tableViewer.setContentProvider(new RecordContentProvider()); tableViewer.setInput(this.recordContent); } createActions(); initializeToolBar(); initializeMenu(); } public void dispose() { columnMap.clear(); toolkit.dispose(); super.dispose(); } protected void addColumn(String name, int minimumWidth){ if (columnMap.get(name) == null){ TableViewerColumn tableColumn = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmn = tableColumn.getColumn(); tcl_container.setColumnData(tblclmn, new ColumnWeightData(0, minimumWidth, true)); tblclmn.setText(name); columnMap.put(name, tblclmn); tblclmn.pack(); tableViewer.setLabelProvider(this.labelProvider); } } /** * Create the actions. */ private void createActions() { textMessage = new Action("Clear") { public void run() { recordContent.clear(); recordBuffer.clear(); tableViewer.refresh(); } }; textMessage.setImageDescriptor(ResourceManager.getPluginImageDescriptor("aerospike-core-plugin", "icons/clear.png")); } protected TableColumn findColumnByName(String name){ TableColumn column = this.columnMap.get(name); return column; } /** * Initialize the toolbar. */ private void initializeToolBar() { IToolBarManager tbm = getViewSite().getActionBars().getToolBarManager(); tbm.add(textMessage); } /** * Initialize the menu. */ private void initializeMenu() { IMenuManager manager = getViewSite().getActionBars().getMenuManager(); manager.add(textMessage); } @Override public void setFocus() { table.setFocus(); } @Deprecated public void scanCallback(final Key key, final Record record) throws AerospikeException { report(key, record); } @Override public void cancel() { close(); this.cancelled = true; } @Override public void close() { if (this.recordSet != null) this.recordSet.close(); if (this.resultSet != null) this.resultSet.close(); } @Override public ViewFormat getViewFormat() { return ViewFormat.TABLE; } @Override public boolean isCancelled() { return this.cancelled; } @Override public void report(String message) { this.report(message, false); } @Override public void report(Record record) { this.report(record, false); } @Override public void report(RecordSet recordSet) { this.report(recordSet, false); } @Override public void report(ResultSet resultSet) { this.report(resultSet, true); } @Override public void report(boolean clear, String message) { reportText(message, clear); } @Override public void report(Level arg0, String message) { reportText(message, false); } protected void reportText(final String message, final boolean clear){ consoleView.report(message, clear); } @Override public void report(final Key key, final Record record) { addRecord(new KeyRecord(key, record)); } @Override public void report(String message, boolean clear) { reportText(message, clear); } @Override public void report(Record record, boolean clear) { this.report((Key)null, record, clear); } @Override public void report(final RecordSet recordSet, final boolean clear) { this.recordSet = recordSet; Iterator<KeyRecord> it = recordSet.iterator(); while (it.hasNext()){ addRecord(it.next()); } recordSet.close(); } protected void addRecord(KeyRecord keyRecord){ recordBuffer.offer(keyRecord); } protected void addColumnsForRecord(KeyRecord keyRecord){ if (keyRecord != null && keyRecord.record != null){ for (Map.Entry<String, Object> entry : keyRecord.record.bins.entrySet()) { String binName = entry.getKey(); addColumn(binName, 10); } } } @Override public void report(ResultSet resultSet, boolean clear) { this.uiRefresh.setClear(clear); this.resultSet = resultSet; } @Override public void report(Level arg0, String format, boolean arg) { reportText(String.format(format, arg), false); } @Override public void report(Key key, Record record, boolean clear) { this.uiRefresh.setClear(clear); report(key, record); } @Override public void reportInfo(Map<String, Object>[] infoMap) { consoleView.reportInfo(infoMap); } @Override public void reportInfo(Map<String, Object> infoMap) { consoleView.reportInfo(infoMap); } @Override public void reportInfo(String format, String... args) { consoleView.reportInfo(format, args); } @Override public void reportInfo(String[] formats, String... args) { consoleView.reportInfo(formats, args); } @Override public void reportInfo(String format, boolean clear, String... args) { consoleView.reportInfo(format, clear, args); } @Override public void reportInfo(String[] formats, boolean clear, String... args) { consoleView.reportInfo(formats, clear, args); } @Override public void setViewFormat(ViewFormat viewFormat) { consoleView.setViewFormat(viewFormat); } public class RecordContentProvider implements IStructuredContentProvider { @Override public void dispose() { } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public Object[] getElements(Object inputElement) { if (inputElement instanceof List){ List<?> list = ((List)inputElement); if (list.size()==0){ return new Object[0]; } else { KeyRecord[] recordArray = new KeyRecord[list.size()]; int i = 0; for (Object element : list){ KeyRecord keyRecord = (KeyRecord) element; recordArray[i] = keyRecord; i++; } return recordArray; } } return null; } } public class RecordLabelProvider extends LabelProvider implements ITableLabelProvider { final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss"); final long AS_EPOCH = 1262304000000L; //TODO this is still wrong RecordView view; public RecordLabelProvider(RecordView view){ this.view = view; } @Override public Image getColumnImage(Object element, int columnIndex) { return null; } @Override public String getColumnText(Object element, int columnIndex) { KeyRecord kr = (KeyRecord) element; TableColumn col = this.view.table.getColumn(columnIndex); String result = ""; switch(columnIndex){ case 0: if (kr.key != null) result = kr.key.namespace; else result = ""; if (col != null) col.pack(); break; case 1: if (kr.key != null) result = kr.key.setName; else result = ""; if (col != null) col.pack(); break; case 2: if (kr.key != null) result = byteToHex(kr.key.digest); else result = ""; break; case 3: if (kr != null && kr.record != null) result = Integer.toString(kr.record.generation); else result = ""; break; case 4: if (kr != null && kr.record != null){ switch (kr.record.expiration){ case -1: //Forever result = "forever"; break; case 0: result = "default"; break; default: long ts = kr.record.expiration + AS_EPOCH; Date date = new Date(ts); result = dateFormat.format(date); if (col != null) col.pack(); break; } } else result = ""; break; default: if (col != null){ String name = col.getText(); if (kr != null && kr.record != null){ Object value = kr.record.getValue(name); if (value != null) result = value.toString(); } else result = ""; col.pack(); } } return result; } String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString().toUpperCase(); formatter.close(); return result; } } protected class UIRefresh extends UIJob { private boolean clear; public UIRefresh() { super("Record view refresh"); super.setPriority(DECORATE); } public boolean isClear() { return clear; } public void setClear(boolean clear) { if (!this.clear) this.clear = clear; } @Override public IStatus runInUIThread(IProgressMonitor progress) { if (!recordBuffer.isEmpty()){ if (tableViewer != null && tableViewer.getControl() != null && !tableViewer.getControl().isDisposed()) { if (clear){ recordContent.clear(); int count = table.getColumnCount(); TableColumn[] columns = table.getColumns() ; if (count > 5){ for (int x = count-1; x >= 5; x--){ columns[x].dispose(); } columnMap.clear(); } } for (;!recordBuffer.isEmpty();){ KeyRecord keyRec; try { keyRec = recordBuffer.take(); addColumnsForRecord(keyRec); recordContent.add(keyRec); } catch (InterruptedException e) { CoreActivator.showError(e, "Error refreshing record view"); } } tableViewer.refresh(); } } schedule(TABLE_REFRESH_PERIOD); return Status.OK_STATUS; } } /* * IErrorReporter */ @Override public void reportError(int line, int charStart, int charEnd, String message){ consoleView.reportError(line, charStart, charEnd, message); } @Override public void reportError(int line, String message){ consoleView.reportError(line, message); } @Override public void reportError(int line, AerospikeException e){ consoleView.reportError(line, e); } @Override public int getErrorCount(){ return consoleView.getErrorCount(); } @Override public List<String> getErrorList(){ return getErrorList(); } class MutexRule implements ISchedulingRule { public boolean isConflicting(ISchedulingRule rule) { return rule == this; } public boolean contains(ISchedulingRule rule) { return rule == this; } } }