/* * Copyright 2015 the original author or authors. * @https://github.com/scouter-project/scouter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package scouter.client.views; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IViewSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.ViewPart; import scouter.client.Images; import scouter.client.model.TextProxy; import scouter.client.net.TcpProxy; import scouter.client.popup.EditableMessageDialog; import scouter.client.server.GroupPolicyConstants; import scouter.client.server.Server; import scouter.client.server.ServerManager; import scouter.client.sorter.ColumnLabelSorter; import scouter.client.util.ConsoleProxy; import scouter.client.util.ExUtil; import scouter.client.util.TimeUtil; import scouter.client.util.ScouterUtil; import scouter.lang.pack.MapPack; import scouter.lang.value.BlobValue; import scouter.lang.value.ListValue; import scouter.lang.value.Value; import scouter.net.RequestCmd; import scouter.util.CastUtil; import scouter.util.DateUtil; import scouter.util.FileUtil; import scouter.util.StringUtil; public class ObjectClassListView extends ViewPart { public static final String ID = ObjectClassListView.class.getName(); private int objHash; private int currentPage = 1; private int totalPage = 1; private Text filterText; private Button leftButton; private Button rightButton; private TableViewer tableViewer; private TableColumnLayout tableColumnLayout; private Clipboard clipboard; private int serverId; public void init(IViewSite site) throws PartInitException { super.init(site); String secId = site.getSecondaryId(); String ids[] = secId.split("&"); this.serverId = CastUtil.cint(ids[0]); this.objHash = CastUtil.cint(ids[1]); } public void createPartControl(Composite parent) { this.setPartName("Loaded Class List[" + TextProxy.object.getLoadText(DateUtil.yyyymmdd(TimeUtil.getCurrentTime(serverId)), objHash, serverId) + "]"); initialLayout(parent); clipboard = new Clipboard(null); load(); } public void load() { final String filter = filterText.getText(); ExUtil.asyncRun(new Runnable() { public void run() { TcpProxy tcp = TcpProxy.getTcpProxy(serverId); MapPack out = null; try { MapPack param = new MapPack(); param.put("objHash", objHash); if (StringUtil.isNotEmpty(filter)) { param.put("filter", filter); } param.put("page", currentPage); out = (MapPack) tcp.getSingle(RequestCmd.OBJECT_CLASS_LIST, param); if (out == null) { return; } } catch (Throwable t) { ConsoleProxy.errorSafe(t.toString()); } finally { TcpProxy.putTcpProxy(tcp); } currentPage = out.getInt("page"); totalPage = out.getInt("totalPage"); ListValue indexLv = out.getList("index"); if (indexLv == null) { return; } ListValue nameLv = out.getList("name"); ListValue typeLv = out.getList("type"); ListValue superClassLv = out.getList("superClass"); ListValue interfaceLv = out.getList("interfaces"); ListValue resourceLv = out.getList("resource"); int count = indexLv.size(); final List<ClassData> classDataList = new ArrayList<ClassData>(count); for (int i = 0; i < count; i++) { ClassData data = new ClassData(); data.index = indexLv.getLong(i); data.type = typeLv.getString(i); data.name = nameLv.getString(i); data.superClass = superClassLv.getString(i); data.interfaces = interfaceLv.getString(i); data.resources = resourceLv.getString(i); classDataList.add(data); } ExUtil.exec(tableViewer.getTable(), new Runnable() { public void run() { validatePageButton(); tableViewer.setInput(classDataList); } }); } }); } private void initialLayout(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(1, true)); createUpperMenu(composite); Composite tableComposite = new Composite(composite, SWT.NONE); tableComposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true)); tableComposite.setLayout(new GridLayout(1, true)); createTableViewer(tableComposite); } private void createTableViewer(Composite composite) { tableViewer = new TableViewer(composite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER); tableColumnLayout = new TableColumnLayout(); composite.setLayout(tableColumnLayout); createColumns(); final Table table = tableViewer.getTable(); table.setHeaderVisible(true); table.setLinesVisible(true); createTableContextMenu(); tableViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent arg0) { openDescription(); } }); tableViewer.setContentProvider(new ArrayContentProvider()); tableViewer.setComparator(new ColumnLabelSorter(tableViewer)); GridData gridData = new GridData(GridData.FILL, GridData.FILL, true, true); tableViewer.getControl().setLayoutData(gridData); } private void createTableContextMenu() { MenuManager manager = new MenuManager(); tableViewer.getControl().setMenu(manager.createContextMenu(tableViewer.getControl())); manager.add(new Action("&Copy", ImageDescriptor.createFromImage(Images.copy)) { public void run() { selectionCopyToClipboard(); } }); Server server = ServerManager.getInstance().getServer(serverId); if (server.isAllowAction(GroupPolicyConstants.ALLOW_EXPORTCLASS)) { manager.add(new Action("&Export Class") { public void run() { StructuredSelection selection = (StructuredSelection) tableViewer.getSelection(); ClassData data = (ClassData) selection.getFirstElement(); final String className = data.name; if (StringUtil.isEmpty(className)) { return; } ExUtil.asyncRun(new Runnable() { public void run() { TcpProxy tcp = TcpProxy.getTcpProxy(serverId); MapPack p = null; try { MapPack param = new MapPack(); param.put("objHash", objHash); param.put("class", className); p = (MapPack) tcp.getSingle(RequestCmd.OBJECT_LOAD_CLASS_BY_STREAM, param); } catch (Exception e) { ConsoleProxy.errorSafe(e.getMessage()); } finally { TcpProxy.putTcpProxy(tcp); } if (p != null) { String error = CastUtil.cString(p.get("error")); if (StringUtil.isNotEmpty(error)) { ConsoleProxy.errorSafe(error); } Value v = p.get("class"); if (v != null) { final BlobValue bv = (BlobValue) v; ExUtil.exec(tableViewer.getTable(), new Runnable() { public void run() { saveClassFile(className, bv); } }); } } } }); } }); manager.add(new Action("&Export Jar") { public void run() { StructuredSelection selection = (StructuredSelection) tableViewer.getSelection(); final ClassData data = (ClassData) selection.getFirstElement(); final String resource = data.resources; if (StringUtil.isEmpty(resource)) { return; } ExUtil.asyncRun(new Runnable() { public void run() { TcpProxy tcp = TcpProxy.getTcpProxy(serverId); MapPack p = null; try { MapPack param = new MapPack(); param.put("objHash", objHash); param.put("resource", resource); p = (MapPack) tcp.getSingle(RequestCmd.OBJECT_CHECK_RESOURCE_FILE, param); } catch (Exception e) { ConsoleProxy.errorSafe(e.getMessage()); } finally { TcpProxy.putTcpProxy(tcp); } if (p != null) { String error = p.getText("error"); if (StringUtil.isNotEmpty(error)) { ConsoleProxy.errorSafe(error); } else { final String name = p.getText("name"); final long size = p.getLong("size"); ExUtil.exec(tableViewer.getTable(), new Runnable() { public void run() { if(MessageDialog.openQuestion(tableViewer.getTable().getShell(), data.name , name + "(" + ScouterUtil.humanReadableByteCount(size, true) +") will be downloaded.\nContinue?")) { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getShell(); FileDialog dialog = new FileDialog(shell, SWT.SAVE); dialog.setOverwrite(true); dialog.setFileName(name); dialog.setFilterExtensions(new String[] { "*.jar", "*.*" }); dialog.setFilterNames(new String[] { "Jar File(*.jar)", "All Files" }); String fileSelected = dialog.open(); if (fileSelected != null) { new DownloadJarFileJob(name, resource, fileSelected).schedule(); } } } }); } } } }); } }); } manager.add(new Action("&Description") { public void run() { openDescription(); } }); manager.add(new Separator()); if (server.isAllowAction(GroupPolicyConstants.ALLOW_REDEFINECLASS)) { manager.add(new Action("&Redefine class") { public void run() { StructuredSelection selection = (StructuredSelection) tableViewer.getSelection(); final ListValue classLv = new ListValue(); Object[] datas = selection.toArray(); for (int i = 0; i < datas.length; i++) { ClassData data = (ClassData) datas[i]; classLv.add(data.name); } if(MessageDialog.openQuestion(tableViewer.getTable().getShell(), classLv.size() + " class(es) selected" , "Redefine class may affect this server.\nContinue?")) { new RedefineClassJob(classLv).schedule(); } } }); } tableViewer.getTable().addListener(SWT.KeyDown, new Listener() { public void handleEvent(Event e) { if(e.stateMask == SWT.CTRL){ if (e.keyCode == 'c' || e.keyCode == 'C') { selectionCopyToClipboard(); } } } }); } public void saveClassFile(String className, final BlobValue bv) { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getShell(); FileDialog dialog = new FileDialog(shell, SWT.SAVE); dialog.setOverwrite(true); dialog.setFileName(className + ".class"); dialog.setFilterExtensions(new String[] { "*.class", "*.*" }); dialog.setFilterNames(new String[] { "Class File(*.class)", "All Files" }); final String fileSelected = dialog.open(); if (fileSelected != null) { ExUtil.asyncRun("Decompile-" + className + TimeUtil.getCurrentTime(serverId), new Runnable() { public void run() { FileUtil.save(fileSelected, bv.value); ConsoleProxy.infoSafe(fileSelected + " saved."); } }); } } private void createUpperMenu(Composite composite) { Group parentGroup = new Group(composite, SWT.NONE); parentGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); GridLayout layout = new GridLayout(5, false); parentGroup.setLayout(layout); filterText = new Text(parentGroup, SWT.BORDER); GridData gridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false); gridData.minimumWidth = 150; filterText.setLayoutData(gridData); final Button applyButton = new Button(parentGroup, SWT.PUSH); applyButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); applyButton.setImage(Images.filter); applyButton.setText("Apply Filter"); applyButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { currentPage = 1; ExUtil.exec(new Runnable() { public void run() { load(); } }); } }); filterText.addTraverseListener(new TraverseListener() { public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_RETURN) { applyButton.notifyListeners(SWT.Selection, new Event()); } } }); leftButton = new Button(parentGroup, SWT.PUSH); leftButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); leftButton.setText("<"); leftButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { if (currentPage > 1) { currentPage--; ExUtil.exec(new Runnable() { public void run() { load(); } }); } } }); rightButton = new Button(parentGroup, SWT.PUSH); rightButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); rightButton.setText(">"); rightButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { if (currentPage < totalPage) { currentPage++; ExUtil.exec(new Runnable() { public void run() { load(); } }); } } }); validatePageButton(); } private void createColumns() { for (ClassColumnEnum column : ClassColumnEnum.values()) { TableViewerColumn c = createTableViewerColumn(column.getTitle(), column.getWidth(), column.getAlignment(), column.isResizable(), column.isMoveable(), column.isNumber()); ColumnLabelProvider labelProvider = null; switch (column) { case NO: labelProvider = new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof ClassData) { return String.valueOf(((ClassData)element).index); } return null; } }; break; case TYPE: labelProvider = new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof ClassData) { return ((ClassData) element).type; } return null; } }; break; case NAME: labelProvider = new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof ClassData) { return ((ClassData) element).name; } return null; } }; break; case SUPERCLASS: labelProvider = new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof ClassData) { return ((ClassData) element).superClass; } return null; } }; break; case INTERFACES: labelProvider = new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof ClassData) { return ((ClassData) element).interfaces; } return null; } }; break; case RESOURCE: labelProvider = new ColumnLabelProvider() { @Override public String getText(Object element) { if (element instanceof ClassData) { return ((ClassData) element).resources; } return null; } }; break; } if (labelProvider != null) { c.setLabelProvider(labelProvider); } } } private TableViewerColumn createTableViewerColumn(String title, int width, int alignment, boolean resizable, boolean moveable, final boolean isNumber) { final TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); final TableColumn column = viewerColumn.getColumn(); column.setText(title); column.setAlignment(alignment); column.setMoveable(moveable); tableColumnLayout.setColumnData(column, new ColumnWeightData(width, width, resizable)); column.setData("isNumber", isNumber); column.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { ColumnLabelSorter sorter = (ColumnLabelSorter) tableViewer.getComparator(); TableColumn selectedColumn = (TableColumn) e.widget; sorter.setColumn(selectedColumn); } }); return viewerColumn; } public void setFocus() { } private void validatePageButton() { if (this.currentPage <= 1) { this.leftButton.setEnabled(false); } else { this.leftButton.setEnabled(true); } if (this.currentPage == this.totalPage) { this.rightButton.setEnabled(false); } else { this.rightButton.setEnabled(true); } } private void selectionCopyToClipboard() { if (tableViewer != null) { TableItem[] items = tableViewer.getTable().getSelection(); if (items != null && items.length > 0) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < items.length; i++) { ClassData data = (ClassData) items[i].getData(); sb.append(data.toString()); } clipboard.setContents(new Object[] {sb.toString()}, new Transfer[] {TextTransfer.getInstance()}); } } } enum ClassColumnEnum { NO("No", 20, SWT.RIGHT, true, true, true), TYPE("Type", 30, SWT.CENTER, true, true, false), NAME("Name", 250, SWT.LEFT, true, true, false), SUPERCLASS("SuperClass", 150, SWT.LEFT, true, true, false), INTERFACES("Interfaces", 150, SWT.LEFT, true, true, false), RESOURCE("Resources", 150, SWT.LEFT, true, true, false); private final String title; private final int width; private final int alignment; private final boolean resizable; private final boolean moveable; private final boolean isNumber; private ClassColumnEnum(String text, int width, int alignment, boolean resizable, boolean moveable, boolean isNumber) { this.title = text; this.width = width; this.alignment = alignment; this.resizable = resizable; this.moveable = moveable; this.isNumber = isNumber; } public String getTitle(){ return title; } public int getAlignment(){ return alignment; } public boolean isResizable(){ return resizable; } public boolean isMoveable(){ return moveable; } public int getWidth() { return width; } public boolean isNumber() { return this.isNumber; } } class ClassData { public long index; public String type; public String name; public String superClass; public String interfaces; public String resources; public String toString() { return index + "\t" + type + "\t" + name + "\t" + superClass + "\t" + interfaces + "\t" + resources + "\n"; } } class RedefineClassJob extends Job { ListValue classLv; public RedefineClassJob(ListValue classLv) { super("Redefine.... " + classLv.size() + " classes....."); this.classLv = classLv; } protected IStatus run(IProgressMonitor monitor) { monitor.beginTask("Redefine class", IProgressMonitor.UNKNOWN); TcpProxy tcp = TcpProxy.getTcpProxy(serverId); MapPack p = null; try { MapPack param = new MapPack(); param.put("objHash", objHash); param.put("class", classLv); p = (MapPack) tcp.getSingle(RequestCmd.REDEFINE_CLASSES, param); } catch(Exception e) { ConsoleProxy.errorSafe(e.getMessage()); } finally { TcpProxy.putTcpProxy(tcp); } if (p != null) { boolean success = p.getBoolean("success"); if (success) { ConsoleProxy.infoSafe("Redefine complete"); } else { String error = p.getText("error"); if (StringUtil.isNotEmpty(error)) { ConsoleProxy.errorSafe(error); } else { ConsoleProxy.errorSafe("Redefine failed."); } } } return Status.CANCEL_STATUS; } } class DownloadJarFileJob extends Job { String resource; String saveFile; public DownloadJarFileJob(String name, String resource, String saveFile) { super("Download...." + name); this.resource = resource; this.saveFile = saveFile; } protected IStatus run(IProgressMonitor monitor) { monitor.beginTask("Downloading resource", IProgressMonitor.UNKNOWN); TcpProxy tcp = TcpProxy.getTcpProxy(serverId); MapPack p = null; try { MapPack param = new MapPack(); param.put("objHash", objHash); param.put("resource", resource); p = (MapPack) tcp.getSingle(RequestCmd.OBJECT_DOWNLOAD_JAR, param); } catch(Exception e) { ConsoleProxy.errorSafe(e.getMessage()); } finally { TcpProxy.putTcpProxy(tcp); } if (p != null) { String error = p.getText("error"); if (StringUtil.isNotEmpty(error)) { ConsoleProxy.errorSafe(error); } else { Value v = p.get("jar"); if (v != null) { BlobValue bv = (BlobValue) v; FileUtil.save(saveFile, bv.value); return Status.OK_STATUS; } } } return Status.CANCEL_STATUS; } } private void openDescription() { StructuredSelection selection = (StructuredSelection) tableViewer.getSelection(); ClassData data = (ClassData) selection.getFirstElement(); final String className = data.name; if (StringUtil.isEmpty(className)) { return; } ExUtil.asyncRun(new Runnable() { public void run() { TcpProxy tcp = TcpProxy.getTcpProxy(serverId); MapPack p = null; try { MapPack param = new MapPack(); param.put("objHash", objHash); param.put("class", className); p = (MapPack) tcp.getSingle(RequestCmd.OBJECT_CLASS_DESC, param); } catch (Exception e) { ConsoleProxy.errorSafe(e.getMessage()); } finally { TcpProxy.putTcpProxy(tcp); } if (p != null) { final String error = CastUtil.cString(p.get("error")); final Value v = p.get("class"); ExUtil.exec(tableViewer.getTable(), new Runnable() { public void run() { if (StringUtil.isNotEmpty(error)) { new EditableMessageDialog().show("ERROR", error); return; } new EditableMessageDialog().show(className, CastUtil.cString(v)); } }); } } }); } }