From 97bb8d9b835336ec72f6963158028ec3fbbfa05c Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Tue, 15 Oct 2019 20:28:28 +0800 Subject: [PATCH] IDEMPIERE-4072 iDempiere Monitor: Implement server and cache details for cluster node --- .../src/org/compiere/model/MSession.java | 7 + .../src/org/compiere/util/CCache.java | 4 + .../src/org/compiere/util/CacheInfo.java | 196 ++++++ .../src/org/compiere/util/CacheMgt.java | 16 + .../compiere/util/GetCacheInfoCallable.java | 54 ++ .../org/compiere/util/ResetCacheCallable.java | 2 +- .../compiere/server/AdempiereServerMgr.java | 9 +- .../org/compiere/server/LogFileInfo.java | 105 ++++ .../org/compiere/server/SystemInfo.java | 360 +++++++++++ .../server/org/compiere/server/TrxInfo.java | 91 +++ .../server/cluster/ClusterServerMgr.java | 23 +- .../cluster/callable/DeleteLogsCallable.java | 72 +++ .../callable/GetSystemInfoCallable.java | 55 ++ .../cluster/callable/ReadLogCallable.java | 142 +++++ .../cluster/callable/RotateLogCallable.java | 61 ++ .../callable/SetTraceLevelCallable.java | 61 ++ .../org/compiere/web/AdempiereMonitor.java | 594 ++++++++++++++---- 17 files changed, 1723 insertions(+), 129 deletions(-) create mode 100644 org.adempiere.base/src/org/compiere/util/CacheInfo.java create mode 100644 org.adempiere.base/src/org/compiere/util/GetCacheInfoCallable.java create mode 100644 org.adempiere.server/src/main/server/org/compiere/server/LogFileInfo.java create mode 100644 org.adempiere.server/src/main/server/org/compiere/server/SystemInfo.java create mode 100644 org.adempiere.server/src/main/server/org/compiere/server/TrxInfo.java create mode 100644 org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/DeleteLogsCallable.java create mode 100644 org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/GetSystemInfoCallable.java create mode 100644 org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/ReadLogCallable.java create mode 100644 org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/RotateLogCallable.java create mode 100644 org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/SetTraceLevelCallable.java diff --git a/org.adempiere.base/src/org/compiere/model/MSession.java b/org.adempiere.base/src/org/compiere/model/MSession.java index e77d6c9826..76f245307e 100644 --- a/org.adempiere.base/src/org/compiere/model/MSession.java +++ b/org.adempiere.base/src/org/compiere/model/MSession.java @@ -319,5 +319,12 @@ public class MSession extends X_AD_Session return null; } // changeLog + /** + * + * @return number of cached sessions + */ + public static int getCachedSessionCount() { + return s_sessions.size()-1; + } } // MSession diff --git a/org.adempiere.base/src/org/compiere/util/CCache.java b/org.adempiere.base/src/org/compiere/util/CCache.java index dc176830fb..e808dfbd9d 100644 --- a/org.adempiere.base/src/org/compiere/util/CCache.java +++ b/org.adempiere.base/src/org/compiere/util/CCache.java @@ -427,4 +427,8 @@ public class CCache implements CacheInterface, Map, Serializable public int getMaxSize() { return m_maxSize; } + + public boolean isDistributed() { + return m_distributed; + } } // CCache diff --git a/org.adempiere.base/src/org/compiere/util/CacheInfo.java b/org.adempiere.base/src/org/compiere/util/CacheInfo.java new file mode 100644 index 0000000000..65ee7a723b --- /dev/null +++ b/org.adempiere.base/src/org/compiere/util/CacheInfo.java @@ -0,0 +1,196 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.compiere.util; + +import java.io.Serializable; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.adempiere.base.IServiceHolder; +import org.adempiere.base.Service; +import org.idempiere.distributed.IClusterMember; +import org.idempiere.distributed.IClusterService; + +/** + * @author hengsin + * + */ +public class CacheInfo implements Serializable { + + /** + * generated serial id + */ + private static final long serialVersionUID = -9069013908523249394L; + + private String name; + private String tableName; + private int size; + private int expireMinutes; + private int maxSize; + private boolean distributed; + private InetAddress nodeAddress; + private String nodeId; + + /** + * + */ + public CacheInfo(CCache cache) { + name = cache.getName(); + tableName = cache.getTableName(); + size = cache.size(); + expireMinutes = cache.getExpireMinutes(); + maxSize = cache.getMaxSize(); + distributed = cache.isDistributed(); + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return the tableName + */ + public String getTableName() { + return tableName; + } + + /** + * @return the size + */ + public int getSize() { + return size; + } + + /** + * @return the expireMinutes + */ + public int getExpireMinutes() { + return expireMinutes; + } + + /** + * @return the maxSize + */ + public int getMaxSize() { + return maxSize; + } + + /** + * @return the distributed + */ + public boolean isDistributed() { + return distributed; + } + + + /** + * @return the nodeAddress + */ + public InetAddress getNodeAddress() { + return nodeAddress; + } + + /** + * @return the nodeId + */ + public String getNodeId() { + return nodeId; + } + + /** + * + * @param sortByName + * @return cache infos + */ + public static List getCacheInfos(boolean sortByName) { + IServiceHolder holder = Service.locator().locate(IClusterService.class); + IClusterService service = holder != null ? holder.getService() : null; + if (service != null && service.getMembers().size() > 1) { + List instances = new ArrayList<>(); + GetCacheInfoCallable callable = new GetCacheInfoCallable(); + Map>> futureMap = service.execute(callable, service.getMembers()); + if (futureMap != null) { + try { + Set>>> results = futureMap.entrySet(); + for(Entry>> f : results) { + List response = f.getValue().get(); + if (response != null && response.size() > 0) { + response.forEach(e -> { + e.setNodeId(f.getKey().getId()); + e.setNodeAddress(f.getKey().getAddress()); + instances.add(e); + }); + } + } + } catch (InterruptedException e) { + throw new RuntimeException(e.getMessage(), e); + } catch (ExecutionException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + if (sortByName) { + Collections.sort(instances, new Comparator() { + @Override + public int compare(CacheInfo o1, CacheInfo o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + } + return instances; + } else { + List instances = CacheMgt.get().getCacheInfos(); + if (sortByName) { + Collections.sort(instances, new Comparator() { + @Override + public int compare(CacheInfo o1, CacheInfo o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + } + return instances; + } + } + + private void setNodeAddress(InetAddress address) { + nodeAddress = address; + } + + private void setNodeId(String id) { + nodeId = id; + } + +} diff --git a/org.adempiere.base/src/org/compiere/util/CacheMgt.java b/org.adempiere.base/src/org/compiere/util/CacheMgt.java index bb90b0c844..8574d4198f 100644 --- a/org.adempiere.base/src/org/compiere/util/CacheMgt.java +++ b/org.adempiere.base/src/org/compiere/util/CacheMgt.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -385,6 +386,21 @@ public class CacheMgt clusterNewRecord(tableName, recordId); } + /** + * + * @return cache infos + */ + public List getCacheInfos() { + List infos = new ArrayList<>(); + CacheInterface[] instances = getInstancesAsArray(); + for(CacheInterface ci : instances) { + if (ci instanceof CCache) { + infos.add(new CacheInfo((CCache) ci)); + } + } + return infos; + } + private static class MaxSizeHashMap extends LinkedHashMap { /** * generated serial id diff --git a/org.adempiere.base/src/org/compiere/util/GetCacheInfoCallable.java b/org.adempiere.base/src/org/compiere/util/GetCacheInfoCallable.java new file mode 100644 index 0000000000..6c9b77d36e --- /dev/null +++ b/org.adempiere.base/src/org/compiere/util/GetCacheInfoCallable.java @@ -0,0 +1,54 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.compiere.util; + +import java.io.Serializable; +import java.util.List; +import java.util.concurrent.Callable; + +/** + * + * @author hengsin + * + */ +public class GetCacheInfoCallable implements Callable>, Serializable { + + + /** + * generated serial id + */ + private static final long serialVersionUID = 6444040776576577718L; + + public GetCacheInfoCallable() { + } + + @Override + public List call() throws Exception { + List instances = CacheMgt.get().getCacheInfos(); + return instances; + } + +} diff --git a/org.adempiere.base/src/org/compiere/util/ResetCacheCallable.java b/org.adempiere.base/src/org/compiere/util/ResetCacheCallable.java index cb817bf1a9..9e537d75be 100644 --- a/org.adempiere.base/src/org/compiere/util/ResetCacheCallable.java +++ b/org.adempiere.base/src/org/compiere/util/ResetCacheCallable.java @@ -32,7 +32,7 @@ public class ResetCacheCallable implements Callable, Serializable private String tableName; private int Record_ID; - protected ResetCacheCallable(String tableName, int Record_ID) + public ResetCacheCallable(String tableName, int Record_ID) { this.tableName = tableName; this.Record_ID = Record_ID; diff --git a/org.adempiere.server/src/main/server/org/compiere/server/AdempiereServerMgr.java b/org.adempiere.server/src/main/server/org/compiere/server/AdempiereServerMgr.java index 4c139f6b65..51a219dbfc 100644 --- a/org.adempiere.server/src/main/server/org/compiere/server/AdempiereServerMgr.java +++ b/org.adempiere.server/src/main/server/org/compiere/server/AdempiereServerMgr.java @@ -40,6 +40,7 @@ import org.compiere.util.Env; import org.idempiere.distributed.ICacheService; import org.idempiere.distributed.IClusterMember; import org.idempiere.distributed.IClusterService; +import org.idempiere.server.cluster.ClusterServerMgr; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleListener; import org.osgi.framework.ServiceReference; @@ -840,14 +841,8 @@ public class AdempiereServerMgr implements ServiceTrackerCustomizer holder = Service.locator().locate(IClusterService.class); - IClusterService service = holder != null ? holder.getService() : null; - return service; - } - private String getClusterMemberId() { - IClusterService service = getClusterService(); + IClusterService service = ClusterServerMgr.getClusterService(); if (service != null) { IClusterMember local = service.getLocalMember(); if (local != null) diff --git a/org.adempiere.server/src/main/server/org/compiere/server/LogFileInfo.java b/org.adempiere.server/src/main/server/org/compiere/server/LogFileInfo.java new file mode 100644 index 0000000000..01ef46a27f --- /dev/null +++ b/org.adempiere.server/src/main/server/org/compiere/server/LogFileInfo.java @@ -0,0 +1,105 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.compiere.server; + +import java.io.File; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.compiere.util.CLogFile; + +/** + * @author hengsin + * + */ +public class LogFileInfo implements Serializable { + + /** + * generated serial id + */ + private static final long serialVersionUID = -5841677623091843473L; + + private String fileName; + private long fileSize; + + + private LogFileInfo() { + } + + + /** + * + * @return LogFileInfo[] + */ + public static LogFileInfo[] getLogFileInfos() { + List list = new ArrayList<>(); + CLogFile fileHandler = CLogFile.get (true, null, false); + File logDir = fileHandler.getLogDirectory(); + if (logDir != null && logDir.isDirectory()) + { + File[] logs = logDir.listFiles(); + for (int i = 0; i < logs.length; i++) + { + // Skip if is not a file - teo_sarca [ 1726066 ] + if (!logs[i].isFile()) + continue; + + LogFileInfo lfi = new LogFileInfo(); + lfi.fileName = logs[i].getAbsolutePath(); + lfi.fileSize = logs[i].length(); + list.add(lfi); + } + } + + return list.toArray(new LogFileInfo[0]); + } + + + /** + * + * @return current log file name + */ + public static String getCurrentLogFile() { + CLogFile fileHandler = CLogFile.get (true, null, false); + return fileHandler.getFileName(); + } + + /** + * @return the fileName + */ + public String getFileName() { + return fileName; + } + + + /** + * @return the fileSize + */ + public long getFileSize() { + return fileSize; + } +} diff --git a/org.adempiere.server/src/main/server/org/compiere/server/SystemInfo.java b/org.adempiere.server/src/main/server/org/compiere/server/SystemInfo.java new file mode 100644 index 0000000000..8192c8537a --- /dev/null +++ b/org.adempiere.server/src/main/server/org/compiere/server/SystemInfo.java @@ -0,0 +1,360 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.compiere.server; + +import java.io.Serializable; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; +import java.net.InetAddress; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.logging.Level; + +import org.compiere.db.AdempiereDatabase; +import org.compiere.db.CConnection; +import org.compiere.model.MSession; +import org.compiere.util.CLogMgt; +import org.compiere.util.CMemoryUsage; +import org.idempiere.distributed.IClusterMember; +import org.idempiere.distributed.IClusterService; +import org.idempiere.server.cluster.ClusterServerMgr; +import org.idempiere.server.cluster.callable.GetSystemInfoCallable; + +/** + * @author hengsin + * + */ +public class SystemInfo implements Serializable { + + /** + * generated serial id + */ + private static final long serialVersionUID = -4451616690416295597L; + + private String operatingSystem; + private String javaVM; + private String databaseDescription; + private String databaseConnectionURL; + private String databaseStatus; + private String memoryUsage; + private String heapMemoryUsage; + private String runtimeName; + private long runtimeUpTime; + private int threadCount; + private int peakThreadCount; + private int daemonThreadCount; + private long totalStartedThreadCount; + private TrxInfo[] trxInfos; + private Level logLevel; + private String currentLogFile; + private LogFileInfo[] logFileInfos; + private long garbageCollectionTime; + private long garbageCollectionCount; + private int availableProcessors; + private double averageSystemLoad; + + private int sessionCount; + + private InetAddress address; + + /** + * + */ + private SystemInfo() { + } + + /** + * @return the operatingSystem + */ + public String getOperatingSystem() { + return operatingSystem; + } + + /** + * @return the javaVM + */ + public String getJavaVM() { + return javaVM; + } + + /** + * @return the databaseDescription + */ + public String getDatabaseDescription() { + return databaseDescription; + } + + /** + * @return the databaseConnectionURL + */ + public String getDatabaseConnectionURL() { + return databaseConnectionURL; + } + + /** + * @return the databaseStatus + */ + public String getDatabaseStatus() { + return databaseStatus; + } + + /** + * @return the memoryUsage + */ + public String getMemoryUsage() { + return memoryUsage; + } + + /** + * @return the heapMemoryUsage + */ + public String getHeapMemoryUsage() { + return heapMemoryUsage; + } + + /** + * @return the runtimeName + */ + public String getRuntimeName() { + return runtimeName; + } + + /** + * @return the runtimeUpTime + */ + public long getRuntimeUpTime() { + return runtimeUpTime; + } + + /** + * @return the threadCount + */ + public int getThreadCount() { + return threadCount; + } + + /** + * @return the peakThreadCount + */ + public int getPeakThreadCount() { + return peakThreadCount; + } + + /** + * @return the daemonThreadCount + */ + public int getDaemonThreadCount() { + return daemonThreadCount; + } + + /** + * @return the totalStartedThreadCount + */ + public long getTotalStartedThreadCount() { + return totalStartedThreadCount; + } + + /** + * @return the trxInfos + */ + public TrxInfo[] getTrxInfos() { + return trxInfos; + } + + /** + * @return the logLevel + */ + public Level getLogLevel() { + return logLevel; + } + + /** + * @return the currentLogFile + */ + public String getCurrentLogFile() { + return currentLogFile; + } + + /** + * @return the logFileInfos + */ + public LogFileInfo[] getLogFileInfos() { + return logFileInfos; + } + + /** + * @return the garbageCollectionTime + */ + public long getGarbageCollectionTime() { + return garbageCollectionTime; + } + + public static SystemInfo getLocalSystemInfo() { + SystemInfo si = new SystemInfo(); + OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean(); + String osInfo = os.getName() + " " + os.getVersion(); + osInfo += " (" + os.getArch() + ")"; + si.operatingSystem = osInfo; + si.availableProcessors = os.getAvailableProcessors(); + if (os instanceof com.sun.management.OperatingSystemMXBean) { + com.sun.management.OperatingSystemMXBean extInfo = (com.sun.management.OperatingSystemMXBean) os; + si.averageSystemLoad = extInfo.getSystemCpuLoad() * 100; + } else { + si.averageSystemLoad = (os.getSystemLoadAverage() / si.availableProcessors) * 100; + } + + String vm = System.getProperty("java.vm.name") + " " + System.getProperty("java.vm.version"); + si.javaVM = vm; + CConnection cc = CConnection.get(); + AdempiereDatabase db = cc.getDatabase(); + si.databaseDescription = db.getDescription(); + si.databaseConnectionURL = cc.getConnectionURL(); + si.databaseStatus = cc.getDatabase().getStatus(); + MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); + si.memoryUsage = new CMemoryUsage(memory.getNonHeapMemoryUsage()).toString(); + si.heapMemoryUsage = new CMemoryUsage(memory.getHeapMemoryUsage()).toString(); + RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean(); + si.runtimeName = rt.getName(); + si.runtimeUpTime = rt.getUptime(); + ThreadMXBean th = ManagementFactory.getThreadMXBean(); + si.threadCount = th.getThreadCount(); + si.peakThreadCount = th.getPeakThreadCount(); + si.daemonThreadCount = th.getDaemonThreadCount(); + si.totalStartedThreadCount = th.getTotalStartedThreadCount(); + si.trxInfos = TrxInfo.getActiveTransactions(); + si.logLevel = CLogMgt.getLevel(); + si.currentLogFile = LogFileInfo.getCurrentLogFile(); + si.logFileInfos = LogFileInfo.getLogFileInfos(); + si.garbageCollectionTime = ManagementFactory.getGarbageCollectorMXBeans().stream().mapToLong(mxBean -> mxBean.getCollectionTime()).sum(); + si.garbageCollectionCount = ManagementFactory.getGarbageCollectorMXBeans().stream().mapToLong(mxBean -> mxBean.getCollectionCount()).sum(); + si.sessionCount = MSession.getCachedSessionCount(); + + return si; + } + + /** + * @return the serialversionuid + */ + public static long getSerialversionuid() { + return serialVersionUID; + } + + /** + * @return the garbageCollectionCount + */ + public long getGarbageCollectionCount() { + return garbageCollectionCount; + } + + + /** + * @return the availableProcessors + */ + public int getAvailableProcessors() { + return availableProcessors; + } + + /** + * @return the averageSystemLoad + */ + public double getAverageSystemLoad() { + return averageSystemLoad; + } + + + /** + * @return the sessionCount + */ + public int getSessionCount() { + return sessionCount; + } + + /** + * @return the address + */ + public InetAddress getAddress() { + return address; + } + + /** + * + * @param nodeId + * @return systeminfo for cluster node + */ + public static SystemInfo getClusterNodeInfo(String nodeId) { + SystemInfo si = null; + + IClusterMember member = ClusterServerMgr.getClusterMember(nodeId); + if (member != null) { + GetSystemInfoCallable callable = new GetSystemInfoCallable(); + Future future = ClusterServerMgr.getClusterService().execute(callable, member); + if (future != null) { + try { + si = future.get(); + si.address = member.getAddress(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + } + + return si; + } + + /** + * + * @param member + * @return session count for cluster node + */ + public static int getClusterSessionCount(IClusterMember member) { + IClusterService service = ClusterServerMgr.getClusterService(); + if (service != null) { + GetSessionCountCallable callable = new GetSessionCountCallable(); + Future future = service.execute(callable, member); + if (future != null) { + try { + return future.get(); + } catch (InterruptedException | ExecutionException e) { + } + } + } + return 0; + } + + private static class GetSessionCountCallable implements Callable, Serializable { + private static final long serialVersionUID = -7793108679625240698L; + + @Override + public Integer call() throws Exception { + return MSession.getCachedSessionCount(); + } + + } +} diff --git a/org.adempiere.server/src/main/server/org/compiere/server/TrxInfo.java b/org.adempiere.server/src/main/server/org/compiere/server/TrxInfo.java new file mode 100644 index 0000000000..820a41c647 --- /dev/null +++ b/org.adempiere.server/src/main/server/org/compiere/server/TrxInfo.java @@ -0,0 +1,91 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.compiere.server; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.compiere.util.Trx; + +/** + * @author hengsin + * + */ +public class TrxInfo implements Serializable { + + /** + * generated serial id + */ + private static final long serialVersionUID = -4002703843474813148L; + + private String displayName; + private Date startTime; + private String stackTrace; + + /** + * + */ + private TrxInfo() { + } + + public static TrxInfo[] getActiveTransactions() { + List list = new ArrayList<>(); + Trx[] trxs = Trx.getActiveTransactions(); + for (Trx trx : trxs) { + if (trx != null && trx.isActive()) { + TrxInfo ti = new TrxInfo(); + ti.displayName = trx.getDisplayName(); + ti.startTime = trx.getStartTime(); + ti.stackTrace = trx.getStrackTrace(); + list.add(ti); + } + } + return list.toArray(new TrxInfo[0]); + } + + /** + * @return the displayName + */ + public String getDisplayName() { + return displayName; + } + + /** + * @return the startTime + */ + public Date getStartTime() { + return startTime; + } + + /** + * @return the stackTrace + */ + public String getStackTrace() { + return stackTrace; + } +} diff --git a/org.adempiere.server/src/main/server/org/idempiere/server/cluster/ClusterServerMgr.java b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/ClusterServerMgr.java index 6b4d957ead..0ebd8a3418 100644 --- a/org.adempiere.server/src/main/server/org/idempiere/server/cluster/ClusterServerMgr.java +++ b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/ClusterServerMgr.java @@ -79,12 +79,33 @@ public class ClusterServerMgr implements IServerManager { private ClusterServerMgr() { } - private IClusterService getClusterService() { + /** + * + * @return cluster service + */ + public static IClusterService getClusterService() { IServiceHolder holder = Service.locator().locate(IClusterService.class); IClusterService service = holder != null ? holder.getService() : null; return service; } + /** + * + * @param nodeId + * @return cluster member node + */ + public static IClusterMember getClusterMember(String nodeId) { + IClusterService service = getClusterService(); + if (service != null) { + Collection members = service.getMembers(); + for(IClusterMember member : members) { + if (member.getId().equals(nodeId)) + return member; + } + } + return null; + } + @Override public ServerInstance getServerInstance(String serverId) { IClusterService service = getClusterService(); diff --git a/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/DeleteLogsCallable.java b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/DeleteLogsCallable.java new file mode 100644 index 0000000000..ac3457b63b --- /dev/null +++ b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/DeleteLogsCallable.java @@ -0,0 +1,72 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.idempiere.server.cluster.callable; + +import java.io.File; +import java.io.Serializable; +import java.util.concurrent.Callable; + +import org.adempiere.util.LogAuthFailure; +import org.compiere.util.CLogFile; + +/** + * @author hengsin + * + */ +public class DeleteLogsCallable implements Callable, Serializable { + + /** + * generated serial id + */ + private static final long serialVersionUID = -5830938669376247267L; + + /** + * + */ + public DeleteLogsCallable() { + } + + @Override + public Boolean call() throws Exception { + CLogFile fileHandler = CLogFile.get (false, null, false); + // + File logDir = fileHandler.getLogDirectory(); + if (logDir != null && logDir.isDirectory()) + { + File[] logs = logDir.listFiles(); + for (int i = 0; i < logs.length; i++) + { + String fileName = logs[i].getAbsolutePath(); + if (fileName.equals(fileHandler.getFileName())) + continue; + if (fileName.endsWith(LogAuthFailure.authFailureFilename)) // Do not delete login failure + continue; + logs[i].delete(); + } + } + return true; + } +} diff --git a/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/GetSystemInfoCallable.java b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/GetSystemInfoCallable.java new file mode 100644 index 0000000000..7466cf2d64 --- /dev/null +++ b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/GetSystemInfoCallable.java @@ -0,0 +1,55 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.idempiere.server.cluster.callable; + +import java.io.Serializable; +import java.util.concurrent.Callable; + +import org.compiere.server.SystemInfo; + +/** + * @author hengsin + * + */ +public class GetSystemInfoCallable implements Callable, Serializable { + + /** + * generated serial + */ + private static final long serialVersionUID = 3496041492358893501L; + + /** + * default constructor + */ + public GetSystemInfoCallable() { + } + + @Override + public SystemInfo call() throws Exception { + return SystemInfo.getLocalSystemInfo(); + } + +} diff --git a/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/ReadLogCallable.java b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/ReadLogCallable.java new file mode 100644 index 0000000000..663a7d00ad --- /dev/null +++ b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/ReadLogCallable.java @@ -0,0 +1,142 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.idempiere.server.cluster.callable; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.logging.Level; + +import org.compiere.Adempiere; +import org.compiere.util.CLogFile; +import org.compiere.util.CLogger; + +/** + * @author hengsin + * + */ +public class ReadLogCallable implements Callable, Serializable { + + /** + * generated serial id + */ + private static final long serialVersionUID = 33969865104073117L; + private static final String s_dirAccessFileName = "dirAccess.txt"; + private static CLogger log = CLogger.getCLogger(ReadLogCallable.class); + + private String fileName; + + /** + * default constructor + */ + public ReadLogCallable(String fileName) { + this.fileName = fileName; + } + + @Override + public byte[] call() throws Exception { + CLogFile fileHandler = CLogFile.get(false, null, false); + // + // Display current log File + if (fileHandler != null && fileHandler.getFileName().equals(fileName)) + fileHandler.flush(); + + File file = new File(fileName); + if (!file.exists() || !file.canRead()) { + return null; + } + if (file.length() == 0) { + return new byte[0]; + } + + boolean found = false; + ArrayList dirAccessList = getDirAcessList(); + + for (File dir : dirAccessList) { + if (file.getCanonicalPath().startsWith(dir.getAbsolutePath())) { + found = true; + break; + } + } + + if (!found) { + log.warning("Couldn't find file in directories that allowed to access"); + return null; + } + + // Stream Log + try (FileInputStream fis = new FileInputStream(file)) { + int bufferSize = 2048; // 2k Buffer + byte[] buffer = new byte[bufferSize]; + // + ByteArrayOutputStream baos = new ByteArrayOutputStream(bufferSize); + // + int read = 0; + while ((read = fis.read(buffer)) > 0) + baos.write(buffer, 0, read); + return baos.toByteArray(); + } catch (Exception ex) { + log.log(Level.SEVERE, "stream" + ex); + return null; + } + } + + private ArrayList getDirAcessList() { + final ArrayList dirAccessList = new ArrayList(); + + // by default has access to log directory + CLogFile fileHandler = CLogFile.get(true, null, false); + File logDir = fileHandler.getLogDirectory(); + dirAccessList.add(logDir); + + // load from dirAccess.properties file + String dirAccessPathName = Adempiere.getAdempiereHome() + File.separator + s_dirAccessFileName; + File dirAccessFile = new File(dirAccessPathName); + if (dirAccessFile.exists()) { + try { + BufferedReader br = new BufferedReader(new FileReader(dirAccessFile)); + while (true) { + String pathName = br.readLine(); + if (pathName == null) + break; + File pathDir = new File(pathName); + if (pathDir.exists() && !dirAccessList.contains(pathDir)) + dirAccessList.add(pathDir); + } + br.close(); + } catch (Exception e) { + log.log(Level.SEVERE, dirAccessPathName + " - " + e.toString()); + } + } + + return dirAccessList; + } +} diff --git a/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/RotateLogCallable.java b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/RotateLogCallable.java new file mode 100644 index 0000000000..0963fb2acc --- /dev/null +++ b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/RotateLogCallable.java @@ -0,0 +1,61 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.idempiere.server.cluster.callable; + +import java.io.Serializable; +import java.util.concurrent.Callable; + +import org.compiere.util.CLogFile; + +/** + * @author hengsin + * + */ +public class RotateLogCallable implements Callable, Serializable { + + /** + * generated serial id + */ + private static final long serialVersionUID = 33969865104073117L; + + /** + * default constructor + */ + public RotateLogCallable() { + } + + @Override + public Boolean call() throws Exception { + CLogFile fileHandler = CLogFile.get (false, null, false); + // + if (fileHandler != null) { + fileHandler.rotateLog(); + return true; + } else { + return false; + } + } +} diff --git a/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/SetTraceLevelCallable.java b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/SetTraceLevelCallable.java new file mode 100644 index 0000000000..6323a24277 --- /dev/null +++ b/org.adempiere.server/src/main/server/org/idempiere/server/cluster/callable/SetTraceLevelCallable.java @@ -0,0 +1,61 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +* * +* Contributors: * +* - Trek Global Corporation * +* - Heng Sin Low * +**********************************************************************/ +package org.idempiere.server.cluster.callable; + +import java.io.Serializable; +import java.util.concurrent.Callable; + +import org.compiere.util.CLogMgt; +import org.compiere.util.Ini; + +/** + * @author hengsin + * + */ +public class SetTraceLevelCallable implements Callable, Serializable { + + /** + * generated serial idd + */ + private static final long serialVersionUID = 6699443869769763231L; + + private String traceLevel; + + /** + * + */ + public SetTraceLevelCallable(String traceLevel) { + this.traceLevel = traceLevel; + } + + @Override + public Boolean call() throws Exception { + CLogMgt.setLevel(traceLevel); + Ini.setProperty(Ini.P_TRACELEVEL, traceLevel); + Ini.saveProperties(false); + return true; + } +} diff --git a/org.adempiere.server/src/main/servlet/org/compiere/web/AdempiereMonitor.java b/org.adempiere.server/src/main/servlet/org/compiere/web/AdempiereMonitor.java index 5bb28f85c4..1e56f04d49 100644 --- a/org.adempiere.server/src/main/servlet/org/compiere/web/AdempiereMonitor.java +++ b/org.adempiere.server/src/main/servlet/org/compiere/web/AdempiereMonitor.java @@ -22,13 +22,10 @@ import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; -import java.lang.management.RuntimeMXBean; -import java.lang.management.ThreadMXBean; import java.net.InetAddress; import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Properties; import java.util.logging.Level; @@ -40,9 +37,6 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.adempiere.base.IServiceHolder; -import org.adempiere.base.Service; -import org.adempiere.util.LogAuthFailure; import org.apache.ecs.HtmlColor; import org.apache.ecs.xhtml.a; import org.apache.ecs.xhtml.b; @@ -63,8 +57,6 @@ import org.apache.ecs.xhtml.td; import org.apache.ecs.xhtml.th; import org.apache.ecs.xhtml.tr; import org.compiere.Adempiere; -import org.compiere.db.AdempiereDatabase; -import org.compiere.db.CConnection; import org.compiere.model.AdempiereProcessorLog; import org.compiere.model.MClient; import org.compiere.model.MSession; @@ -75,24 +67,30 @@ import org.compiere.model.Query; import org.compiere.server.AdempiereServerGroup; import org.compiere.server.AdempiereServerMgr; import org.compiere.server.IServerManager; +import org.compiere.server.LogFileInfo; import org.compiere.server.ServerCount; import org.compiere.server.ServerInstance; +import org.compiere.server.SystemInfo; +import org.compiere.server.TrxInfo; import org.compiere.util.CLogFile; import org.compiere.util.CLogMgt; import org.compiere.util.CLogger; -import org.compiere.util.CMemoryUsage; +import org.compiere.util.CacheInfo; import org.compiere.util.CacheMgt; import org.compiere.util.DB; import org.compiere.util.Env; -import org.compiere.util.Ini; import org.compiere.util.TimeUtil; -import org.compiere.util.Trx; import org.compiere.util.Util; import org.compiere.util.WebDoc; import org.compiere.util.WebEnv; import org.compiere.util.WebUtil; +import org.idempiere.distributed.IClusterMember; import org.idempiere.distributed.IClusterService; import org.idempiere.server.cluster.ClusterServerMgr; +import org.idempiere.server.cluster.callable.DeleteLogsCallable; +import org.idempiere.server.cluster.callable.ReadLogCallable; +import org.idempiere.server.cluster.callable.RotateLogCallable; +import org.idempiere.server.cluster.callable.SetTraceLevelCallable; /** * Adempiere Server Monitor @@ -155,6 +153,12 @@ public class AdempiereMonitor extends HttpServlet createXMLSummaryPage(request, response); return; } + if (processNodeInfoPage(request, response)) + { + if (xmlOutput) + createXMLSummaryPage(request, response); + return; + } // if (processRunNowParameter (request)) ; @@ -366,12 +370,26 @@ public class AdempiereMonitor extends HttpServlet { String traceCmd = WebUtil.getParameter (request, "Trace"); String traceLevel = WebUtil.getParameter (request, "TraceLevel"); + String nodeId = WebUtil.getParameter (request, "nodeId"); if (traceLevel != null && traceLevel.length() > 0) { if (log.isLoggable(Level.INFO)) log.info ("New Level: " + traceLevel); - CLogMgt.setLevel(traceLevel); - Ini.setProperty(Ini.P_TRACELEVEL, traceLevel); - Ini.saveProperties(false); + SetTraceLevelCallable callable = new SetTraceLevelCallable(traceLevel); + try + { + if (!Util.isEmpty(nodeId, true)) + { + ClusterServerMgr.getClusterService().execute(callable, ClusterServerMgr.getClusterMember(nodeId)).get(); + } + else + { + callable.call(); + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } return false; } @@ -383,32 +401,68 @@ public class AdempiereMonitor extends HttpServlet // if (traceCmd.equals("ROTATE")) { - if (fileHandler != null) - fileHandler.rotateLog(); + RotateLogCallable callable = new RotateLogCallable(); + try + { + if (!Util.isEmpty(nodeId, true)) + { + ClusterServerMgr.getClusterService().execute(callable, ClusterServerMgr.getClusterMember(nodeId)).get(); + } + else + { + callable.call(); + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } return false; // re-display } else if (traceCmd.equals("DELETE")) { - File logDir = fileHandler.getLogDirectory(); - if (logDir != null && logDir.isDirectory()) + DeleteLogsCallable callable = new DeleteLogsCallable(); + try { - File[] logs = logDir.listFiles(); - for (int i = 0; i < logs.length; i++) + if (!Util.isEmpty(nodeId, true)) { - String fileName = logs[i].getAbsolutePath(); - if (fileName.equals(fileHandler.getFileName())) - continue; - if (fileName.endsWith(LogAuthFailure.authFailureFilename)) // Do not delete login failure - continue; - if (logs[i].delete()) - log.warning("Deleted: " + fileName); - else - log.warning("Not Deleted: " + fileName); + ClusterServerMgr.getClusterService().execute(callable, ClusterServerMgr.getClusterMember(nodeId)).get(); + } + else + { + callable.call(); } + } + catch (Exception e) + { + throw new RuntimeException(e); } return false; // re-display } + if (!Util.isEmpty(nodeId, true)) + { + ReadLogCallable callable = new ReadLogCallable(traceCmd); + try { + byte[] contents = ClusterServerMgr.getClusterService().execute(callable, ClusterServerMgr.getClusterMember(nodeId)).get(); + if (contents == null || contents.length == 0) + return false; + + try(ServletOutputStream out = response.getOutputStream ()) + { + response.setContentType("text/plain"); + response.setBufferSize(2048); + response.setContentLength(contents.length); + out.write(contents); + out.flush(); + } + return true; + } catch (Exception e) { + log.log(Level.WARNING, e.getMessage(), e); + return false; + } + } + // Display current log File if (fileHandler != null && fileHandler.getFileName().equals(traceCmd)) fileHandler.flush(); @@ -538,7 +592,8 @@ public class AdempiereMonitor extends HttpServlet { String cmd = WebUtil.getParameter (request, "CacheReset"); if (cmd == null || cmd.length() == 0) - return false; + return createCacheDetailsPage(request, response); + String tableName = WebUtil.getParameter (request, "CacheTableName"); String record_ID = WebUtil.getParameter (request, "CacheRecord_ID"); @@ -628,15 +683,56 @@ public class AdempiereMonitor extends HttpServlet table.addElement(line); bb.addElement(table); - IServiceHolder holder = Service.locator().locate(IClusterService.class); - IClusterService service = holder != null ? holder.getService() : null; - if (service != null && service.getLocalMember() != null) + IClusterService service = ClusterServerMgr.getClusterService(); + Collection members = null; + IClusterMember local = null; + if (service != null) { - line = new tr(); - line.addElement(new th().addElement("Cluster Node Id")); - line.addElement(new td().addElement(WebEnv.getCellContent(service.getLocalMember().getId()))); - table.addElement(line); - bb.addElement(table); + members = service.getMembers(); + local = service.getLocalMember(); + if (members.size() > 1 && local != null) + { + line = new tr(); + line.addElement(new th().addElement("Cluster Nodes")); + p para = new p(); + StringBuilder nodeBuilder = new StringBuilder(local.getId()); + InetAddress address = local.getAddress(); + String ip = address != null ? address.getHostAddress() : null; + if (ip != null && + (ip.startsWith("10.") || + ip.startsWith("172.16") || + ip.startsWith("192.168"))) + { + nodeBuilder.append(" (").append(ip).append(")"); + } + para.addElement(nodeBuilder.toString()); + + for(IClusterMember member : members) + { + if (member.getId().equals(local.getId())) + { + continue; + } + else + { + para.addElement(" - "); + nodeBuilder = new StringBuilder(member.getId()); + address = member.getAddress(); + ip = address != null ? address.getHostAddress() : null; + if (ip != null && + (ip.startsWith("10.") || + ip.startsWith("172.16") || + ip.startsWith("192.168"))) + { + nodeBuilder.append(" (").append(ip).append(")"); + } + a link = new a ("idempiereMonitor?NodeInfo="+member.getId(), nodeBuilder.toString()); + para.addElement(link); + } + } + line.addElement(new td().addElement(para)); + table.addElement(line); + } } // p para = new p(); @@ -674,7 +770,7 @@ public class AdempiereMonitor extends HttpServlet bb.addElement(para); // **** Log Management **** - createLogMgtPage(bb); + createLogMgtPage(bb, members, local); // ***** Server Details ***** bb.removeEndEndModifier(); @@ -749,7 +845,7 @@ public class AdempiereMonitor extends HttpServlet line.addElement(new td().addElement(server.getStatistics())); table.addElement(line); // - if (server.getClusterMember() != null) + if (server.getClusterMember() != null && members != null && members.size() > 1) { InetAddress address = server.getClusterMember().getAddress(); String ip = address != null ? address.getHostAddress() : null; @@ -785,7 +881,7 @@ public class AdempiereMonitor extends HttpServlet // fini WebUtil.createResponse (request, response, this, null, doc, false); } // createSummaryPage - + private String createServerCountMessage(ServerCount serverCount) { StringBuilder builder = new StringBuilder(); @@ -904,8 +1000,10 @@ public class AdempiereMonitor extends HttpServlet /** * Add Log Management to page * @param bb body + * @param members + * @param local */ - private void createLogMgtPage (body bb) + private void createLogMgtPage (body bb, Collection members, IClusterMember local) { bb.addElement(new hr()); @@ -917,95 +1015,91 @@ public class AdempiereMonitor extends HttpServlet // Properties ctx = new Properties(); MSystem system = MSystem.get(ctx); + SystemInfo systemInfo = SystemInfo.getLocalSystemInfo(); tr line = new tr(); - line.addElement(new th().addElement(system.getDBAddress())); - line.addElement(new td().addElement(Ini.getAdempiereHome())); + line.addElement(new th().addElement(Adempiere.getURL())); + line.addElement(new td().addElement(Adempiere.getAdempiereHome())); table.addElement(line); // OS + Name line = new tr(); - String info = System.getProperty("os.name") - + " " + System.getProperty("os.version"); - String s = System.getProperty("sun.os.patch.level"); - if (s != null && s.length() > 0) - info += " (" + s + ")"; - line.addElement(new th().addElement(info)); - info = system.getName(); + line.addElement(new th().addElement(systemInfo.getOperatingSystem())); + String info = system.getName(); if (system.getCustomPrefix() != null) info += " (" + system.getCustomPrefix() + ")"; line.addElement(new td().addElement(info)); table.addElement(line); // Java + email line = new tr(); - info = System.getProperty("java.vm.name") - + " " + System.getProperty("java.vm.version"); - line.addElement(new th().addElement(info)); - line.addElement(new td().addElement(system.getUserName())); + line.addElement(new th().addElement(systemInfo.getJavaVM())); + line.addElement(new td().addElement(system.getSupportEMail())); table.addElement(line); // DB + Instance line = new tr(); - CConnection cc = CConnection.get(); - AdempiereDatabase db = cc.getDatabase(); - info = db.getDescription(); - line.addElement(new th().addElement(info)); - line.addElement(new td().addElement(cc.getConnectionURL())); + line.addElement(new th().addElement(systemInfo.getDatabaseDescription())); + line.addElement(new td().addElement(systemInfo.getDatabaseConnectionURL())); table.addElement(line); line = new tr(); line.addElement(new th().addElement("DB Connection Pool")); - line.addElement(new td().addElement(cc.getDatabase().getStatus())); + line.addElement(new td().addElement(systemInfo.getDatabaseStatus())); table.addElement(line); // Processors/Support line = new tr(); - line.addElement(new th().addElement("Processor/Support")); - line.addElement(new td().addElement(system.getNoProcessors() + "/" + system.getSupportUnits())); + line.addElement(new th().addElement("Processor")); + line.addElement(new td().addElement(systemInfo.getAvailableProcessors()+"")); table.addElement(line); + if (systemInfo.getAverageSystemLoad() >= 0) + { + line = new tr(); + line.addElement(new th().addElement("System Load")); + line.addElement(new td().addElement(systemInfo.getAverageSystemLoad()+"%")); + table.addElement(line); + } // Memory line = new tr(); - MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); line.addElement(new th().addElement("VM Memory")); - line.addElement(new td().addElement(new CMemoryUsage(memory.getNonHeapMemoryUsage()).toString())); + line.addElement(new td().addElement(systemInfo.getMemoryUsage())); table.addElement(line); line = new tr(); line.addElement(new th().addElement("Heap Memory")); - line.addElement(new td().addElement(new CMemoryUsage(memory.getHeapMemoryUsage()).toString())); + line.addElement(new td().addElement(systemInfo.getHeapMemoryUsage())); table.addElement(line); // Runtime line = new tr(); - RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean(); - line.addElement(new th().addElement("Runtime " + rt.getName())); - line.addElement(new td().addElement(TimeUtil.formatElapsed(rt.getUptime()))); + line.addElement(new th().addElement("Runtime " + systemInfo.getRuntimeName())); + line.addElement(new td().addElement(TimeUtil.formatElapsed(systemInfo.getRuntimeUpTime()))); table.addElement(line); // Threads line = new tr(); - ThreadMXBean th = ManagementFactory.getThreadMXBean(); - line.addElement(new th().addElement("Threads " + th.getThreadCount())); - line.addElement(new td().addElement("Peak=" + th.getPeakThreadCount() - + ", Demons=" + th.getDaemonThreadCount() - + ", Total=" + th.getTotalStartedThreadCount())); + line.addElement(new th().addElement("Threads " + systemInfo.getThreadCount())); + line.addElement(new td().addElement("Peak=" + systemInfo.getPeakThreadCount() + + ", Daemons=" + systemInfo.getDaemonThreadCount() + + ", Total=" + systemInfo.getTotalStartedThreadCount())); table.addElement(line); //Transactions - Trx[] trxs = Trx.getActiveTransactions(); - for (Trx trx : trxs) + TrxInfo[] trxs = systemInfo.getTrxInfos(); + for (TrxInfo trx : trxs) { - if (trx != null && trx.isActive()) - { - line = new tr(); - line.addElement(new th().addElement("Active Transaction ")); - td td = new td(); - td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('" + escapeEcmaScript(trx.getDisplayName()) +"" - + "
" + escapeEcmaScript(trx.getStrackTrace()) + "
')"); - td.addElement("Name="+trx.getDisplayName() + ", StartTime=" + trx.getStartTime()); - td.setTitle("Click to see stack trace"); - td.setStyle("text-decoration: underline; color: blue"); - line.addElement(td); - table.addElement(line); - } + line = new tr(); + line.addElement(new th().addElement("Active Transaction ")); + td td = new td(); + td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('" + escapeEcmaScript(trx.getDisplayName()) +"" + + "
" + escapeEcmaScript(trx.getStackTrace()) + "
')"); + td.addElement("Name="+trx.getDisplayName() + ", StartTime=" + trx.getStartTime()); + td.setTitle("Click to see stack trace"); + td.setStyle("text-decoration: underline; color: blue"); + line.addElement(td); + table.addElement(line); } // Cache Reset line = new tr(); line.addElement(new th().addElement(CacheMgt.get().toStringX())); - line.addElement(new td().addElement(new a ("idempiereMonitor?CacheReset=Yes", "Reset Cache"))); + p cachePara = new p(); + cachePara.addElement(new a ("idempiereMonitor?CacheReset=Yes", "Reset Cache")) + .addElement(" - ") + .addElement(new a ("idempiereMonitor?CacheDetails=Yes", "Cache Details")); + line.addElement(new td().addElement(cachePara)); table.addElement(line); // Trace Level @@ -1018,7 +1112,7 @@ public class AdempiereMonitor extends HttpServlet { options[i] = new option(CLogMgt.LEVELS[i].getName()); options[i].addElement(CLogMgt.LEVELS[i].getName()); - if (CLogMgt.LEVELS[i] == CLogMgt.getLevel()) + if (CLogMgt.LEVELS[i] == systemInfo.getLogLevel()) options[i].setSelected(true); } select sel = new select("TraceLevel", options); @@ -1028,14 +1122,17 @@ public class AdempiereMonitor extends HttpServlet table.addElement(line); // line = new tr(); - CLogFile fileHandler = CLogFile.get (true, null, false); line.addElement(new th().addElement("Trace File")); - line.addElement(new td().addElement(new a ("idempiereMonitor?Trace=" + fileHandler.getFileName(), "Current"))); + line.addElement(new td().addElement(new a ("idempiereMonitor?Trace=" + systemInfo.getCurrentLogFile(), "Current"))); table.addElement(line); // line = new tr(); - line.addElement(new td().addElement(new a ("idempiereMonitor?Trace=ROTATE", "Rotate Trace Log"))); - line.addElement(new td().addElement(new a ("idempiereMonitor?Trace=DELETE", "Delete all Trace Logs"))); + p tlp = new p(); + tlp.addElement(new a ("idempiereMonitor?Trace=ROTATE", "Rotate Trace Log")) + .addElement(" - ") + .addElement(new a ("idempiereMonitor?Trace=DELETE", "Delete all Trace Logs")); + line.addElement(new th()); + line.addElement(new td().addElement(tlp)); table.addElement(line); // bb.addElement(table); @@ -1044,28 +1141,20 @@ public class AdempiereMonitor extends HttpServlet p p = new p(); p.addElement(new b("All Log Files: ")); // All in dir - File logDir = fileHandler.getLogDirectory(); - if (logDir != null && logDir.isDirectory()) + LogFileInfo logFiles[] = systemInfo.getLogFileInfos(); + for (LogFileInfo logFile : logFiles) { - File[] logs = logDir.listFiles(); - for (int i = 0; i < logs.length; i++) - { - // Skip if is not a file - teo_sarca [ 1726066 ] - if (!logs[i].isFile()) - continue; - - if (i != 0) - p.addElement(" - "); - String fileName = logs[i].getAbsolutePath(); - a link = new a ("idempiereMonitor?Trace=" + fileName, fileName); - p.addElement(link); - int size = (int)(logs[i].length()/1024); - if (size < 1024) - p.addElement(" (" + size + "k)"); - else - p.addElement(" (" + size/1024 + "M)"); - } - } + if (logFile != logFiles[0]) + p.addElement(" - "); + String fileName = logFile.getFileName(); + a link = new a ("idempiereMonitor?Trace=" + fileName, fileName); + p.addElement(link); + int size = (int)(logFile.getFileSize()/1024); + if (size < 1024) + p.addElement(" (" + size + "k)"); + else + p.addElement(" (" + size/1024 + "M)"); + } bb.addElement(p); // Clients and Web Stores @@ -1131,6 +1220,20 @@ public class AdempiereMonitor extends HttpServlet p.addElement(" "); line.addElement(new td().addElement(p)); table.addElement(line); + if (members != null && members.size() > 1) { + line = new tr(); + line.addElement(new th().addElement("")); + p = null; + for(IClusterMember member : members) { + if (p == null) + p = new p(); + else + p.addElement(" - "); + p.addElement(member.getId() + " : " + ((member.getId().equals(local.getId())) ? systemInfo.getSessionCount() : SystemInfo.getClusterSessionCount(member))); + } + line.addElement(new td().addElement(p)); + table.addElement(line); + } // line = new tr(); @@ -1187,9 +1290,7 @@ public class AdempiereMonitor extends HttpServlet m_serverMgr = AdempiereServerMgr.get(); //switch to cluster manager if cluster service is available - IServiceHolder holder = Service.locator().locate(IClusterService.class); - IClusterService service = holder != null ? holder.getService() : null; - if (service != null) + if (ClusterServerMgr.getClusterService() != null) m_serverMgr = ClusterServerMgr.getInstance(); m_dirAccessList = getDirAcessList(); @@ -1307,4 +1408,257 @@ public class AdempiereMonitor extends HttpServlet input = input.replace("\t", "\\t"); return input; } + + /** + * return cache details page + * @param request request + * @param response response + * @return true if it was a cache details request + * @throws ServletException + * @throws IOException + */ + private boolean createCacheDetailsPage (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + String cmd = WebUtil.getParameter (request, "CacheDetails"); + if (cmd == null || cmd.length() == 0) + return false; + + WebDoc doc = WebDoc.create ("iDempiere Server Cache Details"); + // Body + body b = doc.getBody(); + // + p para = new p(); + a link = new a ("idempiereMonitor", "Return"); + para.addElement(link); + b.addElement(para); + // + table table = new table(); + table.setBorder(1); + table.setCellSpacing(2); + table.setCellPadding(2); + + // Header + tr line = new tr(); + line.addElement(new th().addElement("Name")); + line.addElement(new th().addElement("Table Name")); + line.addElement(new th().addElement("Size")); + line.addElement(new th().addElement("Expire (Minutes)")); + line.addElement(new th().addElement("Max Size")); + line.addElement(new th().addElement("Distributed")); + table.addElement(line); + + List instances = CacheInfo.getCacheInfos(true); + if (instances.size() > 0 && instances.get(0).getNodeId() != null) + { + line.addElement(new th().addElement("Node Id")); + } + for (CacheInfo ccache : instances) + { + line = new tr(); + line.addElement(new td().addElement(WebEnv.getCellContent(ccache.getName()))); + line.addElement(new td().addElement(WebEnv.getCellContent(ccache.getTableName()))); + line.addElement(new td().addElement(WebEnv.getCellContent(ccache.getSize()))); + line.addElement(new td().addElement(WebEnv.getCellContent(ccache.getExpireMinutes()))); + line.addElement(new td().addElement(WebEnv.getCellContent(ccache.getMaxSize()))); + line.addElement(new td().addElement(WebEnv.getCellContent(ccache.isDistributed()))); + if (ccache.getNodeId() != null) + { + line.addElement(new td().addElement(WebEnv.getCellContent(ccache.getNodeId()))); + } + table.addElement(line); + } + // + b.addElement(table); + link = new a ("#top", "Top"); + b.addElement(link); + + // fini + WebUtil.createResponse (request, response, this, null, doc, false); + return true; + } // processLogParameter + + /** + * return cache details page + * @param request request + * @param response response + * @return true if it was a cache details request + * @throws ServletException + * @throws IOException + */ + public boolean processNodeInfoPage (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + String nodeId = WebUtil.getParameter (request, "NodeInfo"); + if (nodeId == null || nodeId.length() == 0) + return false; + + WebDoc doc = WebDoc.create ("Details for node " + nodeId); + // Body + body b = doc.getBody(); + p para = new p(); + a link = new a ("idempiereMonitor", "Return"); + para.addElement(link); + b.addElement(para); + + createNodeInfoPage(b, nodeId); + + WebUtil.createResponse (request, response, this, null, doc, false); + + return true; + } + + private void createNodeInfoPage (body bb, String nodeId) + { + SystemInfo systemInfo = SystemInfo.getClusterNodeInfo(nodeId); + if (systemInfo == null) + return; + + bb.addElement(new hr()); + + table table = new table(); + table.setBorder(1); + table.setCellSpacing(2); + table.setCellPadding(2); + + InetAddress address = systemInfo.getAddress(); + String ip = address != null ? address.getHostAddress() : null; + if (ip != null && + (ip.startsWith("10.") || + ip.startsWith("172.16") || + ip.startsWith("192.168"))) + { + tr line = new tr(); + line.addElement(new th().addElement("IP Address")); + line.addElement(new td().addElement(ip)); + table.addElement(line); + } + + // OS + Name + tr line = new tr(); + line.addElement(new th().addElement(systemInfo.getOperatingSystem())); + table.addElement(line); + // Java + email + line = new tr(); + line.addElement(new th().addElement(systemInfo.getJavaVM())); + table.addElement(line); + // DB + Instance + line = new tr(); + line.addElement(new th().addElement(systemInfo.getDatabaseDescription())); + line.addElement(new td().addElement(systemInfo.getDatabaseConnectionURL())); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("DB Connection Pool")); + line.addElement(new td().addElement(systemInfo.getDatabaseStatus())); + table.addElement(line); + // Processors/Support + line = new tr(); + line.addElement(new th().addElement("Processor (Average System Load)")); + line.addElement(new td().addElement(systemInfo.getAvailableProcessors() + " (" + + systemInfo.getAverageSystemLoad() + ") ")); + table.addElement(line); + // Memory + line = new tr(); + line.addElement(new th().addElement("VM Memory")); + line.addElement(new td().addElement(systemInfo.getMemoryUsage())); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("Heap Memory")); + line.addElement(new td().addElement(systemInfo.getHeapMemoryUsage())); + table.addElement(line); + // Runtime + line = new tr(); + line.addElement(new th().addElement("Runtime " + systemInfo.getRuntimeName())); + line.addElement(new td().addElement(TimeUtil.formatElapsed(systemInfo.getRuntimeUpTime()))); + table.addElement(line); + // Threads + line = new tr(); + line.addElement(new th().addElement("Threads " + systemInfo.getThreadCount())); + line.addElement(new td().addElement("Peak=" + systemInfo.getPeakThreadCount() + + ", Daemons=" + systemInfo.getDaemonThreadCount() + + ", Total=" + systemInfo.getTotalStartedThreadCount())); + table.addElement(line); + + //Transactions + TrxInfo[] trxs = systemInfo.getTrxInfos(); + for (TrxInfo trx : trxs) + { + line = new tr(); + line.addElement(new th().addElement("Active Transaction ")); + td td = new td(); + td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('" + escapeEcmaScript(trx.getDisplayName()) +"" + + "
" + escapeEcmaScript(trx.getStackTrace()) + "
')"); + td.addElement("Name="+trx.getDisplayName() + ", StartTime=" + trx.getStartTime()); + td.setTitle("Click to see stack trace"); + td.setStyle("text-decoration: underline; color: blue"); + line.addElement(td); + table.addElement(line); + } + + // Trace Level + line = new tr(); + line.addElement(new th().addElement(new label("TraceLevel").addElement("Trace Log Level"))); + form myForm = new form("idempiereMonitor", form.METHOD_POST, form.ENC_DEFAULT); + // LogLevel Selection + option[] options = new option[CLogMgt.LEVELS.length]; + for (int i = 0; i < options.length; i++) + { + options[i] = new option(CLogMgt.LEVELS[i].getName()); + options[i].addElement(CLogMgt.LEVELS[i].getName()); + if (CLogMgt.LEVELS[i] == systemInfo.getLogLevel()) + options[i].setSelected(true); + } + select sel = new select("TraceLevel", options); + myForm.addElement(sel); + myForm.addElement(new input(input.TYPE_HIDDEN, "nodeId", nodeId)); + myForm.addElement(new input(input.TYPE_SUBMIT, "Set", "Set")); + line.addElement(new td().addElement(myForm)); + table.addElement(line); + // + line = new tr(); + line.addElement(new th().addElement("Trace File")); + line.addElement(new td().addElement(new a ("idempiereMonitor?Trace=" + systemInfo.getCurrentLogFile() + + "&nodeId=" + nodeId, "Current"))); + table.addElement(line); + // + line = new tr(); + p tlp = new p(); + tlp.addElement(new a ("idempiereMonitor?Trace=ROTATE&nodeId="+nodeId, "Rotate Trace Log")) + .addElement(" - ") + .addElement(new a ("idempiereMonitor?Trace=DELETE&nodeId="+nodeId, "Delete all Trace Logs")); + line.addElement(new th()); + line.addElement(new td().addElement(tlp)); + table.addElement(line); + // + bb.addElement(table); + + // List Log Files + p p = new p(); + p.addElement(new b("All Log Files: ")); + // All in dir + LogFileInfo logFiles[] = systemInfo.getLogFileInfos(); + for (LogFileInfo logFile : logFiles) + { + if (logFile != logFiles[0]) + p.addElement(" - "); + String fileName = logFile.getFileName(); + a link = new a ("idempiereMonitor?Trace=" + fileName + "&nodeId="+nodeId, fileName); + p.addElement(link); + int size = (int)(logFile.getFileSize()/1024); + if (size < 1024) + p.addElement(" (" + size + "k)"); + else + p.addElement(" (" + size/1024 + "M)"); + } + bb.addElement(p); + + // + line = new tr(); + line.addElement(new th().addElement("Active sessions for node" )); + line.addElement(new td().addElement(""+systemInfo.getSessionCount())); + table.addElement(line); + // + bb.addElement(table); + } + } // AdempiereMonitor