3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.platform.web.requestid;
22 import java.util.Base64;
23 import java.util.concurrent.atomic.AtomicLong;
24 import java.util.concurrent.atomic.AtomicReference;
25 import org.sonar.core.util.UuidGenerator;
28 * This implementation of {@link RequestIdGenerator} creates unique identifiers for HTTP requests leveraging
29 * {@link UuidGenerator.WithFixedBase#generate(int)} and a counter of HTTP requests.
31 * To work around the limit of unique values produced by {@link UuidGenerator.WithFixedBase#generate(int)}, the
32 * {@link UuidGenerator.WithFixedBase} instance will be renewed every
33 * {@link RequestIdConfiguration#getUidGeneratorRenewalCount() RequestIdConfiguration#uidGeneratorRenewalCount}
37 * This implementation is Thread safe.
40 public class RequestIdGeneratorImpl implements RequestIdGenerator {
42 * The value to which the HTTP request count will be compared to (using a modulo operator,
43 * see {@link #mustRenewUuidGenerator(long)}).
46 * This value can't be the last value before {@link UuidGenerator.WithFixedBase#generate(int)} returns a non unique
47 * value, ie. 2^23-1 because there is no guarantee the renewal will happen before any other thread calls
48 * {@link UuidGenerator.WithFixedBase#generate(int)} method of the deplated {@link UuidGenerator.WithFixedBase} instance.
52 * To keep a comfortable margin of error, 2^22 will be used.
55 public static final long UUID_GENERATOR_RENEWAL_COUNT = 4_194_304;
57 private final AtomicLong counter = new AtomicLong();
58 private final RequestIdGeneratorBase requestIdGeneratorBase;
59 private final RequestIdConfiguration requestIdConfiguration;
60 private final AtomicReference<UuidGenerator.WithFixedBase> uuidGenerator;
62 public RequestIdGeneratorImpl(RequestIdGeneratorBase requestIdGeneratorBase, RequestIdConfiguration requestIdConfiguration) {
63 this.requestIdGeneratorBase = requestIdGeneratorBase;
64 this.uuidGenerator = new AtomicReference<>(requestIdGeneratorBase.createNew());
65 this.requestIdConfiguration = requestIdConfiguration;
69 public String generate() {
70 UuidGenerator.WithFixedBase currentUuidGenerator = this.uuidGenerator.get();
71 long counterValue = counter.getAndIncrement();
72 if (counterValue != 0 && mustRenewUuidGenerator(counterValue)) {
73 UuidGenerator.WithFixedBase newUuidGenerator = requestIdGeneratorBase.createNew();
74 uuidGenerator.set(newUuidGenerator);
75 return generate(newUuidGenerator, counterValue);
77 return generate(currentUuidGenerator, counterValue);
81 * Since renewal of {@link UuidGenerator.WithFixedBase} instance is based on the HTTP request counter, only a single
82 * thread can get the right value which will make this method return true. So, this is thread-safe by design, therefor
83 * this method doesn't need external synchronization.
85 * The value to which the counter is compared should however be chosen with caution: see {@link #UUID_GENERATOR_RENEWAL_COUNT}.
88 private boolean mustRenewUuidGenerator(long counter) {
89 return counter % requestIdConfiguration.getUidGeneratorRenewalCount() == 0;
92 private static String generate(UuidGenerator.WithFixedBase uuidGenerator, long increment) {
93 return Base64.getEncoder().encodeToString(uuidGenerator.generate((int) increment));