<%@ page contentType="text/html; charset=UTF-8" %> <%-- - - Copyright (C) 2005-2008 Jive Software, 2017-2025 Ignite Realtime Foundation. All rights reserved. - - 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. --%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <%@ taglib prefix="admin" uri="admin" %> <%@ page import="org.jivesoftware.database.DbConnectionManager" %> <%@ page import="org.jivesoftware.openfire.XMPPServer" %> <%@ page import="org.jivesoftware.openfire.cluster.ClusterManager" errorPage="error.jsp" %> <%@ page import="org.jivesoftware.openfire.cluster.ClusterNodeInfo" %> <%@ page import="org.jivesoftware.openfire.cluster.GetBasicStatistics" %> <%@ page import="org.jivesoftware.util.CookieUtils" %> <%@ page import="org.jivesoftware.util.JiveGlobals" %> <%@ page import="org.jivesoftware.util.ParamUtils" %> <%@ page import="org.jivesoftware.util.StringUtils" %> <%@ page import="org.jivesoftware.util.cache.CacheFactory" %> <%@ page import="org.slf4j.Logger" %> <%@ page import="org.slf4j.LoggerFactory" %> <%@ page import="java.net.URLEncoder" %> <%@ page import="java.nio.charset.StandardCharsets" %> <%@ page import="java.text.DecimalFormat" %> <%@ page import="org.jivesoftware.openfire.cluster.ClusterEventListener" %> <%@ page import="java.util.concurrent.Semaphore" %> <%@ page import="java.util.concurrent.TimeUnit" %> <%@ page import="org.jivesoftware.openfire.cluster.NodeID" %> <%@ page import="com.google.common.collect.Table" %> <%@ page import="com.google.common.collect.HashBasedTable" %> <%@ page import="java.util.*" %> <% webManager.init(request, response, session, application, out ); %> <fmt:message key="system.clustering.title"/> <% // Get parameters boolean update = request.getParameter("update") != null; boolean clusteringEnabled = ParamUtils.getBooleanParameter(request, "clusteringEnabled"); boolean updateSuccess = false; final Logger LOGGER = LoggerFactory.getLogger("system-clustering.jsp"); Cookie csrfCookie = CookieUtils.getCookie(request, "csrf"); String csrfParam = ParamUtils.getParameter(request, "csrf"); if (update) { if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) { update = false; } } csrfParam = StringUtils.randomString(15); CookieUtils.setCookie(request, response, "csrf", csrfParam, -1); pageContext.setAttribute("csrf", csrfParam); if (update) { if (!clusteringEnabled) { LOGGER.info("Disabling clustering"); // Log the event webManager.logEvent("disabled clustering", null); final Semaphore leftClusterSemaphore = new Semaphore(0); final ClusterEventListener listener = new ClusterEventListener() { @Override public void joinedCluster() { } @Override public void joinedCluster(byte[] nodeID) { } @Override public void leftCluster() { leftClusterSemaphore.release(); } @Override public void leftCluster(byte[] nodeID) { } @Override public void markedAsSeniorClusterMember() { } }; ClusterManager.addListener(listener); ClusterManager.setClusteringEnabled(false); try { updateSuccess = leftClusterSemaphore.tryAcquire(30, TimeUnit.SECONDS); } finally { ClusterManager.removeListener(listener); } LOGGER.info("Clustering disabled"); } else { if (ClusterManager.isClusteringAvailable()) { LOGGER.info("Enabling clustering"); // Log the event webManager.logEvent("enabled clustering", null); final Semaphore joinedClusterSemaphore = new Semaphore(0); final ClusterEventListener listener = new ClusterEventListener() { @Override public void joinedCluster() { joinedClusterSemaphore.release(); } @Override public void joinedCluster(byte[] nodeID) { } @Override public void leftCluster() { } @Override public void leftCluster(byte[] nodeID) { } @Override public void markedAsSeniorClusterMember() { } }; ClusterManager.addListener(listener); ClusterManager.setClusteringEnabled(true); try { updateSuccess = joinedClusterSemaphore.tryAcquire(30, TimeUnit.SECONDS); } finally { ClusterManager.removeListener(listener); } LOGGER.info("Clustering enabled"); } else { LOGGER.error("Failed to enable clustering. Clustering is not available."); } } } boolean usingEmbeddedDB = DbConnectionManager.isEmbeddedDB(); boolean clusteringAvailable = !usingEmbeddedDB && ClusterManager.isClusteringAvailable(); int maxClusterNodes = ClusterManager.getMaxClusterNodes(); clusteringEnabled = ClusterManager.isClusteringStarted() || ClusterManager.isClusteringStarting(); final List clusterNodesInfo = new ArrayList<>(ClusterManager.getNodesInfo()); // Sort them so they are always consistent in order clusterNodesInfo.sort(Comparator.comparing(ClusterNodeInfo::getHostName)); // Get some basic statistics from the cluster nodes // TODO Set a timeout so the page can load fast even if a node is taking too long to answer Collection> statistics = CacheFactory.doSynchronousClusterTask(new GetBasicStatistics(), true); // Calculate percentages int clients = 0; int incoming = 0; int outgoing = 0; for (Map statsMap : statistics) { if (statsMap == null) { continue; } clients += (Integer) statsMap.get(GetBasicStatistics.CLIENT); incoming += (Integer) statsMap.get(GetBasicStatistics.INCOMING); outgoing += (Integer) statsMap.get(GetBasicStatistics.OUTGOING); } for (Map statsMap : statistics) { if (statsMap == null) { continue; } int current = (Integer) statsMap.get(GetBasicStatistics.CLIENT); int percentage = clients == 0 ? 0 : current * 100 / clients; statsMap.put(GetBasicStatistics.CLIENT, current + " (" + Math.round(percentage) + "%)"); current = (Integer) statsMap.get(GetBasicStatistics.INCOMING); percentage = incoming == 0 ? 0 : current * 100 / incoming; statsMap.put(GetBasicStatistics.INCOMING, current + " (" + Math.round(percentage) + "%)"); current = (Integer) statsMap.get(GetBasicStatistics.OUTGOING); percentage = outgoing == 0 ? 0 : current * 100 / outgoing; statsMap.put(GetBasicStatistics.OUTGOING, current + " (" + Math.round(percentage) + "%)"); } final Map> allPluginVersions = ClusterManager.getPluginAndOpenfireVersions(); final Table pluginVersions = HashBasedTable.create(); final Set plugins = new TreeSet<>(); clusterNodesInfo.forEach(clusterNodeInfo -> { final NodeID nodeID = clusterNodeInfo.getNodeID(); pluginVersions.put("Openfire", nodeID, allPluginVersions.get("Openfire").get(nodeID)); allPluginVersions.forEach((pluginName, value) -> { final String pluginVersion = value.get(nodeID); plugins.add(pluginName); pluginVersions.put(pluginName, nodeID, pluginVersion == null ? "-" : pluginVersion); }); }); pageContext.setAttribute("localNodeID", XMPPServer.getInstance().getNodeID()); pageContext.setAttribute("pluginVersions", pluginVersions); pageContext.setAttribute("plugins", plugins); pageContext.setAttribute("clusteringStarted", CacheFactory.isClusteringStarted()); pageContext.setAttribute("clusterNodesInfo", clusterNodesInfo); %>

