"Server log" - "download" implementation obligates to register LogDownloadController in every web context #PL-4320

This commit is contained in:
Yuriy Artamonov 2015-02-20 10:42:54 +00:00
parent ded8fce8c3
commit a131009d3f
10 changed files with 297 additions and 42 deletions

View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 2008-2015 Haulmont. All rights reserved.
* Use is subject to license terms, see http://www.cuba-platform.com/license for details.
*/
package com.haulmont.cuba.web.app.ui.serverlogviewer;
import com.haulmont.cuba.core.entity.JmxInstance;
import com.haulmont.cuba.core.sys.logging.LogArchiver;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.WindowParam;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.web.export.LogDataProvider;
import javax.inject.Inject;
import java.util.*;
/**
* @author artamonov
* @version $Id$
*/
public class LogDownloadOptionsWindow extends AbstractWindow {
private static final int BYTES_IN_MB = 1024 * 1024;
@Inject
protected Button downloadFullBtn;
@Inject
protected Button downloadTailBtn;
@Inject
protected LookupField remoteContextField;
@Inject
protected BoxLayout remoteContextBox;
@Inject
protected Label sizeNotificationLabel;
@WindowParam(required = true)
protected JmxInstance connection;
@WindowParam(required = true)
protected String logFileName;
@WindowParam
protected List<String> remoteContextList;
@WindowParam(required = true)
protected Long logFileSize;
@Override
public void init(Map<String, Object> params) {
super.init(params);
getDialogParams().setWidthAuto();
if (remoteContextList == null || remoteContextList.isEmpty()) {
remoteContextBox.setVisible(false);
} else {
List<String> contexts = new ArrayList<>(remoteContextList);
Collections.sort(contexts, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if ((o1.contains("core") && !o2.contains("core"))
|| (o2.contains("portal") && !o1.contains("portal"))) {
return -1;
}
if ((o2.contains("core") && !o1.contains("core"))
|| (o1.contains("portal") && !o2.contains("portal"))) {
return 1;
}
return o1.compareTo(o2);
}
});
remoteContextField.setOptionsList(contexts);
remoteContextField.setValue(contexts.get(0));
}
if (logFileSize <= LogArchiver.LOG_TAIL_FOR_PACKING_SIZE) {
downloadTailBtn.setVisible(false);
downloadFullBtn.setCaption(getMessage("log.download"));
} else {
long sizeMb = logFileSize / BYTES_IN_MB;
sizeNotificationLabel.setValue(formatMessage("log.selectDownloadOption", sizeMb));
}
}
public void cancel() {
close(CLOSE_ACTION_ID);
}
public void downloadTail() {
LogDataProvider logDataProvider;
if (remoteContextBox.isVisible()) {
logDataProvider = new LogDataProvider(connection, logFileName,
(String) remoteContextField.getValue(), true);
} else {
logDataProvider = new LogDataProvider(connection, logFileName, true);
}
exportFile(logDataProvider, logFileName);
close(CLOSE_ACTION_ID);
}
public void downloadFull() {
LogDataProvider logDataProvider;
if (remoteContextBox.isVisible()) {
logDataProvider = new LogDataProvider(connection, logFileName,
(String) remoteContextField.getValue(), true);
} else {
logDataProvider = new LogDataProvider(connection, logFileName, true);
}
exportFile(logDataProvider, logFileName);
close(CLOSE_ACTION_ID);
}
protected void exportFile(LogDataProvider logDataProvider, String fileName) {
AppConfig.createExportDisplay(this).show(logDataProvider, fileName + ".zip");
}
}

View File

