001/*
002 * Copyright 2022-2025 Revetware LLC.
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 com.soklet.core.impl;
018
019import com.soklet.core.IdGenerator;
020
021import javax.annotation.Nonnull;
022import javax.annotation.Nullable;
023import javax.annotation.concurrent.ThreadSafe;
024import java.net.InetAddress;
025import java.util.concurrent.atomic.AtomicLong;
026
027import static java.lang.String.format;
028import static java.util.Objects.requireNonNull;
029
030/**
031 * @author <a href="https://www.revetkn.com">Mark Allen</a>
032 */
033@ThreadSafe
034public class DefaultIdGenerator implements IdGenerator {
035        @Nonnull
036        private static final String DEFAULT_ID_PREFIX;
037
038        static {
039                String idPrefix = "";
040
041                // IDs ultimately look like "192.168.4.75-1234" (or just "1234" if we can't detect host address)
042                try {
043                        String hostAddress = InetAddress.getLocalHost().getHostAddress();
044
045                        if (hostAddress != null) {
046                                hostAddress = hostAddress.trim();
047
048                                if (hostAddress.length() > 0)
049                                        idPrefix = format("%s-", hostAddress);
050                        }
051                } catch (Exception e) {
052                        // Ignored
053                }
054
055                DEFAULT_ID_PREFIX = idPrefix;
056        }
057
058        @Nonnull
059        private final Long minimumId;
060        @Nonnull
061        private final Long maximumId;
062        @Nonnull
063        private final AtomicLong idGenerator;
064        @Nonnull
065        private final String idPrefix;
066
067        public DefaultIdGenerator() {
068                this(1L, 9_999_999L);
069        }
070
071        public DefaultIdGenerator(@Nonnull Long minimumId,
072                                                                                                                @Nonnull Long maximumId) {
073                this(minimumId, maximumId, null);
074        }
075
076        public DefaultIdGenerator(@Nonnull Long minimumId,
077                                                                                                                @Nonnull Long maximumId,
078                                                                                                                @Nullable String idPrefix) {
079                requireNonNull(minimumId);
080                requireNonNull(maximumId);
081
082                if (minimumId < 0)
083                        throw new IllegalArgumentException(format("Minimum ID (%s) cannot be negative", minimumId));
084
085                if (maximumId < 1)
086                        throw new IllegalArgumentException(format("Maximum ID (%s) must be greater than zero", maximumId));
087
088                if (minimumId >= maximumId)
089                        throw new IllegalArgumentException(format("Minimum ID (%s) must be less than maximum ID (%s)", minimumId, maximumId));
090
091                if (idPrefix == null)
092                        idPrefix = getDefaultIdPrefix();
093
094                this.minimumId = minimumId;
095                this.maximumId = maximumId;
096                this.idPrefix = idPrefix;
097                this.idGenerator = new AtomicLong(getMinimumId());
098        }
099
100        @Nonnull
101        @Override
102        public Object generateId() {
103                return format("%s%s", getIdPrefix(), getIdGenerator().getAndAccumulate(getMaximumId(),
104                                (currentId, ignored) -> currentId < getMaximumId() ? ++currentId : getMinimumId()));
105        }
106
107        @Nonnull
108        protected String getDefaultIdPrefix() {
109                return DEFAULT_ID_PREFIX;
110        }
111
112        @Nonnull
113        protected AtomicLong getIdGenerator() {
114                return this.idGenerator;
115        }
116
117        @Nonnull
118        protected Long getMinimumId() {
119                return this.minimumId;
120        }
121
122        @Nonnull
123        protected Long getMaximumId() {
124                return this.maximumId;
125        }
126
127        @Nonnull
128        protected String getIdPrefix() {
129                return this.idPrefix;
130        }
131}