<% if (update) { if (updateSuccess) { %> <% if (ClusterManager.isClusteringStarted()) { %> <% } else { %> <% } %> <% } else { %> <% } } else if (!clusteringAvailable) { %>


<% if (usingEmbeddedDB) { %> <% } else if (maxClusterNodes == 0) { %> <% } else { %> <% } %>

<% } %>
<%= clusteringAvailable ? "" : "disabled" %>>
<%= clusteringAvailable ? "" : "disabled" %>>

<% if (clusteringAvailable) { %> "> <% } %>

<% if (!clusterNodesInfo.isEmpty()) { for (ClusterNodeInfo nodeInfo : clusterNodesInfo) { boolean isLocalMember = XMPPServer.getInstance().getNodeID().equals(nodeInfo.getNodeID()); String nodeID = Base64.getUrlEncoder().encodeToString(nodeInfo.getNodeID().toByteArray()); Map nodeStats = null; for (Map statsMap : statistics) { if (statsMap == null) { continue; } if (Arrays.equals((byte[]) statsMap.get(GetBasicStatistics.NODE), nodeInfo.getNodeID().toByteArray())) { nodeStats = statsMap; break; } } %> " style="vertical-align: middle"> <% } } else if (ClusterManager.isClusteringStarting()) { %> <% } %>
 
<% if (isLocalMember) { %> <%= nodeInfo.getHostName() %> <% } else { %> <%= nodeInfo.getHostName() %> <% } %>
<%= nodeInfo.getNodeID() %>
<%= JiveGlobals.formatDateTime(new Date(nodeInfo.getJoinedTime())) %> <%= nodeStats != null ? nodeStats.get(GetBasicStatistics.CLIENT) : "N/A" %> <%= nodeStats != null ? nodeStats.get(GetBasicStatistics.INCOMING) : "N/A" %> <%= nodeStats != null ? nodeStats.get(GetBasicStatistics.OUTGOING) : "N/A" %> <% int percent = 0; String memory = "N/A"; if (nodeStats != null) { double usedMemory = (Double) nodeStats.get(GetBasicStatistics.MEMORY_CURRENT); double maxMemory = (Double) nodeStats.get(GetBasicStatistics.MEMORY_MAX); double percentFree = ((maxMemory - usedMemory) / maxMemory) * 100.0; percent = 100-(int)Math.round(percentFree); DecimalFormat mbFormat = new DecimalFormat("#0.00"); memory = mbFormat.format(usedMemory) + " MB of " + mbFormat.format(maxMemory) + " MB used"; } %>
<% if (percent == 0) { %> <% } else { %> <% if (percent >= 90) { %> <% } else { %> <% } %> <% } %>
<%= memory %>
 
"/>
<%--@elvariable id="clusterNodeInfo" type="org.jivesoftware.openfire.cluster.ClusterNodeInfo>"--%>
local" style="width: 1%"> Warning
local" style="width: 1%"> Warning