001/* 002 * Copyright (C) 2022-present The Prometheus jmx_exporter Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package io.prometheus.jmx.common.http.authenticator; 018 019import java.nio.charset.StandardCharsets; 020import java.util.LinkedHashMap; 021import java.util.LinkedList; 022 023/** 024 * Class to implement a Credentials cache that is size constrained 025 * 026 * <p>The cache will purge old entries to make size for a cacheable credential 027 * 028 * <p>A credential that exceeds maximumCacheSizeBytes is not cached 029 */ 030public class CredentialsCache { 031 032 private final int maximumCacheSizeBytes; 033 private final LinkedHashMap<Credentials, Byte> linkedHashMap; 034 private final LinkedList<Credentials> linkedList; 035 036 private int currentCacheSizeBytes; 037 038 /** 039 * Constructor 040 * 041 * @param maximumCacheSizeBytes maximum cache size in bytes 042 */ 043 public CredentialsCache(int maximumCacheSizeBytes) { 044 this.maximumCacheSizeBytes = maximumCacheSizeBytes; 045 linkedHashMap = new LinkedHashMap<>(); 046 linkedList = new LinkedList<>(); 047 } 048 049 /** 050 * Method to add a Credentials to the cache 051 * 052 * <p>A credential that exceeds maximumCacheSizeBytes is not cached 053 * 054 * @param credentials credential 055 */ 056 public synchronized void add(Credentials credentials) { 057 int credentialSizeBytes = credentials.toString().getBytes(StandardCharsets.UTF_8).length; 058 059 // Don't cache the entry since it's bigger than the maximum cache size 060 // Don't invalidate other entries 061 if (credentialSizeBytes > maximumCacheSizeBytes) { 062 return; 063 } 064 065 // Purge old cache entries until we have space or the cache is empty 066 while (((currentCacheSizeBytes + credentialSizeBytes) > maximumCacheSizeBytes) 067 && (currentCacheSizeBytes > 0)) { 068 Credentials c = linkedList.removeLast(); 069 linkedHashMap.remove(c); 070 currentCacheSizeBytes -= credentialSizeBytes; 071 if (currentCacheSizeBytes < 0) { 072 currentCacheSizeBytes = 0; 073 } 074 } 075 076 linkedHashMap.put(credentials, (byte) 1); 077 linkedList.addFirst(credentials); 078 currentCacheSizeBytes += credentialSizeBytes; 079 } 080 081 /** 082 * Method to return whether the cache contains the Credentials 083 * 084 * @param credentials credentials 085 * @return true if the set contains the Credential, else false 086 */ 087 public synchronized boolean contains(Credentials credentials) { 088 return linkedHashMap.containsKey(credentials); 089 } 090 091 /** 092 * Method to remove a Credentials from the cache 093 * 094 * @param credentials credentials 095 * @return true if the Credentials existed and was removed, else false 096 */ 097 public synchronized boolean remove(Credentials credentials) { 098 if (linkedHashMap.remove(credentials) != null) { 099 linkedList.remove(credentials); 100 currentCacheSizeBytes -= credentials.toString().getBytes(StandardCharsets.UTF_8).length; 101 return true; 102 } else { 103 return false; 104 } 105 } 106 107 /** 108 * Method to get the maximum cache size in bytes 109 * 110 * @return the maximum cache size in bytes 111 */ 112 public int getMaximumCacheSizeBytes() { 113 return maximumCacheSizeBytes; 114 } 115 116 /** 117 * Method to get the current cache size in bytes 118 * 119 * @return the current cache size in bytes 120 */ 121 public synchronized int getCurrentCacheSizeBytes() { 122 return currentCacheSizeBytes; 123 } 124}