@ -5,6 +5,7 @@
package com.haulmont.cuba.web.app.ui.serverlogviewer;
import com.haulmont.bali.util.ParamsMap;
import com.haulmont.cuba.core.entity.JmxInstance;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.UserSessionSource;
@ -14,13 +15,10 @@ import com.haulmont.cuba.core.sys.logging.LoggingHelper;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.Button;
import com.haulmont.cuba.gui.components.CheckBox;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Label;
import com.haulmont.cuba.gui.components.Timer;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.ValueListener;
import com.haulmont.cuba.gui.export.ExportDisplay;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.web.app.ui.jmxinstance.edit.JmxInstanceEditor;
import com.haulmont.cuba.web.export.LogDataProvider;
@ -31,7 +29,8 @@ import com.haulmont.cuba.web.jmx.JmxRemoteLoggingAPI;
import com.vaadin.event.ShortcutAction;
import com.vaadin.event.ShortcutListener;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.*;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Panel;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
@ -53,8 +52,6 @@ public class ServerLogWindow extends AbstractWindow {
private final Log log = LogFactory.getLog(getClass());
private static final int BYTES_IN_MB = (1024 * 1024);
@Inject
protected CollectionDatasource<JmxInstance, UUID> jmxInstancesDs;
@ -413,34 +410,22 @@ public class ServerLogWindow extends AbstractWindow {
if (fileName != null) {
try {
final JmxInstance selectedConnection = getSelectedConnection();
// check if we have many suitable JmxControlBean instances
// show dialog with context select and size options if needed
List<String> availableContexts = jmxRemoteLoggingAPI.getAvailableContexts(selectedConnection);
long size = jmxRemoteLoggingAPI.getLogFileSize(selectedConnection, fileName);
if (size <= LogArchiver.LOG_TAIL_FOR_PACKING_SIZE) {
exportFile(new LogDataProvider(selectedConnection, fileName), fileName);
if (size <= LogArchiver.LOG_TAIL_FOR_PACKING_SIZE && availableContexts.size() == 1) {
ExportDisplay exportDisplay = AppConfig.createExportDisplay(this);
exportDisplay.show(new LogDataProvider(selectedConnection, fileName), fileName + ".zip");
} else {
long sizeMb = size / BYTES_IN_MB;
showOptionDialog(getMessage("log.downloadOption"), formatMessage("log.selectDownloadOption", sizeMb),
MessageType.CONFIRMATION,
new Action[]{
new AbstractAction("log.downloadTail") {
@Override
public void actionPerform(Component component) {
exportFile(new LogDataProvider(selectedConnection, fileName), fileName);
}
},
new AbstractAction("log.downloadFull") {
@Override
public void actionPerform(Component component) {
exportFile(new LogDataProvider(selectedConnection, fileName, true), fileName);
}
},
new AbstractAction("actions.Cancel") {
@Override
public void actionPerform(Component component) {
}
}
});
openWindow("serverLogDownloadOptionsDialog",
WindowManager.OpenType.DIALOG,
ParamsMap.of("logFileName", fileName,
"connection", selectedConnection,
"logFileSize", size,
"remoteContextList", availableContexts));
}
} catch (RuntimeException | LogControlException e) {
showNotification(getMessage("exception.logControl"), NotificationType.ERROR);
@ -451,10 +436,6 @@ public class ServerLogWindow extends AbstractWindow {
}
}
protected void exportFile(LogDataProvider logDataProvider, String fileName) {
AppConfig.createExportDisplay(this).show(logDataProvider, fileName + ".zip");
}
public void updateLogTail(@SuppressWarnings("unused") Timer timer) {
updateLogTail(true);
}

View File

@ -0,0 +1,24 @@
<!--
~ Copyright (c) 2008-2015 Haulmont. All rights reserved.
~ Use is subject to license terms, see http://www.cuba-platform.com/license for details.
-->
<window xmlns="http://schemas.haulmont.com/cuba/5.3/window.xsd"
class="com.haulmont.cuba.web.app.ui.serverlogviewer.LogDownloadOptionsWindow"
messagesPack="com.haulmont.cuba.web.app.ui.serverlogviewer"
caption="msg://log.download.options">
<layout spacing="true">
<hbox spacing="true" id="remoteContextBox">
<label value="msg://log.remoteContext" align="MIDDLE_CENTER"/>
<lookupField id="remoteContextField" required="true" width="200px"/>
</hbox>
<label id="sizeNotificationLabel"/>
<hbox spacing="true" align="MIDDLE_RIGHT">
<button id="downloadTailBtn" caption="msg://log.downloadTail" invoke="downloadTail"/>
<button id="downloadFullBtn" caption="msg://log.downloadFull" invoke="downloadFull"/>
<button caption="msg://actions.Cancel" invoke="cancel" icon="icons/cancel.png"/>
</hbox>
</layout>
</window>

View File

@ -26,7 +26,10 @@ log.notSelected=Log file is not selected
log.downloadOption=Download log file
log.downloadTail=Tail 20 MB
log.downloadFull=Full log
log.selectDownloadOption=Log file size is %s.\nDo you want do dowload full log?
log.download=Download
log.download.options=Download log file
log.remoteContext=Remote context
log.selectDownloadOption=Log file size is %s. Do you want do dowload full log?
actions.Get=Get
actions.Set=Set

View File

@ -27,7 +27,10 @@ log.notSelected=Файл журнала не выбран
log.downloadOption=Скачать файл журнала
log.downloadTail=Последние 20 МБ
log.downloadFull=Весь файл
log.selectDownloadOption=Размер файла журнала %s МБ.\nВы хотите скачать весь файл?
log.download.options=Скачать файл журнала
log.download=Скачать
log.remoteContext=Удалённый контекст
log.selectDownloadOption=Размер файла журнала %s МБ. Вы хотите скачать весь файл?
actions.Get=Прочитать
actions.Set=Установить

View File

@ -53,16 +53,21 @@ public class LogDataProvider implements ExportDataProvider {
protected JmxInstance jmxInstance;
protected String remoteContext;
protected boolean downloadFullLog = false;
public LogDataProvider(JmxInstance jmxInstance, String logFileName) {
this.logFileName = logFileName;
this.jmxInstance = jmxInstance;
this(jmxInstance, logFileName, null, false);
}
public LogDataProvider(JmxInstance jmxInstance, String logFileName, boolean downloadFullLog) {
this(jmxInstance, logFileName, null, downloadFullLog);
}
public LogDataProvider(JmxInstance jmxInstance, String logFileName, String remoteContext, boolean downloadFullLog) {
this.logFileName = logFileName;
this.jmxInstance = jmxInstance;
this.remoteContext = remoteContext;
this.downloadFullLog = downloadFullLog;
}
@ -73,7 +78,7 @@ public class LogDataProvider implements ExportDataProvider {
String url;
try {
url = jmxRemoteLoggingAPI.getLogFileLink(jmxInstance, logFileName);
url = jmxRemoteLoggingAPI.getLogFileLink(jmxInstance, remoteContext, logFileName);
} catch (Exception e) {
log.error("Unable to get log file link from JMX interface");

View File

@ -19,6 +19,7 @@ import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.Collection;
import java.util.Properties;
import java.util.Set;
@ -57,6 +58,52 @@ public final class JmxConnectionHelper {
});
}
protected static ObjectName getObjectName(final MBeanServerConnection connection, final String remoteContext,
final Class objectClass) throws IOException {
Set<ObjectName> names = connection.queryNames(null, null);
return (ObjectName) CollectionUtils.find(names, new Predicate() {
@Override
public boolean evaluate(Object o) {
ObjectName objectName = (ObjectName) o;
if (StringUtils.equals(remoteContext, objectName.getDomain())) {
return false;
}
MBeanInfo info;
try {
info = connection.getMBeanInfo(objectName);
} catch (Exception e) {
throw new JmxControlException(e);
}
return StringUtils.equals(objectClass.getName(), info.getClassName());
}
});
}
protected static Collection<ObjectName> getSuitableObjectNames(final MBeanServerConnection connection,
final Class objectClass) throws IOException {
Set<ObjectName> names = connection.queryNames(null, null);
// find all suitable beans
@SuppressWarnings("unchecked")
Collection<ObjectName> suitableNames = CollectionUtils.select(names, new Predicate() {
@Override
public boolean evaluate(Object o) {
ObjectName objectName = (ObjectName) o;
MBeanInfo info;
try {
info = connection.getMBeanInfo(objectName);
} catch (Exception e) {
throw new JmxControlException(e);
}
return StringUtils.equals(objectClass.getName(), info.getClassName());
}
});
return suitableNames;
}
protected static <T> T getProxy(MBeanServerConnection connection, ObjectName objectName, final Class<T> objectClass) {
return JMX.newMBeanProxy(connection, objectName, objectClass, true);
}

View File

@ -46,6 +46,16 @@ public interface JmxRemoteLoggingAPI {
*/
String getLogFileLink(JmxInstance instance, String fileName) throws LogControlException;
/**
* Get URL for log file downloading.
*
* @param instance jmx connection
* @param remoteContext remote web context
* @param fileName name of a log file
* @return URL
*/
String getLogFileLink(JmxInstance instance, String remoteContext, String fileName) throws LogControlException;
/**
* Get size for log file downloading.
*
@ -121,6 +131,12 @@ public interface JmxRemoteLoggingAPI {
*/
LoggingHostInfo getHostInfo(JmxInstance instance);
/**
* @param instance jmx connection
* @return list of available web contexts on this instance
*/
List<String> getAvailableContexts(JmxInstance instance);
public class LoggingHostInfo {
private List<String> loggerNames;
private List<String> appenders;

View File

@ -16,6 +16,8 @@ import javax.annotation.ManagedBean;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -63,6 +65,18 @@ public class JmxRemoteLoggingBean implements JmxRemoteLoggingAPI {
});
}
@Override
public String getLogFileLink(JmxInstance instance, final String remoteContext,
final String fileName) throws LogControlException {
return withConnection(instance, new JmxAction<String>() {
@Override
public String perform(JmxInstance jmx, MBeanServerConnection connection) throws Exception {
JmxLogControlMBean logControlMBean = getRemoteLogControl(connection, remoteContext);
return logControlMBean.getLogFileLink(fileName);
}
});
}
@Override
public long getLogFileSize(JmxInstance instance, final String fileName) throws LogControlException {
return withConnection(instance, new JmxAction<Long>() {
@ -192,6 +206,22 @@ public class JmxRemoteLoggingBean implements JmxRemoteLoggingAPI {
});
}
@Override
public List<String> getAvailableContexts(JmxInstance instance) {
return withConnection(instance, new JmxAction<List<String>>() {
@Override
public List<String> perform(JmxInstance jmx, MBeanServerConnection connection) throws Exception {
Collection<ObjectName> objectNames =
JmxConnectionHelper.getSuitableObjectNames(connection, JmxLogControl.class);
List<String> contexts = new ArrayList<>();
for (ObjectName objectName : objectNames) {
contexts.add(objectName.getDomain());
}
return contexts;
}
});
}
protected JmxLogControlMBean getRemoteLogControl(MBeanServerConnection connection) {
ObjectName objectName;
try {
@ -206,4 +236,19 @@ public class JmxRemoteLoggingBean implements JmxRemoteLoggingAPI {
return JmxConnectionHelper.getProxy(connection, objectName, JmxLogControlMBean.class);
}
protected JmxLogControlMBean getRemoteLogControl(MBeanServerConnection connection, String remoteContext) {
ObjectName objectName;
try {
objectName = JmxConnectionHelper.getObjectName(connection, remoteContext, JmxLogControl.class);
} catch (IOException e) {
throw new JmxControlException(e);
}
if (objectName == null) {
throw new JmxControlException("Could not find JmxLogControl implementation");
}
return JmxConnectionHelper.getProxy(connection, objectName, JmxLogControlMBean.class);
}
}

View File

@ -50,6 +50,9 @@
<screen id="serverLogLoggerControlDialog"
template="/com/haulmont/cuba/web/app/ui/serverlogviewer/control-logger-window.xml" multipleOpen="true"/>
<screen id="serverLogDownloadOptionsDialog"
template="com/haulmont/cuba/web/app/ui/serverlogviewer/log-download-options.xml"/>
<screen id="printDomain"
class="com.haulmont.cuba.web.app.domain.DomainProvider"/>
</screen-config>
</screen-config>