001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2013-2015 ForgeRock AS 025 */ 026package org.opends.server.protocols.http; 027 028import java.util.Arrays; 029import java.util.HashMap; 030import java.util.List; 031import java.util.Map; 032import java.util.Map.Entry; 033import java.util.concurrent.atomic.AtomicInteger; 034import java.util.concurrent.atomic.AtomicLong; 035 036import org.opends.server.protocols.ldap.LDAPStatistics; 037import org.opends.server.types.Attribute; 038 039/** 040 * Collects statistics for HTTP. This class inherits from {@link LDAPStatistics} 041 * to show the administrator how the underlying LDAP internal operations are 042 * performing. 043 */ 044public class HTTPStatistics extends LDAPStatistics 045{ 046 047 /** 048 * Map containing the total number of requests per HTTP methods. 049 * <p> 050 * key: HTTP method => value: number of requests for that method. 051 * </p> 052 * Not using a ConcurrentMap implementation here because the keys are static. 053 * The keys are static because they need to be listed in the schema which is 054 * static. 055 */ 056 private Map<String, AtomicInteger> requestMethodsTotalCount = new HashMap<>(); 057 /** 058 * Map containing the total execution time for the requests per HTTP methods. 059 * <p> 060 * key: HTTP method => value: total execution time for requests using that 061 * method. 062 * </p> 063 * Not using a ConcurrentMap implementation here because the keys are static. 064 * The keys are static because they need to be listed in the schema which is 065 * static. 066 */ 067 private Map<String, AtomicLong> requestMethodsTotalTime = new HashMap<>(); 068 /** 069 * Total number of requests. The total number may be different than the sum of 070 * the supported HTTP methods above because clients could use unsupported HTTP 071 * methods. 072 */ 073 private AtomicInteger requestsTotalCount = new AtomicInteger(0); 074 075 /** 076 * Constructor for this class. 077 * 078 * @param instanceName 079 * The name for this monitor provider instance. 080 */ 081 public HTTPStatistics(String instanceName) 082 { 083 super(instanceName); 084 085 // List the HTTP methods supported by Rest2LDAP 086 final List<String> supportedHttpMethods = 087 Arrays.asList("delete", "get", "patch", "post", "put"); 088 for (String method : supportedHttpMethods) 089 { 090 requestMethodsTotalCount.put(method, new AtomicInteger(0)); 091 requestMethodsTotalTime.put(method, new AtomicLong(0)); 092 } 093 } 094 095 /** {@inheritDoc} */ 096 @Override 097 public void clearStatistics() 098 { 099 this.requestMethodsTotalCount.clear(); 100 this.requestMethodsTotalTime.clear(); 101 this.requestsTotalCount.set(0); 102 103 super.clearStatistics(); 104 } 105 106 /** {@inheritDoc} */ 107 @Override 108 public List<Attribute> getMonitorData() 109 { 110 // first take a snapshot of all the data as fast as possible 111 final int totalCount = this.requestsTotalCount.get(); 112 final Map<String, Integer> totalCountsSnapshot = new HashMap<>(); 113 for (Entry<String, AtomicInteger> entry : requestMethodsTotalCount.entrySet()) 114 { 115 totalCountsSnapshot.put(entry.getKey(), entry.getValue().get()); 116 } 117 final Map<String, Long> totalTimesSnapshot = new HashMap<>(); 118 for (Entry<String, AtomicLong> entry1 : requestMethodsTotalTime.entrySet()) 119 { 120 totalTimesSnapshot.put(entry1.getKey(), entry1.getValue().get()); 121 } 122 123 // do the same with the underlying data 124 final List<Attribute> results = super.getMonitorData(); 125 addAll(results, totalCountsSnapshot, "ds-mon-http-", "-requests-total-count"); 126 addAll(results, totalTimesSnapshot, "ds-mon-resident-time-http-", "-requests-total-time"); 127 results.add(createAttribute("ds-mon-http-requests-total-count", Integer.toString(totalCount))); 128 return results; 129 } 130 131 private void addAll(final List<Attribute> results, 132 final Map<String, ?> toOutput, String prefix, String suffix) 133 { 134 for (Entry<String, ?> entry : toOutput.entrySet()) 135 { 136 final String httpMethod = entry.getKey(); 137 final String nb = entry.getValue().toString(); 138 results.add(createAttribute(prefix + httpMethod + suffix, nb)); 139 } 140 } 141 142 /** 143 * Adds a request to the stats using the provided HTTP method. 144 * 145 * @param httpMethod 146 * the method of the HTTP request to add to the stats 147 * @throws NullPointerException 148 * if the httpMethod is null 149 */ 150 public void addRequest(String httpMethod) throws NullPointerException 151 { 152 AtomicInteger nb = 153 this.requestMethodsTotalCount.get(httpMethod.toLowerCase()); 154 if (nb != null) 155 { 156 nb.incrementAndGet(); 157 } // else this is an unsupported HTTP method 158 // always count any requests regardless of whether the method is supported 159 this.requestsTotalCount.incrementAndGet(); 160 } 161 162 /** 163 * Adds to the total time of an HTTP request method. 164 * 165 * @param httpMethod 166 * the method of the HTTP request to add to the stats 167 * @param time 168 * the time to add to the total 169 * @throws NullPointerException 170 * if the httpMethod is null 171 */ 172 public void updateRequestMonitoringData(String httpMethod, long time) 173 throws NullPointerException 174 { 175 AtomicLong nb = this.requestMethodsTotalTime.get(httpMethod.toLowerCase()); 176 if (nb != null) 177 { 178 nb.addAndGet(time); 179 } // else this is an unsupported HTTP method 180 } 181}