/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.synapse.endpoints; import org.apache.axis2.transport.base.MessageLevelMetricsCollector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * This class is the metrics collector and JMX control point for Endpoints */ public class EndpointView implements EndpointViewMBean, MessageLevelMetricsCollector { private static final Log log = LogFactory.getLog(EndpointView.class); private static final Long ONE = 1L; private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(20, new ThreadFactory() { public Thread newThread(Runnable r) { return new Thread(r, "endpoint-jmx-stat-collector"); } } ); /** The name of the endpoint */ private String endpointName = null; /** The actual Endpoint implementation we manage */ private Endpoint endpoint = null; // metrics collected / maintained private long messagesReceived; private long faultsReceiving; private long timeoutsReceiving; private long bytesReceived; private long minSizeReceived; private long maxSizeReceived; private double avgSizeReceived; private final Map<Integer, Long> receivingFaultTable = Collections.synchronizedMap(new HashMap<Integer, Long>()); private long messagesSent; private long faultsSending; private long timeoutsSending; private long bytesSent; private long minSizeSent; private long maxSizeSent; private double avgSizeSent; private int consecutiveSuspensions; private int consecutiveTimeouts; private int totalSuspensions; private int totalTimeouts; private AtomicInteger suspensions = new AtomicInteger(0); private AtomicInteger timeouts = new AtomicInteger(0); private Date suspendedAt; private Date timedoutAt; private final Map<Integer, Long> sendingFaultTable = Collections.synchronizedMap(new HashMap<Integer, Long>()); private final Map<Integer, Long> responseCodeTable = Collections.synchronizedMap(new HashMap<Integer, Long>()); private long lastResetTime = System.currentTimeMillis(); private ScheduledFuture future; private Queue<Integer> suspensionCounts = new LinkedList<Integer>(); private Queue<Integer> timeoutCounts = new LinkedList<Integer>(); /** * Create a new MBean to manage the given endpoint * @param endpointName the name of the endpoint * @param endpoint the actual endpoint */ public EndpointView(final String endpointName, Endpoint endpoint) { this.endpointName = endpointName; this.endpoint = endpoint; this.future = scheduler.scheduleAtFixedRate(new Runnable() { public void run() { if (suspensionCounts.size() == 15) { suspensionCounts.remove(); } suspensionCounts.offer(suspensions.getAndSet(0)); if (timeoutCounts.size() == 15) { timeoutCounts.remove(); } timeoutCounts.offer(timeouts.getAndSet(0)); } }, 60, 60, TimeUnit.SECONDS); } public void destroy() { future.cancel(true); suspensionCounts.clear(); timeoutCounts.clear(); } // --- endpoint control --- /** * Switch on a leaf endpoint, or all endpoints on a group - from maintenance * @throws Exception */ public void switchOn() throws Exception { if (endpoint.getChildren() != null) { for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { e.getMetricsMBean().switchOn(); } } } else { if (endpoint.getContext() != null) { endpoint.getContext().switchOn(); } } } /** * Switch off a leaf endpoint, or all endpoints of a group - for maintenance * * @throws Exception */ public void switchOff() throws Exception { if (endpoint.getChildren() != null) { for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { e.getMetricsMBean().switchOff(); } } } else { if (endpoint.getContext() != null) { endpoint.getContext().switchOff(); } } } // --- endpoint status check --- /** * Is a leaf level endpoint active? For a group endpoint this means at least one is active * @return true if at least one is active in a group endpoint; for a leaf - if it is currently active * @throws Exception */ public boolean isActive() throws Exception { if (endpoint.getChildren() != null) { return getActiveChildren() > 0; } else if (endpoint.getContext() != null) { return endpoint.getContext().isState(EndpointContext.ST_ACTIVE); } return false; } /** * Is this leaf level endpoint in timeout state? For a group, has all endpoints timed out? * @return true if a leaf level endpoint has timed out, For a group, has all endpoints timed out? * @throws Exception */ public boolean isTimedout() throws Exception { return isEndpointInState(EndpointContext.ST_TIMEOUT); } /** * Is this leaf level endpoint in suspend state? * @return true if a leaf level endpoint is suspended, false for group endpoints and non-suspend * @throws Exception */ public boolean isSuspended() throws Exception { return isEndpointInState(EndpointContext.ST_SUSPENDED); } /** * Is this leaf level endpoint switched off? * @return true if a leaf level endpoint is off, false for group endpoints and non-off * @throws Exception */ public boolean isSwitchedOff() throws Exception { return isEndpointInState(EndpointContext.ST_OFF); } /** * Return number of children for this endpoint * @return the number of children for this endpoint * @throws Exception */ public int getTotalChildren() throws Exception { return (endpoint.getChildren() == null ? 0 : endpoint.getChildren().size()); } /** * Return the number of active children for this endpoint * @return the number of active children for this endpoint * @throws Exception */ public int getActiveChildren() throws Exception { if (endpoint.getChildren() == null) { return 0; } else { int activeCount = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getContext().isState(EndpointContext.ST_ACTIVE)) { activeCount++; } } return activeCount; } } /** * Return the number of ready children for this endpoint * @return the number of ready children for this endpoint * @throws Exception */ public int getReadyChildren() throws Exception { if (endpoint.getChildren() == null) { return 0; } else { int readyCount = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getContext().readyToSend()) { readyCount++; } } return readyCount; } } // --- endpoint metrics --- /** * Time when statistics was last reset for this leaf endpoint * @return the time when statistics was last reset for this leaf endpoint, or -1 for group endpoints */ public long getLastResetTime() { return (endpoint.getChildren() != null ? -1 : lastResetTime); } /** * Time since statistics was last reset for this leaf endpoint * @return the time since statistics was last reset for this leaf endpoint, or -1 for group endpoints */ public long getMetricsWindow() { return (endpoint.getChildren() != null ? -1 : System.currentTimeMillis() - lastResetTime); } /** * A Map of receive faults with the error code and count * @return a Map of receive faults */ public Map<Integer, Long> getReceivingFaultTable() { if (endpoint.getChildren() != null) { Map<Integer, Long> receivingFaultTable = new HashMap<Integer, Long>(); for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { addTableMaps(receivingFaultTable, e.getMetricsMBean().getReceivingFaultTable()); } } addTableMaps(receivingFaultTable, this.receivingFaultTable); return receivingFaultTable; } else { return receivingFaultTable; } } /** * A Map of send faults with the error code and count * @return a Map of send faults */ public Map<Integer, Long> getSendingFaultTable() { if (endpoint.getChildren() != null) { Map<Integer, Long> sendingFaultTable = new HashMap<Integer, Long>(); for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { addTableMaps(sendingFaultTable, e.getMetricsMBean().getSendingFaultTable()); } } addTableMaps(sendingFaultTable, this.sendingFaultTable); return sendingFaultTable; } else { return sendingFaultTable; } } /** * A Map of response codes and counts * @return a Map of response codes and counts */ public Map<Integer, Long> getResponseCodeTable() { if (endpoint.getChildren() != null) { Map<Integer, Long> responseCodeTable = new HashMap<Integer, Long>(); for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { addTableMaps(responseCodeTable, e.getMetricsMBean().getResponseCodeTable()); } } return responseCodeTable; } else { return responseCodeTable; } } public Date getSuspendedAt() { return suspendedAt; } public void setSuspendedAt(Date suspendedAt) { this.suspendedAt = suspendedAt; } public Date getTimedoutAt() { return timedoutAt; } public void setTimedoutAt(Date timedoutAt) { this.timedoutAt = timedoutAt; } public int getConsecutiveEndpointSuspensions() { return consecutiveSuspensions; } public void incrementSuspensions() { consecutiveSuspensions++; totalSuspensions++; suspensions.incrementAndGet(); } public void resetConsecutiveSuspensions() { consecutiveSuspensions = 0; } public int getConsecutiveEndpointTimeouts() { return consecutiveTimeouts; } public void incrementTimeouts() { consecutiveTimeouts++; totalTimeouts++; timeouts.incrementAndGet(); } public void resetConsecutiveTimeouts() { consecutiveTimeouts = 0; } public int getTotalEndpointSuspensions() { return totalSuspensions; } public int getTotalEndpointTimeouts() { return totalTimeouts; } public int getLastMinuteEndpointSuspensions() { return getTotal(suspensionCounts, 1); } public int getLast5MinuteEndpointSuspensions() { return getTotal(suspensionCounts, 5); } public int getLast15MinuteEndpointSuspensions() { return getTotal(suspensionCounts, 15); } public int getLastMinuteEndpointTimeouts() { return getTotal(timeoutCounts, 1); } public int getLast5MinuteEndpointTimeouts() { return getTotal(timeoutCounts, 5); } public int getLast15MinuteEndpointTimeouts() { return getTotal(timeoutCounts, 15); } private int getTotal(Queue<Integer> queue, int count) { int sum = 0; Integer[] array = queue.toArray(new Integer[queue.size()]); if (count > array.length) { for (int i = 0; i < array.length; i++) { sum +=array[i]; } } else { for (int i = 0; i < count; i++) { sum += array[array.length - 1 - i]; } } return sum; } /** * Number of messages (ie replies) received * @return # of messages (replies) received */ public long getMessagesReceived() { if (endpoint.getChildren() != null) { long messagesReceived = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { messagesReceived += e.getMetricsMBean().getMessagesReceived(); } } return messagesReceived; } else { return messagesReceived; } } /** * Number of faults, receiving replies * @return # of faults, receiving replies */ public long getFaultsReceiving() { if (endpoint.getChildren() != null) { long faultsReceiving = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { faultsReceiving += e.getMetricsMBean().getFaultsReceiving(); } } return faultsReceiving; } else { return faultsReceiving; } } /** * Number of timeouts, receiving replies * @return # of timeouts, receiving replies */ public long getTimeoutsReceiving() { if (endpoint.getChildren() != null) { long timeoutsReceiving = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { timeoutsReceiving += e.getMetricsMBean().getTimeoutsReceiving(); } } return timeoutsReceiving; } else { return timeoutsReceiving; } } /** * Number of bytes received, receiving replies * @return # of bytes received, receiving replies */ public long getBytesReceived() { if (endpoint.getChildren() != null) { long bytesReceived = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { bytesReceived += e.getMetricsMBean().getBytesReceived(); } } return bytesReceived; } else { return bytesReceived; } } /** * Number of messages sent * @return # of messages sent */ public long getMessagesSent() { if (endpoint.getChildren() != null) { long messagesSent = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { messagesSent += e.getMetricsMBean().getMessagesSent(); } } return messagesSent; } else { return messagesSent; } } /** * Number of faults sending * @return # of faults sending */ public long getFaultsSending() { if (endpoint.getChildren() != null) { long faultsSending = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { faultsSending += e.getMetricsMBean().getFaultsSending(); } } return faultsSending; } else { return faultsSending; } } /** * Number of timeouts, sending * @return # of timeouts, sending */ public long getTimeoutsSending() { if (endpoint.getChildren() != null) { long timeoutsSending = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { timeoutsSending += e.getMetricsMBean().getTimeoutsSending(); } } return timeoutsSending; } else { return timeoutsSending; } } /** * Number of bytes sent * @return # of bytes sent */ public long getBytesSent() { if (endpoint.getChildren() != null) { long bytesSent = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { bytesSent += e.getMetricsMBean().getBytesSent(); } } return bytesSent; } else { return bytesSent; } } public long getMinSizeReceived() { if (endpoint.getChildren() != null) { long minSizeReceived = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { if (minSizeReceived == 0) { minSizeReceived = e.getMetricsMBean().getMinSizeReceived(); } else if (e.getMetricsMBean().getMinSizeReceived() < minSizeReceived) { minSizeReceived = e.getMetricsMBean().getMinSizeReceived(); } } } return minSizeReceived; } else { return minSizeReceived; } } public long getMaxSizeReceived() { if (endpoint.getChildren() != null) { long maxSizeReceived = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { if (maxSizeReceived == 0) { maxSizeReceived = e.getMetricsMBean().getMaxSizeReceived(); } else if (e.getMetricsMBean().getMaxSizeReceived() > maxSizeReceived) { maxSizeReceived = e.getMetricsMBean().getMaxSizeReceived(); } } } return maxSizeReceived; } else { return maxSizeReceived; } } public long getMinSizeSent() { if (endpoint.getChildren() != null) { long minSizeSent = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { if (minSizeSent == 0) { minSizeSent = e.getMetricsMBean().getMinSizeSent(); } else if (e.getMetricsMBean().getMinSizeSent() < minSizeSent) { minSizeSent = e.getMetricsMBean().getMinSizeSent(); } } } return minSizeSent; } else { return minSizeSent; } } public long getMaxSizeSent() { if (endpoint.getChildren() != null) { long maxSizeSent = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { if (maxSizeSent == 0) { maxSizeSent = e.getMetricsMBean().getMaxSizeSent(); } else if (e.getMetricsMBean().getMaxSizeSent() > maxSizeSent) { maxSizeSent = e.getMetricsMBean().getMaxSizeSent(); } } } return maxSizeSent; } else { return maxSizeSent; } } public double getAvgSizeReceived() { if (endpoint.getChildren() != null) { double avgSizeReceived = 0; for (Endpoint e : endpoint.getChildren()) { double epValue = e.getMetricsMBean() == null ? 0 : e.getMetricsMBean().getAvgSizeReceived(); if (epValue > 0) { avgSizeReceived = (avgSizeReceived == 0 ? epValue : (avgSizeReceived + epValue) / 2); } } return avgSizeReceived; } else { return avgSizeReceived; } } public double getAvgSizeSent() { if (endpoint.getChildren() != null) { double avgSizeSent = 0; for (Endpoint e : endpoint.getChildren()) { double epValue = e.getMetricsMBean() == null ? 0 : e.getMetricsMBean().getAvgSizeSent(); if (epValue > 0) { avgSizeSent = (avgSizeSent == 0 ? epValue : (avgSizeSent + epValue) / 2); } } return avgSizeSent; } else { return avgSizeSent; } } // --- MessageLevelMetricsCollector methods --- public void resetStatistics() { messagesReceived = 0; faultsReceiving = 0; timeoutsReceiving = 0; bytesReceived = 0; minSizeReceived = 0; maxSizeReceived = 0; avgSizeReceived = 0; receivingFaultTable.clear(); messagesSent = 0; faultsSending = 0; timeoutsSending = 0; bytesSent = 0; minSizeSent = 0; maxSizeSent = 0; avgSizeSent = 0; sendingFaultTable.clear(); responseCodeTable.clear(); lastResetTime = System.currentTimeMillis(); if (endpoint.getChildren() != null) { for (Endpoint e : endpoint.getChildren()) { if (e.getMetricsMBean() != null) { e.getMetricsMBean().resetStatistics(); } } } log.info("Endpoint statistics reset for : " + endpointName + " (and/or its children)"); } public synchronized void incrementMessagesReceived() { messagesReceived++; } public synchronized void incrementFaultsReceiving(int errorCode) { faultsReceiving++; Object o = receivingFaultTable.get(errorCode); if (o == null) { receivingFaultTable.put(errorCode, ONE); } else { receivingFaultTable.put(errorCode, (Long) o + 1); } } public synchronized void incrementTimeoutsReceiving() { timeoutsReceiving++; } public synchronized void incrementBytesReceived(long size) { bytesReceived += size; } public synchronized void incrementMessagesSent() { messagesSent++; } public synchronized void incrementFaultsSending(int errorCode) { faultsSending++; Object o = sendingFaultTable.get(errorCode); if (o == null) { sendingFaultTable.put(errorCode, ONE); } else { sendingFaultTable.put(errorCode, (Long) o + 1); } } public synchronized void incrementTimeoutsSending() { timeoutsSending++; } public synchronized void incrementBytesSent(long size) { bytesSent += size; } public synchronized void notifyReceivedMessageSize(long size) { if (minSizeReceived == 0 || size < minSizeReceived) { minSizeReceived = size; } if (size > maxSizeReceived) { maxSizeReceived = size; } avgSizeReceived = (avgSizeReceived == 0 ? size : (avgSizeReceived + size) / 2); } public synchronized void notifySentMessageSize(long size) { if (minSizeSent == 0 || size < minSizeSent) { minSizeSent = size; } if (size > maxSizeSent) { maxSizeSent = size; } avgSizeSent = (avgSizeSent == 0 ? size : (avgSizeSent + size) / 2); } /** * Report a/an [typically non-fatal] error to the sending fault table, without incrementing * the sendingFault count e.g. to report a successful fail-over etc * @param errorCode the code to report */ public void reportSendingFault(int errorCode) { synchronized(sendingFaultTable) { Object o = sendingFaultTable.get(errorCode); if (o == null) { sendingFaultTable.put(errorCode, ONE); } else { sendingFaultTable.put(errorCode, (Long) o + 1); } } } /** * Report a/an [typically non-fatal] error to the receiving fault table, without incrementing * the receivingFault count * @param errorCode the code to report */ public void reportReceivingFault(int errorCode) { synchronized(receivingFaultTable) { Object o = receivingFaultTable.get(errorCode); if (o == null) { receivingFaultTable.put(errorCode, ONE); } else { receivingFaultTable.put(errorCode, (Long) o + 1); } } } /** * Collect response code statistics * @param respCode response code */ public void reportResponseCode(int respCode) { synchronized(responseCodeTable) { Object o = responseCodeTable.get(respCode); if (o == null) { responseCodeTable.put(respCode, ONE); } else { responseCodeTable.put(respCode, (Long) o + 1); } } } //---------- utility methods --------------- private static void addTableMaps(Map<Integer, Long> t, Map<Integer, Long> s) { for (Map.Entry<Integer, Long> o : s.entrySet()) { if (t.containsKey(o.getKey())) { t.put(o.getKey(), o.getValue() + s.get(o.getKey())); } else { t.put(o.getKey(), s.get(o.getKey())); } } } /** * Is the endpoint considered to be in the given state? * @param state the state to consider * @return true if all endpoints in a group are of the given state, or if a leaf endpoint is in the given state */ public boolean isEndpointInState(int state) { if (endpoint.getChildren() != null) { int count = 0, total = 0; for (Endpoint e : endpoint.getChildren()) { if (e.getContext().isState(state)) { count++; } total++; } return count == total; } else if (endpoint.getContext() != null) { return endpoint.getContext().isState(state); } return false; } }