├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .mvn
├── extensions.xml
├── jvm.config
├── maven.config
├── modernizer
│ ├── violations-production-code-only.xml
│ └── violations.xml
├── rrf
│ ├── groupId-central.maven.org.txt
│ ├── groupId-confluent.txt
│ ├── groupId-flyway-repo.txt
│ ├── groupId-google-maven-central-copy.txt
│ ├── groupId-jitpack.io.txt
│ ├── groupId-maven-central.txt
│ ├── groupId-ossrh.txt
│ └── groupId-repo.gradle.org.txt
└── wrapper
│ └── maven-wrapper.properties
├── LICENSE
├── README.md
├── mvnw
├── pom.xml
└── src
├── main
├── java
│ ├── io
│ │ └── airlift
│ │ │ └── units
│ │ │ ├── DataSize.java
│ │ │ ├── Duration.java
│ │ │ ├── MaxDataSize.java
│ │ │ ├── MaxDataSizeValidator.java
│ │ │ ├── MaxDuration.java
│ │ │ ├── MaxDurationValidator.java
│ │ │ ├── MaxThreadCount.java
│ │ │ ├── MaxThreadCountValidator.java
│ │ │ ├── MinDataSize.java
│ │ │ ├── MinDataSizeValidator.java
│ │ │ ├── MinDuration.java
│ │ │ ├── MinDurationValidator.java
│ │ │ ├── MinThreadCount.java
│ │ │ ├── MinThreadCountValidator.java
│ │ │ ├── Preconditions.java
│ │ │ └── ThreadCount.java
│ └── module-info.java
└── resources
│ └── ValidationMessages.properties
└── test
└── java
└── io
└── airlift
└── units
├── ConstraintValidatorAssert.java
├── MockMaxDataSize.java
├── MockMaxDuration.java
├── MockMaxThreadCount.java
├── MockMinDataSize.java
├── MockMinDuration.java
├── MockMinThreadCount.java
├── TestDataSize.java
├── TestDataSizeValidator.java
├── TestDuration.java
├── TestDurationValidator.java
├── TestThreadCountValidator.java
└── TestThreadsCount.java
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | java-version:
14 | - 17
15 | steps:
16 | - uses: actions/checkout@v1
17 | - uses: actions/setup-java@v1
18 | with:
19 | java-version: ${{ matrix.java-version }}
20 | - name: Maven Install
21 | run: ./mvnw install -B -V -DskipTests -Dair.check.skip-all
22 | - name: Maven Tests
23 | run: ./mvnw install -B -P ci
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.ipr
3 | *.iws
4 | target/
5 | /var
6 | pom.xml.versionsBackup
7 | test-output/
8 | /atlassian-ide-plugin.xml
9 | .idea
10 | .*.swp
11 | .*.swo
12 | .project
13 | .classpath
14 | .settings
15 | .pmdruleset.xml
16 | .pmd
17 |
--------------------------------------------------------------------------------
/.mvn/extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | io.takari.maven
5 | takari-smart-builder
6 | 0.6.6
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.mvn/jvm.config:
--------------------------------------------------------------------------------
1 | -Xmx8192m
2 | --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
3 | --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
4 | --add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
5 | --add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
6 | --add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
7 | --add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
8 | --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
9 | --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
10 | --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
11 | --add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
12 | -XX:+ExitOnOutOfMemoryError
13 |
--------------------------------------------------------------------------------
/.mvn/maven.config:
--------------------------------------------------------------------------------
1 | --strict-checksums
2 | -Daether.syncContext.named.factory=file-lock
3 | -Daether.syncContext.named.nameMapper=file-hgav
4 | -Daether.syncContext.named.retry=5
5 | -Daether.dependencyCollector.impl=bf
6 | -Daether.remoteRepositoryFilter.prefixes=true
7 | -Daether.remoteRepositoryFilter.prefixes.basedir=${session.rootDirectory}/.mvn/rrf/
8 | -Daether.remoteRepositoryFilter.groupId=true
9 | -Daether.remoteRepositoryFilter.groupId.basedir=${session.rootDirectory}/.mvn/rrf/
10 | -b
11 | smart
12 | -Dair.main.basedir=${session.rootDirectory}
13 |
--------------------------------------------------------------------------------
/.mvn/modernizer/violations-production-code-only.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | java/util/concurrent/ThreadPoolExecutor."<init>":(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;)V
5 | 1.1
6 | Use constructor that takes ThreadFactory and name the threads
7 |
8 |
9 |
10 | java/util/concurrent/ThreadPoolExecutor."<init>":(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/RejectedExecutionHandler;)V
11 | 1.1
12 | Use constructor that takes ThreadFactory and name the threads
13 |
14 |
15 |
16 | java/util/concurrent/ScheduledThreadPoolExecutor."<init>":(I)V
17 | 1.1
18 | Use constructor that takes ThreadFactory and name the threads
19 |
20 |
21 |
22 | java/util/concurrent/ScheduledThreadPoolExecutor."<init>":(ILjava/util/concurrent/RejectedExecutionHandler;)V
23 | 1.1
24 | Use constructor that takes ThreadFactory and name the threads
25 |
26 |
27 |
28 | java/util/concurrent/Executors.newFixedThreadPool:(I)Ljava/util/concurrent/ExecutorService;
29 | 1.1
30 | Use factory method that takes ThreadFactory and name the threads
31 |
32 |
33 |
34 | java/util/concurrent/Executors.newWorkStealingPool:()Ljava/util/concurrent/ExecutorService;
35 | 1.1
36 | Use factory method that takes ThreadFactory and name the threads
37 |
38 |
39 |
40 | java/util/concurrent/Executors.newWorkStealingPool:(I)Ljava/util/concurrent/ExecutorService;
41 | 1.1
42 | Use factory method that takes ThreadFactory and name the threads
43 |
44 |
45 |
46 | java/util/concurrent/Executors.newSingleThreadExecutor:()Ljava/util/concurrent/ExecutorService;
47 | 1.1
48 | Use factory method that takes ThreadFactory and name the threads
49 |
50 |
51 |
52 | java/util/concurrent/Executors.newCachedThreadPool:()Ljava/util/concurrent/ExecutorService;
53 | 1.1
54 | Use factory method that takes ThreadFactory and name the threads
55 |
56 |
57 |
58 | java/util/concurrent/Executors.newSingleThreadScheduledExecutor:()Ljava/util/concurrent/ScheduledExecutorService;
59 | 1.1
60 | Use factory method that takes ThreadFactory and name the threads
61 |
62 |
63 |
64 | java/util/concurrent/Executors.newScheduledThreadPool:(I)Ljava/util/concurrent/ScheduledExecutorService;
65 | 1.1
66 | Use factory method that takes ThreadFactory and name the threads
67 |
68 |
69 |
--------------------------------------------------------------------------------
/.mvn/modernizer/violations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | java/lang/Class.newInstance:()Ljava/lang/Object;
5 | 1.1
6 | Prefer Class.getConstructor().newInstance()
7 |
8 |
9 |
10 | java/lang/String."<init>":([B)V
11 | 1.1
12 | Prefer new String(byte[], Charset)
13 |
14 |
15 |
16 | java/lang/String.getBytes:()[B
17 | 1.1
18 | Prefer String.getBytes(Charset)
19 |
20 |
21 |
22 | java/lang/String.toString:()Ljava/lang/String;
23 | 1.1
24 | Call to toString() is redundant
25 |
26 |
27 |
28 |
29 | java/io/File.toString:()Ljava/lang/String;
30 | 1.1
31 | Prefer File.getPath()
32 |
33 |
34 |
35 | java/lang/Thread$Builder.factory:()Ljava/util/concurrent/ThreadFactory;
36 | 1.1
37 | Use io.airlift.concurrent.Threads's thread factories, as the set thread context class loader
38 |
39 |
40 | java/lang/Thread$Builder$OfPlatform.factory:()Ljava/util/concurrent/ThreadFactory;
41 | 1.1
42 | Use io.airlift.concurrent.Threads's thread factories, as the set thread context class loader
43 |
44 |
45 | java/lang/Thread$Builder$OfVirtual.factory:()Ljava/util/concurrent/ThreadFactory;
46 | 1.1
47 | Use io.airlift.concurrent.Threads's thread factories, as the set thread context class loader
48 |
49 |
50 |
51 | com/google/common/primitives/Ints.checkedCast:(J)I
52 | 1.8
53 | Prefer Math.toIntExact(long)
54 |
55 |
56 |
57 | com/google/common/collect/ImmutableMap$Builder.build:()Lcom/google/common/collect/ImmutableMap;
58 | 1.8
59 | Use buildOrThrow() instead, as it makes it clear that it will throw on duplicated values
60 |
61 |
62 | com/google/common/collect/ImmutableTable$Builder.build:()Lcom/google/common/collect/ImmutableTable;
63 | 1.8
64 | Use buildOrThrow() instead, as it makes it clear that it will throw on duplicated values
65 |
66 |
67 |
68 | com/google/common/collect/ImmutableBiMap$Builder."<init>":()V
69 | 1.8
70 | Use builder() static factory method instead
71 |
72 |
73 | com/google/common/collect/ImmutableList$Builder."<init>":()V
74 | 1.8
75 | Use builder() static factory method instead
76 |
77 |
78 | com/google/common/collect/ImmutableMap$Builder."<init>":()V
79 | 1.8
80 | Use builder() static factory method instead
81 |
82 |
83 | com/google/common/collect/ImmutableMultimap$Builder."<init>":()V
84 | 1.8
85 | Use builder() static factory method instead
86 |
87 |
88 | com/google/common/collect/ImmutableMultiset$Builder."<init>":()V
89 | 1.8
90 | Use builder() static factory method instead
91 |
92 |
93 | com/google/common/collect/ImmutableSet$Builder."<init>":()V
94 | 1.8
95 | Use builder() static factory method instead
96 |
97 |
98 | com/google/common/collect/ImmutableSortedMap$Builder."<init>":()V
99 | 1.8
100 | Use orderedBy() static factory method instead
101 |
102 |
103 | com/google/common/collect/ImmutableSortedSet$Builder."<init>":()V
104 | 1.8
105 | Use orderedBy() static factory method instead
106 |
107 |
108 | com/google/common/collect/ImmutableTable$Builder."<init>":()V
109 | 1.8
110 | Use builder() static factory method instead
111 |
112 |
113 |
114 | com/google/common/cache/CacheBuilder.build:()Lcom/google/common/cache/Cache;
115 | 1.8
116 | Guava Cache has concurrency issues around invalidation and ongoing loads. Use EvictableCacheBuilder or SafeCaches to build caches.
117 | See https://github.com/trinodb/trino/issues/10512 for more information and see https://github.com/trinodb/trino/issues/10512#issuecomment-1016221168
118 | for why Caffeine does not solve the problem.
119 |
120 |
121 |
122 | com/google/common/cache/CacheBuilder.build:(Lcom/google/common/cache/CacheLoader;)Lcom/google/common/cache/LoadingCache;
123 | 1.8
124 | Guava LoadingCache has concurrency issues around invalidation and ongoing loads. Use EvictableCacheBuilder or SafeCaches to build caches.
125 | See https://github.com/trinodb/trino/issues/10512 for more information and see https://github.com/trinodb/trino/issues/10512#issuecomment-1016221168
126 | for why Caffeine does not solve the problem.
127 |
128 |
129 |
130 | org/testng/Assert.assertEquals:(Ljava/lang/Iterable;Ljava/lang/Iterable;)V
131 | 1.8
132 | Use AssertJ or QueryAssertions due to TestNG #543
133 |
134 |
135 |
136 | org/testng/Assert.assertEquals:(Ljava/lang/Iterable;Ljava/lang/Iterable;Ljava/lang/String;)V
137 | 1.8
138 | Use AssertJ or QueryAssertions due to TestNG #543
139 |
140 |
141 |
142 | org/testng/Assert.assertThrows:(Lorg/testng/Assert$ThrowingRunnable;)V
143 | 1.8
144 | Use AssertJ's assertThatThrownBy, see https://github.com/trinodb/trino/issues/5320 for rationale
145 |
146 |
147 |
148 | org/testng/Assert.assertThrows:(Ljava/lang/Class;Lorg/testng/Assert$ThrowingRunnable;)V
149 | 1.8
150 | Use AssertJ's assertThatThrownBy, see https://github.com/trinodb/trino/issues/5320 for rationale
151 |
152 |
153 |
154 | com/amazonaws/services/glue/model/Table.getTableType:()Ljava/lang/String;
155 | 1.1
156 | Table type is nullable in Glue model, which is too easy to forget about. Prefer GlueToTrinoConverter.getTableType
157 |
158 |
159 |
160 | com/amazonaws/services/glue/model/Column.getParameters:()Ljava/util/Map;
161 | 1.1
162 | Column parameters map is nullable in Glue model, which is too easy to forget about. Prefer GlueToTrinoConverter.getColumnParameters
163 |
164 |
165 |
166 | com/amazonaws/services/glue/model/Table.getParameters:()Ljava/util/Map;
167 | 1.1
168 | Table parameters map is nullable in Glue model, which is too easy to forget about. Prefer GlueToTrinoConverter.getTableParameters
169 |
170 |
171 |
172 | com/amazonaws/services/glue/model/Partition.getParameters:()Ljava/util/Map;
173 | 1.1
174 | Partition parameters map is nullable in Glue model, which is too easy to forget about. Prefer GlueToTrinoConverter.getPartitionParameters
175 |
176 |
177 |
178 | com/amazonaws/services/glue/model/SerDeInfo.getParameters:()Ljava/util/Map;
179 | 1.1
180 | SerDeInfo parameters map is nullable in Glue model, which is too easy to forget about. Prefer GlueToTrinoConverter.getSerDeInfoParameters
181 |
182 |
183 |
184 | org/apache/hadoop/fs/FileSystem.close:()V
185 | 1.1
186 | Hadoop FileSystem instances are shared and should not be closed
187 |
188 |
189 |
190 | java/util/TimeZone.getTimeZone:(Ljava/lang/String;)Ljava/util/TimeZone;
191 | 1.8
192 | Avoid TimeZone.getTimeZone as it returns GMT for a zone not supported by the JVM. Use TimeZone.getTimeZone(ZoneId.of(..)) instead, or TimeZone.getTimeZone(..., false).
193 |
194 |
195 |
196 | org/joda/time/DateTimeZone.toTimeZone:()Ljava/util/TimeZone;
197 | 1.8
198 | Avoid DateTimeZone.toTimeZone as it returns GMT for a zone not supported by the JVM. Use TimeZone.getTimeZone(ZoneId.of(dtz.getId())) instead.
199 |
200 |
201 |
202 | com/esri/core/geometry/ogc/OGCGeometry.equals:(Lcom/esri/core/geometry/ogc/OGCGeometry;)Z
203 | 1.6
204 | Prefer OGCGeometry.Equals(OGCGeometry)
205 |
206 |
207 |
208 | com/esri/core/geometry/ogc/OGCGeometry.equals:(Ljava/lang/Object;)Z
209 | 1.6
210 | Prefer OGCGeometry.Equals(OGCGeometry)
211 |
212 |
213 |
214 | io/airlift/units/DataSize."<init>":(DLio/airlift/units/DataSize$Unit;)V
215 | 1.8
216 | Use io.airlift.units.DataSize.of(long, DataSize.Unit)
217 |
218 |
219 |
220 | io/airlift/units/DataSize.succinctDataSize:(DLio/airlift/units/DataSize$Unit;)Lio/airlift/units/DataSize;
221 | 1.8
222 | Use io.airlift.units.DataSize.of(long, DataSize.Unit).succinct() -- Note that succinct conversion only affects toString() results
223 |
224 |
225 |
226 | io/airlift/units/DataSize.getValue:()D
227 | 1.8
228 | Use io.airlift.units.DataSize.toBytes() and Unit.inBytes() for conversion
229 |
230 |
231 |
232 | io/airlift/units/DataSize.getValue:(Lio/airlift/units/DataSize$Unit;)D
233 | 1.8
234 | Use io.airlift.units.DataSize.toBytes() and Unit.inBytes() for conversion
235 |
236 |
237 |
238 | io/airlift/units/DataSize.roundTo:(Lio/airlift/units/DataSize$Unit;)J
239 | 1.8
240 | Method is deprecated for removal
241 |
242 |
243 |
244 | io/airlift/units/DataSize.convertTo:(Lio/airlift/units/DataSize$Unit;)Lio/airlift/units/DataSize;
245 | 1.8
246 | Use io.airlift.units.DataSize.to(DataSize.Unit)
247 |
248 |
249 |
250 | io/airlift/units/DataSize.convertToMostSuccinctDataSize:()Lio/airlift/units/DataSize;
251 | 1.8
252 | Use io.airlift.units.DataSize.succinct()
253 |
254 |
255 |
256 | io/airlift/testing/Closeables.closeQuietly:([Ljava/io/Closeable;)V
257 | 1.0
258 | Use Closeables.closeAll() or Closer.
259 |
260 |
261 |
262 | com/google/inject/util/Modules.combine:(Ljava/lang/Iterable;)Lcom/google/inject/Module;
263 | 1.8
264 | Use io.airlift.configuration.ConfigurationAwareModule.combine
265 |
266 |
267 |
268 | com/google/inject/util/Modules.combine:([Lcom/google/inject/Module;)Lcom/google/inject/Module;
269 | 1.8
270 | Use io.airlift.configuration.ConfigurationAwareModule.combine
271 |
272 |
273 |
274 | io/jsonwebtoken/Jwts.builder:()Lio/jsonwebtoken/JwtBuilder;
275 | 1.8
276 | Use io.trino.server.security.jwt.JwtsUtil or equivalent
277 |
278 |
279 |
280 | io/jsonwebtoken/Jwts.parserBuilder:()Lio/jsonwebtoken/JwtParserBuilder;
281 | 1.8
282 | Use io.trino.server.security.jwt.JwtsUtil or equivalent
283 |
284 |
285 |
286 | org/openjdk/jol/info/ClassLayout.instanceSize:()J
287 | 1.8
288 | Use io.airlift.slice.SizeOf.instanceSize
289 |
290 |
291 |
292 | org/testng/annotations/BeforeTest
293 | 1.8
294 | Prefer org.testng.annotations.BeforeClass
295 |
296 |
297 |
298 | org/testng/annotations/AfterTest
299 | 1.8
300 | Prefer org.testng.annotations.AfterClass
301 |
302 |
303 |
304 | com/fasterxml/jackson/core/JsonFactory."<init>":()V
305 | 1.8
306 | Use io.trino.plugin.base.util.JsonUtils.jsonFactory()
307 |
308 |
309 |
310 | com/fasterxml/jackson/core/JsonFactoryBuilder."<init>":()V
311 | 1.8
312 | Use io.trino.plugin.base.util.JsonUtils.jsonFactoryBuilder() instead
313 |
314 |
315 |
--------------------------------------------------------------------------------
/.mvn/rrf/groupId-central.maven.org.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airlift/units/2099ad602a106369f3b182be077ab69ed8df87b6/.mvn/rrf/groupId-central.maven.org.txt
--------------------------------------------------------------------------------
/.mvn/rrf/groupId-confluent.txt:
--------------------------------------------------------------------------------
1 | io.confluent
2 | org.apache.kafka
3 |
4 |
--------------------------------------------------------------------------------
/.mvn/rrf/groupId-flyway-repo.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airlift/units/2099ad602a106369f3b182be077ab69ed8df87b6/.mvn/rrf/groupId-flyway-repo.txt
--------------------------------------------------------------------------------
/.mvn/rrf/groupId-google-maven-central-copy.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airlift/units/2099ad602a106369f3b182be077ab69ed8df87b6/.mvn/rrf/groupId-google-maven-central-copy.txt
--------------------------------------------------------------------------------
/.mvn/rrf/groupId-jitpack.io.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airlift/units/2099ad602a106369f3b182be077ab69ed8df87b6/.mvn/rrf/groupId-jitpack.io.txt
--------------------------------------------------------------------------------
/.mvn/rrf/groupId-maven-central.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airlift/units/2099ad602a106369f3b182be077ab69ed8df87b6/.mvn/rrf/groupId-maven-central.txt
--------------------------------------------------------------------------------
/.mvn/rrf/groupId-ossrh.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airlift/units/2099ad602a106369f3b182be077ab69ed8df87b6/.mvn/rrf/groupId-ossrh.txt
--------------------------------------------------------------------------------
/.mvn/rrf/groupId-repo.gradle.org.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airlift/units/2099ad602a106369f3b182be077ab69ed8df87b6/.mvn/rrf/groupId-repo.gradle.org.txt
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Units
2 | [](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.airlift%22%20AND%20a%3A%22units%22)
3 |
4 | Units is a Java library for immutable, human-friendly units.
5 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.3.2
23 | #
24 | # Optional ENV vars
25 | # -----------------
26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source
27 | # MVNW_REPOURL - repo url base for downloading maven distribution
28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
30 | # ----------------------------------------------------------------------------
31 |
32 | set -euf
33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x
34 |
35 | # OS specific support.
36 | native_path() { printf %s\\n "$1"; }
37 | case "$(uname)" in
38 | CYGWIN* | MINGW*)
39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
40 | native_path() { cygpath --path --windows "$1"; }
41 | ;;
42 | esac
43 |
44 | # set JAVACMD and JAVACCMD
45 | set_java_home() {
46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
47 | if [ -n "${JAVA_HOME-}" ]; then
48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
49 | # IBM's JDK on AIX uses strange locations for the executables
50 | JAVACMD="$JAVA_HOME/jre/sh/java"
51 | JAVACCMD="$JAVA_HOME/jre/sh/javac"
52 | else
53 | JAVACMD="$JAVA_HOME/bin/java"
54 | JAVACCMD="$JAVA_HOME/bin/javac"
55 |
56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
59 | return 1
60 | fi
61 | fi
62 | else
63 | JAVACMD="$(
64 | 'set' +e
65 | 'unset' -f command 2>/dev/null
66 | 'command' -v java
67 | )" || :
68 | JAVACCMD="$(
69 | 'set' +e
70 | 'unset' -f command 2>/dev/null
71 | 'command' -v javac
72 | )" || :
73 |
74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
76 | return 1
77 | fi
78 | fi
79 | }
80 |
81 | # hash string like Java String::hashCode
82 | hash_string() {
83 | str="${1:-}" h=0
84 | while [ -n "$str" ]; do
85 | char="${str%"${str#?}"}"
86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
87 | str="${str#?}"
88 | done
89 | printf %x\\n $h
90 | }
91 |
92 | verbose() { :; }
93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
94 |
95 | die() {
96 | printf %s\\n "$1" >&2
97 | exit 1
98 | }
99 |
100 | trim() {
101 | # MWRAPPER-139:
102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
103 | # Needed for removing poorly interpreted newline sequences when running in more
104 | # exotic environments such as mingw bash on Windows.
105 | printf "%s" "${1}" | tr -d '[:space:]'
106 | }
107 |
108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
109 | while IFS="=" read -r key value; do
110 | case "${key-}" in
111 | distributionUrl) distributionUrl=$(trim "${value-}") ;;
112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
113 | esac
114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
116 |
117 | case "${distributionUrl##*/}" in
118 | maven-mvnd-*bin.*)
119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
125 | *)
126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
127 | distributionPlatform=linux-amd64
128 | ;;
129 | esac
130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
131 | ;;
132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
134 | esac
135 |
136 | # apply MVNW_REPOURL and calculate MAVEN_HOME
137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
139 | distributionUrlName="${distributionUrl##*/}"
140 | distributionUrlNameMain="${distributionUrlName%.*}"
141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}"
142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
144 |
145 | exec_maven() {
146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
148 | }
149 |
150 | if [ -d "$MAVEN_HOME" ]; then
151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME"
152 | exec_maven "$@"
153 | fi
154 |
155 | case "${distributionUrl-}" in
156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
158 | esac
159 |
160 | # prepare tmp dir
161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
163 | trap clean HUP INT TERM EXIT
164 | else
165 | die "cannot create temp dir"
166 | fi
167 |
168 | mkdir -p -- "${MAVEN_HOME%/*}"
169 |
170 | # Download and Install Apache Maven
171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
172 | verbose "Downloading from: $distributionUrl"
173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
174 |
175 | # select .zip or .tar.gz
176 | if ! command -v unzip >/dev/null; then
177 | distributionUrl="${distributionUrl%.zip}.tar.gz"
178 | distributionUrlName="${distributionUrl##*/}"
179 | fi
180 |
181 | # verbose opt
182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
184 |
185 | # normalize http auth
186 | case "${MVNW_PASSWORD:+has-password}" in
187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
189 | esac
190 |
191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
192 | verbose "Found wget ... using wget"
193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
195 | verbose "Found curl ... using curl"
196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
197 | elif set_java_home; then
198 | verbose "Falling back to use Java to download"
199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
201 | cat >"$javaSource" <<-END
202 | public class Downloader extends java.net.Authenticator
203 | {
204 | protected java.net.PasswordAuthentication getPasswordAuthentication()
205 | {
206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
207 | }
208 | public static void main( String[] args ) throws Exception
209 | {
210 | setDefault( new Downloader() );
211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
212 | }
213 | }
214 | END
215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java
216 | verbose " - Compiling Downloader.java ..."
217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
218 | verbose " - Running Downloader.java ..."
219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
220 | fi
221 |
222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
223 | if [ -n "${distributionSha256Sum-}" ]; then
224 | distributionSha256Result=false
225 | if [ "$MVN_CMD" = mvnd.sh ]; then
226 | echo "Checksum validation is not supported for maven-mvnd." >&2
227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
228 | exit 1
229 | elif command -v sha256sum >/dev/null; then
230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
231 | distributionSha256Result=true
232 | fi
233 | elif command -v shasum >/dev/null; then
234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
235 | distributionSha256Result=true
236 | fi
237 | else
238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
240 | exit 1
241 | fi
242 | if [ $distributionSha256Result = false ]; then
243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
245 | exit 1
246 | fi
247 | fi
248 |
249 | # unzip and move
250 | if command -v unzip >/dev/null; then
251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
252 | else
253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
254 | fi
255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
257 |
258 | clean || :
259 | exec_maven "$@"
260 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | io.airlift
7 | airbase
8 | 182
9 |
10 | units
11 | 1.11-SNAPSHOT
12 | jar
13 |
14 | units
15 | Library for immutable human-friendly units
16 | https://github.com/airlift/units
17 |
18 | 2016
19 |
20 |
21 | Airlift
22 |
23 |
24 |
25 |
26 | Apache License 2.0
27 | http://www.apache.org/licenses/LICENSE-2.0
28 | repo
29 |
30 |
31 |
32 |
33 | true
34 | true
35 | -missing
36 | 17
37 | 234
38 |
39 |
40 |
41 |
42 |
43 | com.fasterxml.jackson.core
44 | jackson-annotations
45 | true
46 |
47 |
48 | jakarta.validation
49 | jakarta.validation-api
50 | true
51 |
52 |
53 |
54 | com.google.guava
55 | guava
56 | test
57 |
58 |
59 |
60 | io.airlift
61 | json
62 | ${dep.airlift.version}
63 | test
64 |
65 |
66 |
67 | io.airlift
68 | testing
69 | ${dep.airlift.version}
70 | test
71 |
72 |
73 |
74 | org.assertj
75 | assertj-core
76 | test
77 |
78 |
79 |
80 | org.hibernate.validator
81 | hibernate-validator
82 | test
83 |
84 |
85 |
86 |
87 | org.testng
88 | testng
89 | test
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/DataSize.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import com.fasterxml.jackson.annotation.JsonCreator;
19 | import com.fasterxml.jackson.annotation.JsonValue;
20 |
21 | import java.util.Locale;
22 | import java.util.regex.Matcher;
23 | import java.util.regex.Pattern;
24 |
25 | import static io.airlift.units.Preconditions.checkArgument;
26 | import static java.lang.Math.floor;
27 | import static java.lang.Math.multiplyExact;
28 | import static java.lang.String.format;
29 | import static java.util.Objects.requireNonNull;
30 |
31 | public final class DataSize
32 | implements Comparable
33 | {
34 | private static final Pattern DECIMAL_WITH_UNIT_PATTERN = Pattern.compile("^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$");
35 |
36 | // We iterate over the DATASIZE_UNITS constant in convertToMostSuccinctDataSize()
37 | // instead of Unit.values() as the latter results in non-trivial amount of memory
38 | // allocation when that method is called in a tight loop. The reason is that the values()
39 | // call allocates a new array at each call.
40 | private static final Unit[] DATASIZE_UNITS = Unit.values();
41 |
42 | /**
43 | * Creates a {@link DataSize} instance with the provided quantity of the provided {@link Unit}. This
44 | * value is immediately converted to bytes which might overflow.
45 | *
46 | * @param size The quantity of the supplied unit
47 | * @param unit The unit to use as the default unit for the constructed instance and to convert the size to bytes
48 | * @throws IllegalArgumentException If the provided size would overflow a long value when converting to bytes
49 | */
50 | public static DataSize of(long size, Unit unit)
51 | throws IllegalArgumentException
52 | {
53 | requireNonNull(unit, "unit is null");
54 | checkArgument(size >= 0, "size is negative: %s", size);
55 | if (unit == Unit.BYTE) {
56 | return new DataSize(size, unit);
57 | }
58 | try {
59 | return new DataSize(multiplyExact(size, unit.inBytes()), unit);
60 | }
61 | catch (ArithmeticException e) {
62 | throw new IllegalArgumentException(format("size is too large to be represented in bytes: %s%s", size, unit.getUnitString()));
63 | }
64 | }
65 |
66 | public static DataSize ofBytes(long bytes)
67 | {
68 | return new DataSize(bytes, Unit.BYTE);
69 | }
70 |
71 | /**
72 | * Prefer {@link DataSize#ofBytes(long)} when conversion to the most 'succinct' unit is not necessary or desirable
73 | */
74 | public static DataSize succinctBytes(long bytes)
75 | {
76 | return ofBytes(bytes).succinct();
77 | }
78 |
79 | /**
80 | * Prefer {@link DataSize#of(long, Unit)} when conversion to the most 'succinct' unit is not necessary or desirable.
81 | * Otherwise, use {@link DataSize#succinctBytes(long)} since it will not incur rounding and loss of precision.
82 | *
83 | * @deprecated use {@link DataSize#succinctBytes(long)} instead, double values are imprecise
84 | */
85 | @Deprecated
86 | public static DataSize succinctDataSize(double size, Unit unit)
87 | {
88 | return new DataSize(roundDoubleSizeInUnitToLongBytes(size, unit), unit).succinct();
89 | }
90 |
91 | private final long bytes;
92 | private final Unit unit;
93 |
94 | /**
95 | * Private constructor to avoid confusing usage sites with having to pass a number of bytes
96 | * alongside non-bytes unit
97 | *
98 | * @param bytes The number of bytes, regardless of unit
99 | * @param unit The preferred display unit of this value
100 | */
101 | private DataSize(long bytes, Unit unit)
102 | {
103 | this.unit = requireNonNull(unit, "unit is null");
104 | checkArgument(bytes >= 0, "bytes is negative");
105 | this.bytes = bytes;
106 | }
107 |
108 | /**
109 | * @deprecated Use {@link DataSize#of(long, Unit)} instead. The imprecise nature of using doubles for DataSize is deprecated for removal
110 | */
111 | @Deprecated
112 | public DataSize(double size, Unit unit)
113 | {
114 | this.unit = requireNonNull(unit, "unit is null");
115 | this.bytes = roundDoubleSizeInUnitToLongBytes(size, unit);
116 | }
117 |
118 | public long toBytes()
119 | {
120 | return bytes;
121 | }
122 |
123 | /**
124 | * @deprecated Use {@link DataSize#toBytes()} instead to avoid floating point precision semantics
125 | */
126 | @Deprecated
127 | public double getValue()
128 | {
129 | return getValue(this.unit);
130 | }
131 |
132 | public Unit getUnit()
133 | {
134 | return unit;
135 | }
136 |
137 | /**
138 | * @deprecated Use {@link DataSize#toBytes()} instead to avoid floating point precision semantics
139 | */
140 | @Deprecated
141 | public double getValue(Unit unit)
142 | {
143 | requireNonNull(unit, "unit is null");
144 | if (unit == Unit.BYTE) {
145 | return (double) bytes;
146 | }
147 | return bytes * (1.0d / unit.inBytes());
148 | }
149 |
150 | /**
151 | * @deprecated Use {@link DataSize#toBytes()} instead. This method uses floating point semantics to compute the
152 | * rounded value which can yield to unexpected loss of precision beyond the intended rounding
153 | */
154 | @Deprecated
155 | public long roundTo(Unit unit)
156 | {
157 | requireNonNull(unit, "unit is null");
158 | if (unit == Unit.BYTE) {
159 | return bytes;
160 | }
161 | double rounded = floor(getValue(unit) + 0.5d);
162 | checkArgument(rounded <= Long.MAX_VALUE,
163 | "size is too large to be represented in requested unit as a long: %s%s", rounded, unit.getUnitString());
164 | return (long) rounded;
165 | }
166 |
167 | private Unit succinctUnit()
168 | {
169 | Unit unitToUse = Unit.BYTE;
170 | for (Unit unitToTest : DATASIZE_UNITS) {
171 | if (unitToTest.bytes <= bytes) {
172 | unitToUse = unitToTest;
173 | }
174 | else {
175 | break;
176 | }
177 | }
178 | return unitToUse;
179 | }
180 |
181 | /**
182 | * @deprecated Use {@link DataSize#to(Unit)} instead. No conversion occurs when calling this method, only the unit
183 | * used for the default string representation is changed. This has no effect on the unit used during JSON serialization
184 | */
185 | @Deprecated
186 | public DataSize convertTo(Unit unit)
187 | {
188 | return to(unit);
189 | }
190 |
191 | public DataSize to(Unit unit)
192 | {
193 | return new DataSize(bytes, unit);
194 | }
195 |
196 | public DataSize succinct()
197 | {
198 | return to(succinctUnit());
199 | }
200 |
201 | /**
202 | * @deprecated Use {@link DataSize#succinct()} instead. No conversion occurs when calling this method, only the unit
203 | * used for the default string representation is changed. This has no effect on the unit used during JSON serialization
204 | */
205 | @Deprecated
206 | public DataSize convertToMostSuccinctDataSize()
207 | {
208 | return succinct();
209 | }
210 |
211 | @JsonValue
212 | public String toBytesValueString()
213 | {
214 | return bytes + Unit.BYTE.getUnitString();
215 | }
216 |
217 | @Override
218 | public String toString()
219 | {
220 | // Fast-path for exact byte values
221 | if (this.unit == Unit.BYTE) {
222 | return toBytesValueString();
223 | }
224 | double unitValue = getValue();
225 | //noinspection FloatingPointEquality
226 | if (floor(unitValue) == unitValue) {
227 | return ((long) unitValue) + unit.getUnitString();
228 | }
229 | return format(Locale.ENGLISH, "%.2f%s", unitValue, unit.getUnitString());
230 | }
231 |
232 | @JsonCreator
233 | public static DataSize valueOf(String size)
234 | throws IllegalArgumentException
235 | {
236 | requireNonNull(size, "size is null");
237 | checkArgument(!size.isEmpty(), "size is empty");
238 |
239 | // Attempt fast path parsing of JSON values without regex validation
240 | int stringLength = size.length();
241 | if (stringLength > 1 && stringLength <= 20 && size.charAt(0) != '+' && size.charAt(stringLength - 1) == 'B') {
242 | // must have at least 1 numeric char, less than Long.MAX_VALUE numeric chars, not start with a sign indicator, and be in unit BYTES
243 | try {
244 | return DataSize.ofBytes(Long.parseLong(size, 0, stringLength - 1, 10));
245 | }
246 | catch (Exception ignored) {
247 | // Ignored, slow path will either handle or produce the appropriate error from here
248 | }
249 | }
250 |
251 | Matcher longOrDouble = DECIMAL_WITH_UNIT_PATTERN.matcher(size);
252 | if (!longOrDouble.matches()) {
253 | throw new IllegalArgumentException("size is not a valid data size string: " + size);
254 | }
255 | Unit unit = Unit.fromUnitString(longOrDouble.group(2));
256 | String number = longOrDouble.group(1);
257 | if (number.indexOf('.') == -1) {
258 | // Strings without decimals can avoid precision loss by parsing as long
259 | return DataSize.of(Long.parseLong(number), unit);
260 | }
261 | return new DataSize(roundDoubleSizeInUnitToLongBytes(Double.parseDouble(number), unit), unit);
262 | }
263 |
264 | private static long roundDoubleSizeInUnitToLongBytes(double size, Unit unit)
265 | {
266 | checkArgument(!Double.isInfinite(size), "size is infinite");
267 | checkArgument(!Double.isNaN(size), "size is not a number");
268 | checkArgument(size >= 0, "size is negative: %s", size);
269 | requireNonNull(unit, "unit is null");
270 | double rounded = floor((size / (1.0d / unit.inBytes())) + 0.5d);
271 | checkArgument(rounded <= Long.MAX_VALUE,
272 | "size is too large to be represented in requested unit as a long: %s%s", size, unit.getUnitString());
273 | return (long) rounded;
274 | }
275 |
276 | @Override
277 | public int compareTo(DataSize o)
278 | {
279 | return Long.compare(bytes, o.bytes);
280 | }
281 |
282 | @Override
283 | public boolean equals(Object o)
284 | {
285 | if (this == o) {
286 | return true;
287 | }
288 | if (o == null || getClass() != o.getClass()) {
289 | return false;
290 | }
291 | return bytes == ((DataSize) o).bytes;
292 | }
293 |
294 | @Override
295 | public int hashCode()
296 | {
297 | return Long.hashCode(bytes);
298 | }
299 |
300 | public enum Unit
301 | {
302 | //This order is important, it should be in increasing magnitude.
303 | BYTE(1L, "B"),
304 | KILOBYTE(1L << 10, "kB"),
305 | MEGABYTE(1L << 20, "MB"),
306 | GIGABYTE(1L << 30, "GB"),
307 | TERABYTE(1L << 40, "TB"),
308 | PETABYTE(1L << 50, "PB");
309 |
310 | private final long bytes;
311 | private final String unitString;
312 |
313 | Unit(long bytes, String unitString)
314 | {
315 | this.bytes = bytes;
316 | this.unitString = unitString;
317 | }
318 |
319 | public long inBytes()
320 | {
321 | return bytes;
322 | }
323 |
324 | public String getUnitString()
325 | {
326 | return unitString;
327 | }
328 |
329 | private static Unit fromUnitString(String unitString)
330 | {
331 | for (Unit unit : DATASIZE_UNITS) {
332 | if (unit.unitString.equals(unitString)) {
333 | return unit;
334 | }
335 | }
336 | throw new IllegalArgumentException("Unknown unit: " + unitString);
337 | }
338 | }
339 | }
340 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/Duration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import com.fasterxml.jackson.annotation.JsonCreator;
19 | import com.fasterxml.jackson.annotation.JsonValue;
20 |
21 | import java.util.Locale;
22 | import java.util.concurrent.TimeUnit;
23 | import java.util.regex.Matcher;
24 | import java.util.regex.Pattern;
25 |
26 | import static io.airlift.units.Preconditions.checkArgument;
27 | import static java.lang.Math.floor;
28 | import static java.lang.String.format;
29 | import static java.util.Objects.requireNonNull;
30 | import static java.util.concurrent.TimeUnit.DAYS;
31 | import static java.util.concurrent.TimeUnit.HOURS;
32 | import static java.util.concurrent.TimeUnit.MICROSECONDS;
33 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
34 | import static java.util.concurrent.TimeUnit.MINUTES;
35 | import static java.util.concurrent.TimeUnit.NANOSECONDS;
36 | import static java.util.concurrent.TimeUnit.SECONDS;
37 |
38 | public final class Duration
39 | implements Comparable
40 | {
41 | private static final Pattern PATTERN = Pattern.compile("^\\s*(\\d+(?:\\.\\d+)?)\\s*([a-zA-Z]+)\\s*$");
42 |
43 | // We iterate over the TIME_UNITS constant in convertToMostSuccinctTimeUnit()
44 | // instead of TimeUnit.values() as the latter results in non-trivial amount of memory
45 | // allocation when that method is called in a tight loop. The reason is that the values()
46 | // call allocates a new array at each call.
47 | private static final TimeUnit[] TIME_UNITS = TimeUnit.values();
48 |
49 | public static final Duration ZERO = new Duration(0, SECONDS);
50 |
51 | public static Duration nanosSince(long start)
52 | {
53 | return succinctNanos(System.nanoTime() - start);
54 | }
55 |
56 | public static Duration succinctNanos(long nanos)
57 | {
58 | return succinctDuration(nanos, NANOSECONDS);
59 | }
60 |
61 | public static Duration succinctDuration(double value, TimeUnit unit)
62 | {
63 | if (value == 0) {
64 | return ZERO;
65 | }
66 | return new Duration(value, unit).convertToMostSuccinctTimeUnit();
67 | }
68 |
69 | private final double value;
70 | private final TimeUnit unit;
71 |
72 | public Duration(double value, TimeUnit unit)
73 | {
74 | if (Double.isInfinite(value)) {
75 | throw new IllegalArgumentException("value is infinite: " + value);
76 | }
77 | checkArgument(!Double.isNaN(value), "value is not a number");
78 | if (value < 0) {
79 | throw new IllegalArgumentException("value is negative: " + value);
80 | }
81 | requireNonNull(unit, "unit is null");
82 |
83 | this.value = value;
84 | this.unit = unit;
85 | }
86 |
87 | public long toMillis()
88 | {
89 | return roundTo(MILLISECONDS);
90 | }
91 |
92 | public double getValue()
93 | {
94 | return value;
95 | }
96 |
97 | public TimeUnit getUnit()
98 | {
99 | return unit;
100 | }
101 |
102 | public double getValue(TimeUnit timeUnit)
103 | {
104 | requireNonNull(timeUnit, "timeUnit is null");
105 | return value * (millisPerTimeUnit(this.unit) * 1.0 / millisPerTimeUnit(timeUnit));
106 | }
107 |
108 | public long roundTo(TimeUnit timeUnit)
109 | {
110 | requireNonNull(timeUnit, "timeUnit is null");
111 | double rounded = Math.floor(getValue(timeUnit) + 0.5d);
112 | if (rounded > Long.MAX_VALUE) {
113 | throw new IllegalArgumentException(format("value %s %s is too large to be represented in requested unit %s as a long", value, unit, timeUnit));
114 | }
115 | return (long) rounded;
116 | }
117 |
118 | public Duration convertTo(TimeUnit timeUnit)
119 | {
120 | requireNonNull(timeUnit, "timeUnit is null");
121 | return new Duration(getValue(timeUnit), timeUnit);
122 | }
123 |
124 | public Duration convertToMostSuccinctTimeUnit()
125 | {
126 | TimeUnit unitToUse = NANOSECONDS;
127 | for (TimeUnit unitToTest : TIME_UNITS) {
128 | // since time units are powers of ten, we can get rounding errors here, so fuzzy match
129 | if (getValue(unitToTest) > 0.9999) {
130 | unitToUse = unitToTest;
131 | }
132 | else {
133 | break;
134 | }
135 | }
136 | return convertTo(unitToUse);
137 | }
138 |
139 | public java.time.Duration toJavaTime()
140 | {
141 | long seconds;
142 | long nanoAdjustment;
143 | long secondsPerUnit = SECONDS.convert(1, unit);
144 | long nanosPerUnit = NANOSECONDS.convert(1, unit);
145 | if (secondsPerUnit > 1) {
146 | seconds = (long) floor(value * secondsPerUnit);
147 | nanoAdjustment = (long) floor((value - (double) seconds / secondsPerUnit) * nanosPerUnit);
148 | }
149 | else {
150 | long unitsPerSecond = unit.convert(1, SECONDS);
151 | seconds = (long) floor(value / unitsPerSecond);
152 | nanoAdjustment = (long) floor((value - (double) seconds * unitsPerSecond) * nanosPerUnit);
153 | }
154 |
155 | if (seconds == Long.MAX_VALUE) {
156 | nanoAdjustment = 0;
157 | }
158 | return java.time.Duration.ofSeconds(seconds, nanoAdjustment);
159 | }
160 |
161 | @JsonValue
162 | @Override
163 | public String toString()
164 | {
165 | return toString(unit);
166 | }
167 |
168 | public String toString(TimeUnit timeUnit)
169 | {
170 | requireNonNull(timeUnit, "timeUnit is null");
171 | double magnitude = getValue(timeUnit);
172 | String timeUnitAbbreviation = timeUnitToString(timeUnit);
173 | return format(Locale.ENGLISH, "%.2f%s", magnitude, timeUnitAbbreviation);
174 | }
175 |
176 | @JsonCreator
177 | public static Duration valueOf(String duration)
178 | throws IllegalArgumentException
179 | {
180 | requireNonNull(duration, "duration is null");
181 | checkArgument(!duration.isEmpty(), "duration is empty");
182 |
183 | Matcher matcher = PATTERN.matcher(duration);
184 | if (!matcher.matches()) {
185 | throw new IllegalArgumentException("duration is not a valid data duration string: " + duration);
186 | }
187 |
188 | double value = Double.parseDouble(matcher.group(1));
189 | String unitString = matcher.group(2);
190 |
191 | TimeUnit timeUnit = valueOfTimeUnit(unitString);
192 | return new Duration(value, timeUnit);
193 | }
194 |
195 | @Override
196 | public int compareTo(Duration o)
197 | {
198 | return Double.compare(getValue(MILLISECONDS), o.getValue(MILLISECONDS));
199 | }
200 |
201 | public boolean isZero()
202 | {
203 | return equals(ZERO);
204 | }
205 |
206 | @Override
207 | public boolean equals(Object o)
208 | {
209 | if (this == o) {
210 | return true;
211 | }
212 | if (o == null || getClass() != o.getClass()) {
213 | return false;
214 | }
215 |
216 | Duration duration = (Duration) o;
217 |
218 | return compareTo(duration) == 0;
219 | }
220 |
221 | @Override
222 | public int hashCode()
223 | {
224 | double value = getValue(MILLISECONDS);
225 | return Double.hashCode(value);
226 | }
227 |
228 | public static TimeUnit valueOfTimeUnit(String timeUnitString)
229 | {
230 | requireNonNull(timeUnitString, "timeUnitString is null");
231 | switch (timeUnitString) {
232 | case "ns":
233 | return NANOSECONDS;
234 | case "us":
235 | return MICROSECONDS;
236 | case "ms":
237 | return MILLISECONDS;
238 | case "s":
239 | return SECONDS;
240 | case "m":
241 | return MINUTES;
242 | case "h":
243 | return HOURS;
244 | case "d":
245 | return DAYS;
246 | default:
247 | throw new IllegalArgumentException("Unknown time unit: " + timeUnitString);
248 | }
249 | }
250 |
251 | public static String timeUnitToString(TimeUnit timeUnit)
252 | {
253 | requireNonNull(timeUnit, "timeUnit is null");
254 | switch (timeUnit) {
255 | case NANOSECONDS:
256 | return "ns";
257 | case MICROSECONDS:
258 | return "us";
259 | case MILLISECONDS:
260 | return "ms";
261 | case SECONDS:
262 | return "s";
263 | case MINUTES:
264 | return "m";
265 | case HOURS:
266 | return "h";
267 | case DAYS:
268 | return "d";
269 | default:
270 | throw new IllegalArgumentException("Unsupported time unit " + timeUnit);
271 | }
272 | }
273 |
274 | private static double millisPerTimeUnit(TimeUnit timeUnit)
275 | {
276 | switch (timeUnit) {
277 | case NANOSECONDS:
278 | return 1.0 / 1000000.0;
279 | case MICROSECONDS:
280 | return 1.0 / 1000.0;
281 | case MILLISECONDS:
282 | return 1;
283 | case SECONDS:
284 | return 1000;
285 | case MINUTES:
286 | return 1000 * 60;
287 | case HOURS:
288 | return 1000 * 60 * 60;
289 | case DAYS:
290 | return 1000 * 60 * 60 * 24;
291 | default:
292 | throw new IllegalArgumentException("Unsupported time unit " + timeUnit);
293 | }
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MaxDataSize.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.Constraint;
17 | import jakarta.validation.Payload;
18 |
19 | import java.lang.annotation.Documented;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.Target;
22 |
23 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
24 | import static java.lang.annotation.ElementType.CONSTRUCTOR;
25 | import static java.lang.annotation.ElementType.FIELD;
26 | import static java.lang.annotation.ElementType.METHOD;
27 | import static java.lang.annotation.ElementType.PARAMETER;
28 | import static java.lang.annotation.ElementType.TYPE_USE;
29 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
30 |
31 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
32 | @Retention(RUNTIME)
33 | @Documented
34 | @Constraint(validatedBy = MaxDataSizeValidator.class)
35 | public @interface MaxDataSize
36 | {
37 | String value();
38 |
39 | String message() default "{io.airlift.units.MaxDataSize.message}";
40 |
41 | Class>[] groups() default {};
42 |
43 | Class extends Payload>[] payload() default {};
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MaxDataSizeValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.ConstraintValidator;
17 | import jakarta.validation.ConstraintValidatorContext;
18 |
19 | public class MaxDataSizeValidator
20 | implements ConstraintValidator
21 | {
22 | private DataSize max;
23 |
24 | @Override
25 | public void initialize(MaxDataSize dataSize)
26 | {
27 | this.max = DataSize.valueOf(dataSize.value());
28 | }
29 |
30 | @Override
31 | public boolean isValid(DataSize dataSize, ConstraintValidatorContext context)
32 | {
33 | return (dataSize == null) || (dataSize.compareTo(max) <= 0);
34 | }
35 |
36 | @Override
37 | public String toString()
38 | {
39 | return "max:" + max;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MaxDuration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import jakarta.validation.Constraint;
19 | import jakarta.validation.Payload;
20 |
21 | import java.lang.annotation.Documented;
22 | import java.lang.annotation.Retention;
23 | import java.lang.annotation.Target;
24 |
25 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
26 | import static java.lang.annotation.ElementType.CONSTRUCTOR;
27 | import static java.lang.annotation.ElementType.FIELD;
28 | import static java.lang.annotation.ElementType.METHOD;
29 | import static java.lang.annotation.ElementType.PARAMETER;
30 | import static java.lang.annotation.ElementType.TYPE_USE;
31 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
32 |
33 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
34 | @Retention(RUNTIME)
35 | @Documented
36 | @Constraint(validatedBy = MaxDurationValidator.class)
37 | public @interface MaxDuration
38 | {
39 | String value();
40 |
41 | String message() default "{io.airlift.units.MaxDuration.message}";
42 |
43 | Class>[] groups() default {};
44 |
45 | Class extends Payload>[] payload() default {};
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MaxDurationValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import jakarta.validation.ConstraintValidator;
19 | import jakarta.validation.ConstraintValidatorContext;
20 |
21 | public class MaxDurationValidator
22 | implements ConstraintValidator
23 | {
24 | private Duration max;
25 |
26 | @Override
27 | public void initialize(MaxDuration duration)
28 | {
29 | this.max = Duration.valueOf(duration.value());
30 | }
31 |
32 | @Override
33 | public boolean isValid(Duration duration, ConstraintValidatorContext context)
34 | {
35 | return duration == null || duration.compareTo(max) <= 0;
36 | }
37 |
38 | @Override
39 | public String toString()
40 | {
41 | return "max:" + max;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MaxThreadCount.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.Constraint;
17 | import jakarta.validation.Payload;
18 |
19 | import java.lang.annotation.Documented;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.Target;
22 |
23 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
24 | import static java.lang.annotation.ElementType.CONSTRUCTOR;
25 | import static java.lang.annotation.ElementType.FIELD;
26 | import static java.lang.annotation.ElementType.METHOD;
27 | import static java.lang.annotation.ElementType.PARAMETER;
28 | import static java.lang.annotation.ElementType.TYPE_USE;
29 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
30 |
31 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
32 | @Retention(RUNTIME)
33 | @Documented
34 | @Constraint(validatedBy = MaxThreadCountValidator.class)
35 | public @interface MaxThreadCount
36 | {
37 | String value();
38 |
39 | String message() default "{io.airlift.units.MaxThreadCount.message}";
40 |
41 | Class>[] groups() default {};
42 |
43 | Class extends Payload>[] payload() default {};
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MaxThreadCountValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.ConstraintValidator;
17 | import jakarta.validation.ConstraintValidatorContext;
18 |
19 | public class MaxThreadCountValidator
20 | implements ConstraintValidator
21 | {
22 | private ThreadCount max;
23 |
24 | @Override
25 | public void initialize(MaxThreadCount annotation)
26 | {
27 | this.max = ThreadCount.valueOf(annotation.value());
28 | }
29 |
30 | @Override
31 | public boolean isValid(ThreadCount threadCount, ConstraintValidatorContext constraintValidatorContext)
32 | {
33 | return threadCount == null || threadCount.compareTo(max) <= 0;
34 | }
35 |
36 | @Override
37 | public String toString()
38 | {
39 | return "max: " + max;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MinDataSize.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.Constraint;
17 | import jakarta.validation.Payload;
18 |
19 | import java.lang.annotation.Documented;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.Target;
22 |
23 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
24 | import static java.lang.annotation.ElementType.CONSTRUCTOR;
25 | import static java.lang.annotation.ElementType.FIELD;
26 | import static java.lang.annotation.ElementType.METHOD;
27 | import static java.lang.annotation.ElementType.PARAMETER;
28 | import static java.lang.annotation.ElementType.TYPE_USE;
29 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
30 |
31 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
32 | @Retention(RUNTIME)
33 | @Documented
34 | @Constraint(validatedBy = MinDataSizeValidator.class)
35 | public @interface MinDataSize
36 | {
37 | String value();
38 |
39 | String message() default "{io.airlift.units.MinDataSize.message}";
40 |
41 | Class>[] groups() default {};
42 |
43 | Class extends Payload>[] payload() default {};
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MinDataSizeValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.ConstraintValidator;
17 | import jakarta.validation.ConstraintValidatorContext;
18 |
19 | public class MinDataSizeValidator
20 | implements ConstraintValidator
21 | {
22 | private DataSize min;
23 |
24 | @Override
25 | public void initialize(MinDataSize dataSize)
26 | {
27 | this.min = DataSize.valueOf(dataSize.value());
28 | }
29 |
30 | @Override
31 | public boolean isValid(DataSize dataSize, ConstraintValidatorContext context)
32 | {
33 | return (dataSize == null) || (dataSize.compareTo(min) >= 0);
34 | }
35 |
36 | @Override
37 | public String toString()
38 | {
39 | return "min:" + min;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MinDuration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import jakarta.validation.Constraint;
19 | import jakarta.validation.Payload;
20 |
21 | import java.lang.annotation.Documented;
22 | import java.lang.annotation.Retention;
23 | import java.lang.annotation.Target;
24 |
25 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
26 | import static java.lang.annotation.ElementType.CONSTRUCTOR;
27 | import static java.lang.annotation.ElementType.FIELD;
28 | import static java.lang.annotation.ElementType.METHOD;
29 | import static java.lang.annotation.ElementType.PARAMETER;
30 | import static java.lang.annotation.ElementType.TYPE_USE;
31 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
32 |
33 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
34 | @Retention(RUNTIME)
35 | @Documented
36 | @Constraint(validatedBy = MinDurationValidator.class)
37 | public @interface MinDuration
38 | {
39 | String value();
40 |
41 | String message() default "{io.airlift.units.MinDuration.message}";
42 |
43 | Class>[] groups() default {};
44 |
45 | Class extends Payload>[] payload() default {};
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MinDurationValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import jakarta.validation.ConstraintValidator;
19 | import jakarta.validation.ConstraintValidatorContext;
20 |
21 | public class MinDurationValidator
22 | implements ConstraintValidator
23 | {
24 | private Duration min;
25 |
26 | @Override
27 | public void initialize(MinDuration duration)
28 | {
29 | this.min = Duration.valueOf(duration.value());
30 | }
31 |
32 | @Override
33 | public boolean isValid(Duration duration, ConstraintValidatorContext context)
34 | {
35 | return duration == null || duration.compareTo(min) >= 0;
36 | }
37 |
38 | @Override
39 | public String toString()
40 | {
41 | return "min:" + min;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MinThreadCount.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.Constraint;
17 | import jakarta.validation.Payload;
18 |
19 | import java.lang.annotation.Documented;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.Target;
22 |
23 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
24 | import static java.lang.annotation.ElementType.CONSTRUCTOR;
25 | import static java.lang.annotation.ElementType.FIELD;
26 | import static java.lang.annotation.ElementType.METHOD;
27 | import static java.lang.annotation.ElementType.PARAMETER;
28 | import static java.lang.annotation.ElementType.TYPE_USE;
29 | import static java.lang.annotation.RetentionPolicy.RUNTIME;
30 |
31 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
32 | @Retention(RUNTIME)
33 | @Documented
34 | @Constraint(validatedBy = MinThreadCountValidator.class)
35 | public @interface MinThreadCount
36 | {
37 | String value();
38 |
39 | String message() default "{io.airlift.units.MinThreadCount.message}";
40 |
41 | Class>[] groups() default {};
42 |
43 | Class extends Payload>[] payload() default {};
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/MinThreadCountValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.ConstraintValidator;
17 | import jakarta.validation.ConstraintValidatorContext;
18 |
19 | public class MinThreadCountValidator
20 | implements ConstraintValidator
21 | {
22 | private ThreadCount min;
23 |
24 | @Override
25 | public void initialize(MinThreadCount annotation)
26 | {
27 | this.min = ThreadCount.valueOf(annotation.value());
28 | }
29 |
30 | @Override
31 | public boolean isValid(ThreadCount threadCount, ConstraintValidatorContext constraintValidatorContext)
32 | {
33 | return threadCount == null || threadCount.compareTo(min) >= 0;
34 | }
35 |
36 | @Override
37 | public String toString()
38 | {
39 | return "min: " + min;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/Preconditions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2007 The Guava Authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | // forked from com.google.common.base.Preconditions
19 | final class Preconditions
20 | {
21 | private Preconditions() {}
22 |
23 | static void checkArgument(boolean expression, String errorMessage)
24 | {
25 | if (!expression) {
26 | throw new IllegalArgumentException(errorMessage);
27 | }
28 | }
29 |
30 | static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs)
31 | {
32 | if (!expression) {
33 | throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs));
34 | }
35 | }
36 |
37 | static void checkState(boolean expression, String errorMessage)
38 | {
39 | if (!expression) {
40 | throw new IllegalStateException(errorMessage);
41 | }
42 | }
43 |
44 | static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs)
45 | {
46 | if (!expression) {
47 | throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs));
48 | }
49 | }
50 |
51 | private static String format(String template, Object... args)
52 | {
53 | template = String.valueOf(template); // null -> "null"
54 |
55 | // start substituting the arguments into the '%s' placeholders
56 | StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
57 | int templateStart = 0;
58 | int i = 0;
59 | while (i < args.length) {
60 | int placeholderStart = template.indexOf("%s", templateStart);
61 | if (placeholderStart == -1) {
62 | break;
63 | }
64 | builder.append(template.substring(templateStart, placeholderStart));
65 | builder.append(args[i++]);
66 | templateStart = placeholderStart + 2;
67 | }
68 | builder.append(template.substring(templateStart));
69 |
70 | // if we run out of placeholders, append the extra args in square braces
71 | if (i < args.length) {
72 | builder.append(" [");
73 | builder.append(args[i++]);
74 | while (i < args.length) {
75 | builder.append(", ");
76 | builder.append(args[i++]);
77 | }
78 | builder.append(']');
79 | }
80 |
81 | return builder.toString();
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/units/ThreadCount.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import java.io.IOException;
17 | import java.nio.file.Path;
18 | import java.nio.file.Paths;
19 | import java.util.OptionalInt;
20 | import java.util.function.Supplier;
21 | import java.util.stream.Stream;
22 |
23 | import static io.airlift.units.Preconditions.checkArgument;
24 | import static java.lang.Math.min;
25 | import static java.lang.Math.round;
26 | import static java.lang.Math.toIntExact;
27 | import static java.lang.String.format;
28 | import static java.nio.file.Files.exists;
29 | import static java.nio.file.Files.lines;
30 |
31 | public class ThreadCount
32 | implements Comparable
33 | {
34 | private static final String PER_CORE_SUFFIX = "C";
35 | private static final Supplier AVAILABLE_PROCESSORS = MachineInfo::getAvailablePhysicalProcessorCount;
36 | private final int threadCount;
37 |
38 | ThreadCount(int threadCount)
39 | {
40 | checkArgument(threadCount >= 0, "Thread count cannot be negative");
41 | this.threadCount = threadCount;
42 | }
43 |
44 | public int getThreadCount()
45 | {
46 | return threadCount;
47 | }
48 |
49 | public static ThreadCount exactValueOf(int value)
50 | {
51 | return new ThreadCount(value);
52 | }
53 |
54 | public static ThreadCount valueOf(String value)
55 | {
56 | if (value.endsWith(PER_CORE_SUFFIX)) {
57 | float parsedMultiplier = parseFloat(value.substring(0, value.lastIndexOf(PER_CORE_SUFFIX)).trim());
58 | checkArgument(parsedMultiplier > 0, "Thread multiplier cannot be negative");
59 | float threadCount = parsedMultiplier * AVAILABLE_PROCESSORS.get();
60 | checkArgument(threadCount <= Integer.MAX_VALUE, "Thread count is greater than 2^32 - 1");
61 | return new ThreadCount(round(threadCount));
62 | }
63 |
64 | return new ThreadCount(parseInteger(value));
65 | }
66 |
67 | public static ThreadCount boundedValueOf(String value, String minValue, String maxValue)
68 | {
69 | ThreadCount parsed = ThreadCount.valueOf(value);
70 | ThreadCount min = ThreadCount.valueOf(minValue);
71 | ThreadCount max = ThreadCount.valueOf(maxValue);
72 |
73 | if (parsed.compareTo(min) < 0) {
74 | return min;
75 | }
76 |
77 | if (parsed.compareTo(max) > 0) {
78 | return max;
79 | }
80 | return parsed;
81 | }
82 |
83 | private static float parseFloat(String value)
84 | {
85 | try {
86 | return Float.parseFloat(value);
87 | }
88 | catch (NumberFormatException e) {
89 | throw new IllegalArgumentException(format("Cannot parse value '%s' as float", value), e);
90 | }
91 | }
92 |
93 | private static int parseInteger(String value)
94 | {
95 | try {
96 | long parsed = Long.parseLong(value);
97 | checkArgument(parsed <= Integer.MAX_VALUE, "Thread count is greater than 2^32 - 1");
98 | return toIntExact(parsed);
99 | }
100 | catch (NumberFormatException e) {
101 | throw new IllegalArgumentException(format("Cannot parse value '%s' as integer", value), e);
102 | }
103 | }
104 |
105 | @Override
106 | public int compareTo(ThreadCount o)
107 | {
108 | return Integer.compare(threadCount, o.threadCount);
109 | }
110 |
111 | @Override
112 | public boolean equals(Object o)
113 | {
114 | if (this == o) {
115 | return true;
116 | }
117 | if (o == null || getClass() != o.getClass()) {
118 | return false;
119 | }
120 |
121 | ThreadCount that = (ThreadCount) o;
122 | return threadCount == that.threadCount;
123 | }
124 |
125 | @Override
126 | public int hashCode()
127 | {
128 | return threadCount;
129 | }
130 |
131 | @Override
132 | public String toString()
133 | {
134 | return (threadCount == 1) ? "1 thread" : (threadCount + " threads");
135 | }
136 |
137 | static final class MachineInfo
138 | {
139 | private static final Path CPU_INFO_PATH = Paths.get("/proc/cpuinfo");
140 |
141 | // cache physical processor count, so that it's not queried multiple times during tests
142 | private static volatile int physicalProcessorCount = -1;
143 |
144 | private MachineInfo() {}
145 |
146 | public static int getAvailablePhysicalProcessorCount()
147 | {
148 | if (physicalProcessorCount != -1) {
149 | return physicalProcessorCount;
150 | }
151 |
152 | String osArch = System.getProperty("os.arch");
153 | // logical core count (including container cpu quota if there is any)
154 | int availableProcessorCount = Runtime.getRuntime().availableProcessors();
155 | int totalPhysicalProcessorCount = availableProcessorCount;
156 | if ("amd64".equals(osArch) || "x86_64".equals(osArch)) {
157 | OptionalInt procInfo = tryReadFromProcCpuinfo();
158 | if (procInfo.isPresent()) {
159 | totalPhysicalProcessorCount = procInfo.getAsInt();
160 | }
161 | }
162 |
163 | // cap available processor count to container cpu quota (if there is any).
164 | physicalProcessorCount = min(totalPhysicalProcessorCount, availableProcessorCount);
165 | return physicalProcessorCount;
166 | }
167 |
168 | private static OptionalInt tryReadFromProcCpuinfo()
169 | {
170 | if (!exists(CPU_INFO_PATH)) {
171 | return OptionalInt.empty();
172 | }
173 |
174 | try (Stream lines = lines(CPU_INFO_PATH)) {
175 | return OptionalInt.of(toIntExact(lines.filter(line ->
176 | line.matches("^processor\\s+: \\d")).count()));
177 | }
178 | catch (IOException e) {
179 | return OptionalInt.empty();
180 | }
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | module org.airlift.units {
15 | requires com.fasterxml.jackson.annotation;
16 | requires jakarta.validation;
17 |
18 | exports io.airlift.units;
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/ValidationMessages.properties:
--------------------------------------------------------------------------------
1 | io.airlift.units.MinDuration.message=must be greater than or equal to {value}
2 | io.airlift.units.MaxDuration.message=must be less than or equal to {value}
3 | io.airlift.units.MinDataSize.message=must be greater than or equal to {value}
4 | io.airlift.units.MaxDataSize.message=must be less than or equal to {value}
5 | io.airlift.units.MinThreadCount.message=must be greater than or equal to {value}
6 | io.airlift.units.MaxThreadCount.message=must be less than or equal to {value}
7 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/ConstraintValidatorAssert.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.ClockProvider;
17 | import jakarta.validation.ConstraintValidator;
18 | import jakarta.validation.ConstraintValidatorContext;
19 | import org.assertj.core.api.AbstractAssert;
20 |
21 | import java.lang.annotation.Annotation;
22 |
23 | final class ConstraintValidatorAssert
24 | extends AbstractAssert, ConstraintValidator>
25 | {
26 | public ConstraintValidatorAssert(ConstraintValidator actual)
27 | {
28 | super(actual, ConstraintValidatorAssert.class);
29 | }
30 |
31 | public static ConstraintValidatorAssert assertThat(ConstraintValidator actual)
32 | {
33 | return new ConstraintValidatorAssert<>(actual);
34 | }
35 |
36 | public void isValidFor(T value)
37 | {
38 | isNotNull();
39 | if (!actual.isValid(value, new MockContext())) {
40 | failWithMessage("Expected <%s> to be valid for <%s>", actual, value);
41 | }
42 | }
43 |
44 | public void isInvalidFor(T value)
45 | {
46 | isNotNull();
47 | if (actual.isValid(value, new MockContext())) {
48 | failWithMessage("Expected <%s> to be invalid for <%s>", actual, value);
49 | }
50 | }
51 |
52 | private static class MockContext
53 | implements ConstraintValidatorContext
54 | {
55 | @Override
56 | public void disableDefaultConstraintViolation()
57 | {
58 | throw new UnsupportedOperationException();
59 | }
60 |
61 | @Override
62 | public String getDefaultConstraintMessageTemplate()
63 | {
64 | throw new UnsupportedOperationException();
65 | }
66 |
67 | @Override
68 | public ClockProvider getClockProvider()
69 | {
70 | throw new UnsupportedOperationException();
71 | }
72 |
73 | @Override
74 | public ConstraintViolationBuilder buildConstraintViolationWithTemplate(String s)
75 | {
76 | throw new UnsupportedOperationException();
77 | }
78 |
79 | @Override
80 | public T unwrap(Class type)
81 | {
82 | throw new UnsupportedOperationException();
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/MockMaxDataSize.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.Payload;
17 |
18 | import java.lang.annotation.Annotation;
19 |
20 | import static java.util.Objects.requireNonNull;
21 |
22 | @SuppressWarnings("ClassExplicitlyAnnotation")
23 | class MockMaxDataSize
24 | implements MaxDataSize
25 | {
26 | private final DataSize dataSize;
27 |
28 | public MockMaxDataSize(DataSize dataSize)
29 | {
30 | this.dataSize = requireNonNull(dataSize, "dataSize is null");
31 | }
32 |
33 | @Override
34 | public String value()
35 | {
36 | return dataSize.toString();
37 | }
38 |
39 | @Override
40 | public String message()
41 | {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Override
46 | public Class>[] groups()
47 | {
48 | throw new UnsupportedOperationException();
49 | }
50 |
51 | @Override
52 | public Class extends Payload>[] payload()
53 | {
54 | throw new UnsupportedOperationException();
55 | }
56 |
57 | @Override
58 | public Class extends Annotation> annotationType()
59 | {
60 | return MaxDuration.class;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/MockMaxDuration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import jakarta.validation.Payload;
19 |
20 | import java.lang.annotation.Annotation;
21 |
22 | @SuppressWarnings("ClassExplicitlyAnnotation")
23 | class MockMaxDuration
24 | implements MaxDuration
25 | {
26 | private final Duration duration;
27 |
28 | public MockMaxDuration(Duration duration)
29 | {
30 | this.duration = duration;
31 | }
32 |
33 | @Override
34 | public String value()
35 | {
36 | return duration.toString();
37 | }
38 |
39 | @Override
40 | public String message()
41 | {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Override
46 | public Class>[] groups()
47 | {
48 | throw new UnsupportedOperationException();
49 | }
50 |
51 | @Override
52 | public Class extends Payload>[] payload()
53 | {
54 | throw new UnsupportedOperationException();
55 | }
56 |
57 | @Override
58 | public Class extends Annotation> annotationType()
59 | {
60 | return MaxDuration.class;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/MockMaxThreadCount.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.Payload;
17 |
18 | import java.lang.annotation.Annotation;
19 |
20 | import static java.util.Objects.requireNonNull;
21 |
22 | @SuppressWarnings("ClassExplicitlyAnnotation")
23 | class MockMaxThreadCount
24 | implements MaxThreadCount
25 | {
26 | private final ThreadCount threadCount;
27 |
28 | public MockMaxThreadCount(ThreadCount threadCount)
29 | {
30 | this.threadCount = requireNonNull(threadCount, "threadCount is null");
31 | }
32 |
33 | @Override
34 | public String value()
35 | {
36 | return String.valueOf(threadCount.getThreadCount());
37 | }
38 |
39 | @Override
40 | public String message()
41 | {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Override
46 | public Class>[] groups()
47 | {
48 | throw new UnsupportedOperationException();
49 | }
50 |
51 | @Override
52 | public Class extends Payload>[] payload()
53 | {
54 | throw new UnsupportedOperationException();
55 | }
56 |
57 | @Override
58 | public Class extends Annotation> annotationType()
59 | {
60 | return MaxThreadCount.class;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/MockMinDataSize.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.Payload;
17 |
18 | import java.lang.annotation.Annotation;
19 |
20 | import static java.util.Objects.requireNonNull;
21 |
22 | @SuppressWarnings("ClassExplicitlyAnnotation")
23 | class MockMinDataSize
24 | implements MinDataSize
25 | {
26 | private final DataSize dataSize;
27 |
28 | public MockMinDataSize(DataSize dataSize)
29 | {
30 | this.dataSize = requireNonNull(dataSize, "dataSize is null");
31 | }
32 |
33 | @Override
34 | public String value()
35 | {
36 | return dataSize.toString();
37 | }
38 |
39 | @Override
40 | public String message()
41 | {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Override
46 | public Class>[] groups()
47 | {
48 | throw new UnsupportedOperationException();
49 | }
50 |
51 | @Override
52 | public Class extends Payload>[] payload()
53 | {
54 | throw new UnsupportedOperationException();
55 | }
56 |
57 | @Override
58 | public Class extends Annotation> annotationType()
59 | {
60 | return MinDataSize.class;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/MockMinDuration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import jakarta.validation.Payload;
19 |
20 | import java.lang.annotation.Annotation;
21 |
22 | @SuppressWarnings("ClassExplicitlyAnnotation")
23 | class MockMinDuration
24 | implements MinDuration
25 | {
26 | private final Duration duration;
27 |
28 | public MockMinDuration(Duration duration)
29 | {
30 | this.duration = duration;
31 | }
32 |
33 | @Override
34 | public String value()
35 | {
36 | return duration.toString();
37 | }
38 |
39 | @Override
40 | public String message()
41 | {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Override
46 | public Class>[] groups()
47 | {
48 | throw new UnsupportedOperationException();
49 | }
50 |
51 | @Override
52 | public Class extends Payload>[] payload()
53 | {
54 | throw new UnsupportedOperationException();
55 | }
56 |
57 | @Override
58 | public Class extends Annotation> annotationType()
59 | {
60 | return MinDuration.class;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/MockMinThreadCount.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.Payload;
17 |
18 | import java.lang.annotation.Annotation;
19 |
20 | import static java.util.Objects.requireNonNull;
21 |
22 | @SuppressWarnings("ClassExplicitlyAnnotation")
23 | class MockMinThreadCount
24 | implements MinThreadCount
25 | {
26 | private final ThreadCount threadCount;
27 |
28 | public MockMinThreadCount(ThreadCount threadCount)
29 | {
30 | this.threadCount = requireNonNull(threadCount, "threadCount is null");
31 | }
32 |
33 | @Override
34 | public String value()
35 | {
36 | return String.valueOf(threadCount.getThreadCount());
37 | }
38 |
39 | @Override
40 | public String message()
41 | {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Override
46 | public Class>[] groups()
47 | {
48 | throw new UnsupportedOperationException();
49 | }
50 |
51 | @Override
52 | public Class extends Payload>[] payload()
53 | {
54 | throw new UnsupportedOperationException();
55 | }
56 |
57 | @Override
58 | public Class extends Annotation> annotationType()
59 | {
60 | return MinThreadCount.class;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/TestDataSize.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import com.google.common.collect.ImmutableList;
19 | import io.airlift.json.JsonCodec;
20 | import org.testng.annotations.DataProvider;
21 | import org.testng.annotations.Test;
22 |
23 | import java.util.Locale;
24 |
25 | import static io.airlift.testing.EquivalenceTester.comparisonTester;
26 | import static io.airlift.units.DataSize.Unit.BYTE;
27 | import static io.airlift.units.DataSize.Unit.GIGABYTE;
28 | import static io.airlift.units.DataSize.Unit.KILOBYTE;
29 | import static io.airlift.units.DataSize.Unit.MEGABYTE;
30 | import static io.airlift.units.DataSize.Unit.PETABYTE;
31 | import static io.airlift.units.DataSize.Unit.TERABYTE;
32 | import static io.airlift.units.DataSize.succinctBytes;
33 | import static io.airlift.units.DataSize.succinctDataSize;
34 | import static org.assertj.core.api.Assertions.assertThat;
35 | import static org.assertj.core.api.Assertions.within;
36 | import static org.testng.Assert.assertEquals;
37 |
38 | public class TestDataSize
39 | {
40 | @Test
41 | public void testSuccinctFactories()
42 | {
43 | assertEquals(succinctBytes(123), DataSize.ofBytes(123));
44 | assertEquals(succinctBytes(123).getUnit(), BYTE);
45 |
46 | DataSize fiveMiBi = DataSize.ofBytes(5 * 1024 * 1024);
47 | assertEquals(succinctBytes(fiveMiBi.toBytes()), fiveMiBi);
48 | assertEquals(succinctBytes(fiveMiBi.toBytes()).getUnit(), MEGABYTE);
49 | }
50 |
51 | @Test
52 | public void testDeprecatedSuccinctFactories()
53 | {
54 | assertEquals(succinctDataSize(123, BYTE), DataSize.ofBytes(123));
55 | assertEquals(succinctDataSize(123, BYTE).getUnit(), BYTE);
56 |
57 | assertEquals(succinctDataSize((long) (5.5 * 1024), BYTE), new DataSize(5.5, KILOBYTE));
58 | assertEquals(succinctDataSize((long) (5.5 * 1024), BYTE).getUnit(), KILOBYTE);
59 |
60 | assertEquals(succinctDataSize(5 * 1024, KILOBYTE), DataSize.of(5, MEGABYTE));
61 | assertEquals(succinctDataSize(5 * 1024, KILOBYTE).getUnit(), MEGABYTE);
62 | }
63 |
64 | @Test
65 | public void testToBytesValueString()
66 | {
67 | DataSize oneByte = DataSize.ofBytes(1);
68 | assertEquals(oneByte.toString(), oneByte.toBytesValueString(), "exact values match toString()");
69 | assertEquals(oneByte.toString(), "1B");
70 |
71 | for (DataSize.Unit unit : DataSize.Unit.values()) {
72 | DataSize oneInUnit = DataSize.of(1, unit);
73 | assertEquals(oneInUnit.toBytesValueString(), unit.inBytes() + "B");
74 | assertEquals(DataSize.valueOf(oneInUnit.toBytesValueString()), oneInUnit);
75 | }
76 |
77 | assertEquals(DataSize.of(1, KILOBYTE).toBytesValueString(), "1024B");
78 | assertEquals(DataSize.of(2, MEGABYTE).toBytesValueString(), "2097152B");
79 | assertEquals(DataSize.of(3, GIGABYTE).toBytesValueString(), "3221225472B");
80 | assertEquals(DataSize.of(4, TERABYTE).toBytesValueString(), "4398046511104B");
81 | assertEquals(DataSize.of(5, PETABYTE).toBytesValueString(), "5629499534213120B");
82 | }
83 |
84 | @Test(dataProvider = "conversions")
85 | public void testConversions(DataSize.Unit unit, DataSize.Unit toUnit, double factor)
86 | {
87 | DataSize size = DataSize.of(1, unit).to(toUnit);
88 | assertEquals(size.getUnit(), toUnit);
89 | assertEquals(size.getValue(), factor);
90 |
91 | assertEquals(size.getValue(toUnit), factor);
92 | }
93 |
94 | @Test(dataProvider = "conversions")
95 | public void testConvertToMostSuccinctDataSize(DataSize.Unit unit, DataSize.Unit toUnit, double factor)
96 | {
97 | DataSize size = new DataSize(factor, toUnit);
98 | DataSize actual = size.succinct();
99 | assertThat(actual).isEqualTo(DataSize.of(1, unit));
100 | assertThat(actual.getValue(unit)).isCloseTo(1.0, within(0.001));
101 | assertThat(actual.getUnit()).isEqualTo(unit);
102 | }
103 |
104 | @Test
105 | public void testValueEquivalence()
106 | {
107 | comparisonTester()
108 | .addLesserGroup(group(0))
109 | .addGreaterGroup(group(1))
110 | .addGreaterGroup(group(123352))
111 | .addGreaterGroup(group(Long.MAX_VALUE))
112 | .check();
113 | }
114 |
115 | private static Iterable group(long bytes)
116 | {
117 | return ImmutableList.of(
118 | DataSize.ofBytes(bytes),
119 | DataSize.ofBytes(bytes).convertTo(KILOBYTE),
120 | DataSize.ofBytes(bytes).convertTo(MEGABYTE),
121 | DataSize.ofBytes(bytes).convertTo(GIGABYTE),
122 | DataSize.ofBytes(bytes).convertTo(TERABYTE),
123 | DataSize.ofBytes(bytes).convertTo(PETABYTE));
124 | }
125 |
126 | @Test
127 | public void testDeprecatedDoubleValueEquivalence()
128 | {
129 | comparisonTester()
130 | .addLesserGroup(deprecatedDoubleValueGroup(0))
131 | .addGreaterGroup(deprecatedDoubleValueGroup(1))
132 | .addGreaterGroup(deprecatedDoubleValueGroup(123352))
133 | .addGreaterGroup(deprecatedDoubleValueGroup(Long.MAX_VALUE))
134 | .check();
135 | }
136 |
137 | private static Iterable deprecatedDoubleValueGroup(double bytes)
138 | {
139 | return ImmutableList.of(
140 | new DataSize(bytes, BYTE),
141 | new DataSize(bytes / 1024, KILOBYTE),
142 | new DataSize(bytes / 1024 / 1024, MEGABYTE),
143 | new DataSize(bytes / 1024 / 1024 / 1024, GIGABYTE),
144 | new DataSize(bytes / 1024 / 1024 / 1024 / 1024, TERABYTE),
145 | new DataSize(bytes / 1024 / 1024 / 1024 / 1024 / 1024, PETABYTE));
146 | }
147 |
148 | @Test(dataProvider = "printedValues")
149 | public void testToString(String expectedString, double value, DataSize.Unit unit)
150 | {
151 | assertEquals(new DataSize(value, unit).toString(), expectedString);
152 | }
153 |
154 | @Test(dataProvider = "printedValues")
155 | public void testNonEnglishLocale(String expectedString, double value, DataSize.Unit unit)
156 | {
157 | synchronized (Locale.class) {
158 | Locale previous = Locale.getDefault();
159 | Locale.setDefault(Locale.GERMAN);
160 | try {
161 | assertEquals(new DataSize(value, unit).toString(), expectedString);
162 | }
163 | finally {
164 | Locale.setDefault(previous);
165 | }
166 | }
167 | }
168 |
169 | @Test(dataProvider = "parseableValues")
170 | public void testValueOf(String string, double expectedValue, DataSize.Unit expectedUnit)
171 | {
172 | DataSize size = DataSize.valueOf(string);
173 |
174 | assertEquals(size.getUnit(), expectedUnit);
175 | assertThat(size.getValue()).isCloseTo(expectedValue, within(0.001));
176 | }
177 |
178 | @Test
179 | public void testValueOfDecimalAndLongHandling()
180 | {
181 | // If parsed as double, this results in only 2^53 not (2^53) + 1
182 | DataSize tooLargeAsDouble = DataSize.ofBytes((1L << 53) + 1);
183 | assertEquals(DataSize.valueOf(Long.toString(tooLargeAsDouble.toBytes()) + "B"), tooLargeAsDouble, "should parse as long and not double");
184 | assertEquals(DataSize.valueOf(Long.toString(tooLargeAsDouble.toBytes()) + ".0B"), DataSize.ofBytes(1L << 53), "should parse as double");
185 | }
186 |
187 | @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "size is null")
188 | public void testValueOfRejectsNull()
189 | {
190 | DataSize.valueOf(null);
191 | }
192 |
193 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is empty")
194 | public void testValueOfRejectsEmptyString()
195 | {
196 | DataSize.valueOf("");
197 | }
198 |
199 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Unknown unit: kg")
200 | public void testValueOfRejectsInvalidUnit()
201 | {
202 | DataSize.valueOf("1.234 kg");
203 | }
204 |
205 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is not a valid.*")
206 | public void testValueOfRejectsInvalidNumber()
207 | {
208 | DataSize.valueOf("1.2x4 B");
209 | }
210 |
211 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
212 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is negative: -1.0")
213 | public void testDeprecatedConstructorRejectsNegativeSize()
214 | {
215 | new DataSize(-1.0, BYTE);
216 | }
217 |
218 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
219 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is infinite")
220 | public void testDeprecatedConstructorRejectsInfiniteSize()
221 | {
222 | new DataSize(Double.POSITIVE_INFINITY, BYTE);
223 | }
224 |
225 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
226 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is infinite")
227 | public void testDeprecatedConstructorRejectsInfiniteSize2()
228 | {
229 | new DataSize(Double.NEGATIVE_INFINITY, BYTE);
230 | }
231 |
232 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
233 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is not a number")
234 | public void testDeprecatedConstructorRejectsNaN()
235 | {
236 | new DataSize(Double.NaN, BYTE);
237 | }
238 |
239 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
240 | @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "unit is null")
241 | public void testConstructorRejectsNullUnit()
242 | {
243 | new DataSize(1, null);
244 | }
245 |
246 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is negative: -11")
247 | public void testOfRejectsNegativeSize()
248 | {
249 | DataSize.of(-11, MEGABYTE);
250 | }
251 |
252 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is too large to be represented in bytes: 9223372036854775807MB")
253 | public void testOfDetectsOverflow()
254 | {
255 | DataSize.of(Long.MAX_VALUE, MEGABYTE);
256 | }
257 |
258 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "bytes is negative")
259 | public void testOfBytesRejectsNegativeSize()
260 | {
261 | DataSize.ofBytes(-1);
262 | }
263 |
264 | @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "unit is null")
265 | public void testOfRejectsNullUnit()
266 | {
267 | DataSize.of(1, null);
268 | }
269 |
270 | @Test
271 | public void testToBytes()
272 | {
273 | assertEquals(DataSize.of(0, BYTE).toBytes(), 0);
274 | assertEquals(DataSize.of(0, MEGABYTE).toBytes(), 0);
275 | assertEquals(DataSize.of(1, BYTE).toBytes(), 1);
276 | assertEquals(DataSize.of(1, KILOBYTE).toBytes(), 1024);
277 | assertEquals(DataSize.of(42, MEGABYTE).toBytes(), 42L * 1024 * 1024);
278 | assertEquals(DataSize.of(1, TERABYTE).toBytes(), 1024L * 1024 * 1024 * 1024);
279 | assertEquals(DataSize.of(1, PETABYTE).toBytes(), 1024L * 1024 * 1024 * 1024 * 1024);
280 | assertEquals(DataSize.of(1024, PETABYTE).toBytes(), 1024L * 1024 * 1024 * 1024 * 1024 * 1024);
281 | assertEquals(DataSize.of(8191, PETABYTE).toBytes(), 8191L * 1024 * 1024 * 1024 * 1024 * 1024);
282 | assertEquals(DataSize.of(Long.MAX_VALUE, BYTE).toBytes(), Long.MAX_VALUE);
283 | }
284 |
285 | @Test
286 | public void testDeprecatedToBytes()
287 | {
288 | assertEquals(new DataSize(37.0 / 1024, KILOBYTE).toBytes(), 37);
289 | assertEquals(new DataSize(Long.MAX_VALUE / 1024.0, KILOBYTE).toBytes(), Long.MAX_VALUE);
290 | }
291 |
292 | @Test
293 | public void testRoundTo()
294 | {
295 | assertEquals(DataSize.ofBytes(0).roundTo(BYTE), 0);
296 | assertEquals(new DataSize(0.5, BYTE).roundTo(BYTE), 1);
297 | assertEquals(new DataSize((42 * 1024) + 511, BYTE).roundTo(KILOBYTE), 42);
298 | assertEquals(new DataSize((42 * 1024) + 512, BYTE).roundTo(KILOBYTE), 43);
299 | assertEquals(new DataSize(513, TERABYTE).roundTo(PETABYTE), 1);
300 | }
301 |
302 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is too large .*")
303 | public void testSizeTooLarge()
304 | {
305 | new DataSize(Long.MAX_VALUE + 1024.0001, BYTE);
306 | }
307 |
308 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is too large .*")
309 | public void testSizeTooLargeInStaticOfMethod()
310 | {
311 | DataSize.of(9000, PETABYTE);
312 | }
313 |
314 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "size is too large .*")
315 | public void testRoundToTooLarge3()
316 | {
317 | new DataSize(9000 * 1024, PETABYTE).roundTo(KILOBYTE);
318 | }
319 |
320 | @Test
321 | public void testJsonRoundTrip()
322 | {
323 | assertJsonRoundTrip(DataSize.ofBytes(1L));
324 | assertJsonRoundTrip(new DataSize(1.234, KILOBYTE));
325 | assertJsonRoundTrip(new DataSize(1.234, MEGABYTE));
326 | assertJsonRoundTrip(new DataSize(1.234, GIGABYTE));
327 | assertJsonRoundTrip(new DataSize(1.234, TERABYTE));
328 | assertJsonRoundTrip(new DataSize(1.234, PETABYTE));
329 |
330 | assertJsonRoundTrip(DataSize.ofBytes(Long.MAX_VALUE));
331 |
332 | // Arbitrary assortment of values other than 1, primes and multiples of 2 and 10
333 | long[] sizes = new long[] {1, 2, 3, 5, 7, 8, 11, 16, 100};
334 |
335 | for (DataSize.Unit unit : DataSize.Unit.values()) {
336 | for (long size : sizes) {
337 | assertJsonRoundTrip(DataSize.ofBytes(size).convertTo(unit));
338 | assertJsonRoundTrip(DataSize.of(size, unit));
339 | }
340 | }
341 | }
342 |
343 | private static void assertJsonRoundTrip(DataSize dataSize)
344 | {
345 | JsonCodec dataSizeCodec = JsonCodec.jsonCodec(DataSize.class);
346 | String json = dataSizeCodec.toJson(dataSize);
347 | DataSize dataSizeCopy = dataSizeCodec.fromJson(json);
348 |
349 | assertThat(dataSizeCopy.getValue(BYTE))
350 | .isCloseTo(dataSize.getValue(BYTE), within(0.001));
351 |
352 | assertEquals(dataSizeCopy.getUnit(), BYTE, "JSON serialization should always be in bytes");
353 | assertEquals(dataSize.toBytes(), dataSizeCopy.toBytes(), "byte value equivalence");
354 | assertEquals(dataSize, dataSizeCopy, "equals method equivalence");
355 | }
356 |
357 | @DataProvider(name = "parseableValues", parallel = true)
358 | private Object[][] parseableValues()
359 | {
360 | return new Object[][] {
361 | // spaces
362 | new Object[] {"1234 B", 1234, BYTE},
363 | new Object[] {"1234 kB", 1234, KILOBYTE},
364 | new Object[] {"1234 MB", 1234, MEGABYTE},
365 | new Object[] {"1234 GB", 1234, GIGABYTE},
366 | new Object[] {"1234 TB", 1234, TERABYTE},
367 | new Object[] {"1234 PB", 1234, PETABYTE},
368 | new Object[] {"1234.567 kB", 1234.567, KILOBYTE},
369 | new Object[] {"1234.567 MB", 1234.567, MEGABYTE},
370 | new Object[] {"1234.567 GB", 1234.567, GIGABYTE},
371 | new Object[] {"1234.567 TB", 1234.567, TERABYTE},
372 | new Object[] {"1234.567 PB", 1234.567, PETABYTE},
373 | // no spaces
374 | new Object[] {"1234B", 1234, BYTE},
375 | new Object[] {"1234kB", 1234, KILOBYTE},
376 | new Object[] {"1234MB", 1234, MEGABYTE},
377 | new Object[] {"1234GB", 1234, GIGABYTE},
378 | new Object[] {"1234TB", 1234, TERABYTE},
379 | new Object[] {"1234PB", 1234, PETABYTE},
380 | new Object[] {"1234.567kB", 1234.567, KILOBYTE},
381 | new Object[] {"1234.567MB", 1234.567, MEGABYTE},
382 | new Object[] {"1234.567GB", 1234.567, GIGABYTE},
383 | new Object[] {"1234.567TB", 1234.567, TERABYTE},
384 | new Object[] {"1234.567PB", 1234.567, PETABYTE}
385 | };
386 | }
387 |
388 | @DataProvider(name = "printedValues", parallel = true)
389 | private Object[][] printedValues()
390 | {
391 | return new Object[][] {
392 | new Object[] {"1234B", 1234, BYTE},
393 | new Object[] {"1234kB", 1234, KILOBYTE},
394 | new Object[] {"1234MB", 1234, MEGABYTE},
395 | new Object[] {"1234GB", 1234, GIGABYTE},
396 | new Object[] {"1234TB", 1234, TERABYTE},
397 | new Object[] {"1234PB", 1234, PETABYTE},
398 | new Object[] {"1234.57kB", 1234.567, KILOBYTE},
399 | new Object[] {"1234.57MB", 1234.567, MEGABYTE},
400 | new Object[] {"1234.57GB", 1234.567, GIGABYTE},
401 | new Object[] {"1234.57TB", 1234.567, TERABYTE},
402 | new Object[] {"1234.57PB", 1234.567, PETABYTE}
403 | };
404 | }
405 |
406 | @DataProvider(name = "conversions", parallel = true)
407 | private Object[][] conversions()
408 | {
409 | return new Object[][] {
410 | new Object[] {BYTE, BYTE, 1},
411 | new Object[] {BYTE, KILOBYTE, 1.0 / 1024},
412 | new Object[] {BYTE, MEGABYTE, 1.0 / 1024 / 1024},
413 | new Object[] {BYTE, GIGABYTE, 1.0 / 1024 / 1024 / 1024},
414 | new Object[] {BYTE, TERABYTE, 1.0 / 1024 / 1024 / 1024 / 1024},
415 | new Object[] {BYTE, PETABYTE, 1.0 / 1024 / 1024 / 1024 / 1024 / 1024},
416 |
417 | new Object[] {KILOBYTE, BYTE, 1024},
418 | new Object[] {KILOBYTE, KILOBYTE, 1},
419 | new Object[] {KILOBYTE, MEGABYTE, 1.0 / 1024},
420 | new Object[] {KILOBYTE, GIGABYTE, 1.0 / 1024 / 1024},
421 | new Object[] {KILOBYTE, TERABYTE, 1.0 / 1024 / 1024 / 1024},
422 | new Object[] {KILOBYTE, PETABYTE, 1.0 / 1024 / 1024 / 1024 / 1024},
423 |
424 | new Object[] {MEGABYTE, BYTE, 1024 * 1024},
425 | new Object[] {MEGABYTE, KILOBYTE, 1024},
426 | new Object[] {MEGABYTE, MEGABYTE, 1},
427 | new Object[] {MEGABYTE, GIGABYTE, 1.0 / 1024},
428 | new Object[] {MEGABYTE, TERABYTE, 1.0 / 1024 / 1024},
429 | new Object[] {MEGABYTE, PETABYTE, 1.0 / 1024 / 1024 / 1024},
430 |
431 | new Object[] {GIGABYTE, BYTE, 1024 * 1024 * 1024},
432 | new Object[] {GIGABYTE, KILOBYTE, 1024 * 1024},
433 | new Object[] {GIGABYTE, MEGABYTE, 1024},
434 | new Object[] {GIGABYTE, GIGABYTE, 1},
435 | new Object[] {GIGABYTE, TERABYTE, 1.0 / 1024},
436 | new Object[] {GIGABYTE, PETABYTE, 1.0 / 1024 / 1024},
437 |
438 | new Object[] {TERABYTE, BYTE, 1024L * 1024 * 1024 * 1024},
439 | new Object[] {TERABYTE, KILOBYTE, 1024 * 1024 * 1024},
440 | new Object[] {TERABYTE, MEGABYTE, 1024 * 1024},
441 | new Object[] {TERABYTE, GIGABYTE, 1024},
442 | new Object[] {TERABYTE, TERABYTE, 1},
443 | new Object[] {TERABYTE, PETABYTE, 1.0 / 1024},
444 |
445 | new Object[] {PETABYTE, BYTE, 1024L * 1024 * 1024 * 1024 * 1024},
446 | new Object[] {PETABYTE, KILOBYTE, 1024L * 1024 * 1024 * 1024},
447 | new Object[] {PETABYTE, MEGABYTE, 1024 * 1024 * 1024},
448 | new Object[] {PETABYTE, GIGABYTE, 1024 * 1024},
449 | new Object[] {PETABYTE, TERABYTE, 1024},
450 | new Object[] {PETABYTE, PETABYTE, 1},
451 | };
452 | }
453 | }
454 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/TestDataSizeValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.ValidationException;
17 | import org.testng.annotations.Test;
18 |
19 | import java.util.Optional;
20 |
21 | import static io.airlift.testing.ValidationAssertions.assertFailsValidation;
22 | import static io.airlift.testing.ValidationAssertions.assertValidates;
23 | import static io.airlift.units.ConstraintValidatorAssert.assertThat;
24 | import static io.airlift.units.DataSize.Unit.GIGABYTE;
25 | import static io.airlift.units.DataSize.Unit.KILOBYTE;
26 | import static io.airlift.units.DataSize.Unit.MEGABYTE;
27 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
28 |
29 | public class TestDataSizeValidator
30 | {
31 | @Test
32 | public void testMaxDataSizeValidator()
33 | {
34 | MaxDataSizeValidator maxValidator = new MaxDataSizeValidator();
35 | maxValidator.initialize(new MockMaxDataSize(new DataSize(8, MEGABYTE)));
36 |
37 | assertThat(maxValidator).isValidFor(new DataSize(0, KILOBYTE));
38 | assertThat(maxValidator).isValidFor(new DataSize(5, KILOBYTE));
39 | assertThat(maxValidator).isValidFor(new DataSize(5005, KILOBYTE));
40 | assertThat(maxValidator).isValidFor(new DataSize(5, MEGABYTE));
41 | assertThat(maxValidator).isValidFor(new DataSize(8, MEGABYTE));
42 | assertThat(maxValidator).isValidFor(new DataSize(8192, KILOBYTE));
43 | assertThat(maxValidator).isInvalidFor(new DataSize(9, MEGABYTE));
44 | assertThat(maxValidator).isInvalidFor(new DataSize(1, GIGABYTE));
45 | }
46 |
47 | @Test
48 | public void testMinDataSizeValidator()
49 | {
50 | MinDataSizeValidator minValidator = new MinDataSizeValidator();
51 | minValidator.initialize(new MockMinDataSize(new DataSize(4, MEGABYTE)));
52 |
53 | assertThat(minValidator).isValidFor(new DataSize(4, MEGABYTE));
54 | assertThat(minValidator).isValidFor(new DataSize(4096, KILOBYTE));
55 | assertThat(minValidator).isValidFor(new DataSize(5, MEGABYTE));
56 | assertThat(minValidator).isInvalidFor(new DataSize(0, GIGABYTE));
57 | assertThat(minValidator).isInvalidFor(new DataSize(1, MEGABYTE));
58 | }
59 |
60 | @Test
61 | public void testAllowsNullMinAnnotation()
62 | {
63 | assertValidates(new NullMinAnnotation());
64 | }
65 |
66 | @Test
67 | public void testAllowsNullMaxAnnotation()
68 | {
69 | assertValidates(new NullMaxAnnotation());
70 | }
71 |
72 | @Test
73 | public void testDetectsBrokenMinAnnotation()
74 | {
75 | assertThatThrownBy(() -> assertValidates(new BrokenMinAnnotation()))
76 | .isInstanceOf(ValidationException.class)
77 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MinDataSizeValidator.")
78 | .hasRootCauseInstanceOf(IllegalArgumentException.class)
79 | .hasRootCauseMessage("size is not a valid data size string: broken");
80 |
81 | assertThatThrownBy(() -> assertValidates(new MinAnnotationOnOptional()))
82 | .isInstanceOf(ValidationException.class)
83 | .hasMessageContaining("No validator could be found for constraint 'io.airlift.units.MinDataSize' validating type 'java.util.Optional'. Check configuration for 'constrainedByMin'");
84 |
85 | assertThatThrownBy(() -> assertValidates(new BrokenOptionalMinAnnotation()))
86 | .isInstanceOf(ValidationException.class)
87 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MinDataSizeValidator.")
88 | .hasRootCauseInstanceOf(IllegalArgumentException.class)
89 | .hasRootCauseMessage("size is not a valid data size string: broken");
90 | }
91 |
92 | @Test
93 | public void testDetectsBrokenMaxAnnotation()
94 | {
95 | assertThatThrownBy(() -> assertValidates(new BrokenMaxAnnotation()))
96 | .isInstanceOf(ValidationException.class)
97 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MaxDataSizeValidator.")
98 | .hasRootCauseInstanceOf(IllegalArgumentException.class)
99 | .hasRootCauseMessage("size is not a valid data size string: broken");
100 |
101 | assertThatThrownBy(() -> assertValidates(new MaxAnnotationOnOptional()))
102 | .isInstanceOf(ValidationException.class)
103 | .hasMessageContaining("No validator could be found for constraint 'io.airlift.units.MaxDataSize' validating type 'java.util.Optional'. Check configuration for 'constrainedByMin'");
104 |
105 | assertThatThrownBy(() -> assertValidates(new BrokenOptionalMaxAnnotation()))
106 | .isInstanceOf(ValidationException.class)
107 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MaxDataSizeValidator.")
108 | .hasRootCauseInstanceOf(IllegalArgumentException.class)
109 | .hasRootCauseMessage("size is not a valid data size string: broken");
110 | }
111 |
112 | @Test
113 | public void testPassesValidation()
114 | {
115 | assertValidates(new ConstrainedDataSize(new DataSize(7, MEGABYTE)));
116 | assertValidates(new ConstrainedOptionalDataSize(Optional.of(new DataSize(7, MEGABYTE))));
117 | assertValidates(new ConstrainedOptionalDataSize(Optional.empty()));
118 | assertValidates(new ConstrainedOptionalDataSize(null));
119 | }
120 |
121 | @Test
122 | public void testFailsMaxDataSizeConstraint()
123 | {
124 | assertFailsValidation(new ConstrainedDataSize(new DataSize(11, MEGABYTE)), "constrainedByMinAndMax", "must be less than or equal to 10000kB", MaxDataSize.class);
125 | assertFailsValidation(new ConstrainedDataSize(new DataSize(11, MEGABYTE)), "constrainedByMax", "must be less than or equal to 10MB", MaxDataSize.class);
126 |
127 | assertFailsValidation(new ConstrainedOptionalDataSize(Optional.of(new DataSize(11, MEGABYTE))), "constrainedByMinAndMax", "must be less than or equal to 10000kB", MaxDataSize.class);
128 | assertFailsValidation(new ConstrainedOptionalDataSize(Optional.of(new DataSize(11, MEGABYTE))), "constrainedByMax", "must be less than or equal to 10MB", MaxDataSize.class);
129 | }
130 |
131 | @Test
132 | public void testFailsMinDataSizeConstraint()
133 | {
134 | assertFailsValidation(new ConstrainedDataSize(new DataSize(1, MEGABYTE)), "constrainedByMin", "must be greater than or equal to 5MB", MinDataSize.class);
135 | assertFailsValidation(new ConstrainedDataSize(new DataSize(1, MEGABYTE)), "constrainedByMinAndMax", "must be greater than or equal to 5000kB", MinDataSize.class);
136 |
137 | assertFailsValidation(new ConstrainedOptionalDataSize(Optional.of(new DataSize(1, MEGABYTE))), "constrainedByMin", "must be greater than or equal to 5MB", MinDataSize.class);
138 | assertFailsValidation(new ConstrainedOptionalDataSize(Optional.of(new DataSize(1, MEGABYTE))), "constrainedByMinAndMax", "must be greater than or equal to 5000kB", MinDataSize.class);
139 | }
140 |
141 | @SuppressWarnings("UnusedDeclaration")
142 | public static class ConstrainedDataSize
143 | {
144 | private final DataSize dataSize;
145 |
146 | public ConstrainedDataSize(DataSize dataSize)
147 | {
148 | this.dataSize = dataSize;
149 | }
150 |
151 | @MinDataSize("5MB")
152 | public DataSize getConstrainedByMin()
153 | {
154 | return dataSize;
155 | }
156 |
157 | @MaxDataSize("10MB")
158 | public DataSize getConstrainedByMax()
159 | {
160 | return dataSize;
161 | }
162 |
163 | @MinDataSize("5000kB")
164 | @MaxDataSize("10000kB")
165 | public DataSize getConstrainedByMinAndMax()
166 | {
167 | return dataSize;
168 | }
169 | }
170 |
171 | @SuppressWarnings("UnusedDeclaration")
172 | public static class ConstrainedOptionalDataSize
173 | {
174 | private final Optional dataSize;
175 |
176 | public ConstrainedOptionalDataSize(Optional dataSize)
177 | {
178 | this.dataSize = dataSize;
179 | }
180 |
181 | public Optional<@MinDataSize("5MB") DataSize> getConstrainedByMin()
182 | {
183 | return dataSize;
184 | }
185 |
186 | public Optional<@MaxDataSize("10MB") DataSize> getConstrainedByMax()
187 | {
188 | return dataSize;
189 | }
190 |
191 | public Optional<@MinDataSize("5000kB") @MaxDataSize("10000kB") DataSize> getConstrainedByMinAndMax()
192 | {
193 | return dataSize;
194 | }
195 | }
196 |
197 | public static class NullMinAnnotation
198 | {
199 | @SuppressWarnings("UnusedDeclaration")
200 | @MinDataSize("1MB")
201 | public DataSize getConstrainedByMin()
202 | {
203 | return null;
204 | }
205 | }
206 |
207 | public static class NullMaxAnnotation
208 | {
209 | @SuppressWarnings("UnusedDeclaration")
210 | @MaxDataSize("1MB")
211 | public DataSize getConstrainedByMin()
212 | {
213 | return null;
214 | }
215 | }
216 |
217 | public static class BrokenMinAnnotation
218 | {
219 | @SuppressWarnings("UnusedDeclaration")
220 | @MinDataSize("broken")
221 | public DataSize getConstrainedByMin()
222 | {
223 | return new DataSize(32, KILOBYTE);
224 | }
225 | }
226 |
227 | public static class BrokenMaxAnnotation
228 | {
229 | @SuppressWarnings("UnusedDeclaration")
230 | @MaxDataSize("broken")
231 | public DataSize getConstrainedByMin()
232 | {
233 | return new DataSize(32, KILOBYTE);
234 | }
235 | }
236 |
237 | public static class MinAnnotationOnOptional
238 | {
239 | @SuppressWarnings("UnusedDeclaration")
240 | @MinDataSize("1MB")
241 | public Optional getConstrainedByMin()
242 | {
243 | return Optional.empty();
244 | }
245 | }
246 |
247 | public static class MaxAnnotationOnOptional
248 | {
249 | @SuppressWarnings("UnusedDeclaration")
250 | @MaxDataSize("1MB")
251 | public Optional getConstrainedByMin()
252 | {
253 | return Optional.empty();
254 | }
255 | }
256 |
257 | public static class BrokenOptionalMinAnnotation
258 | {
259 | @SuppressWarnings("UnusedDeclaration")
260 | public Optional<@MinDataSize("broken") DataSize> getConstrainedByMin()
261 | {
262 | return Optional.empty();
263 | }
264 | }
265 |
266 | public static class BrokenOptionalMaxAnnotation
267 | {
268 | @SuppressWarnings("UnusedDeclaration")
269 | public Optional<@MaxDataSize("broken") DataSize> getConstrainedByMax()
270 | {
271 | return Optional.empty();
272 | }
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/TestDuration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import io.airlift.json.JsonCodec;
19 | import org.testng.annotations.DataProvider;
20 | import org.testng.annotations.Test;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 | import java.util.Locale;
25 | import java.util.concurrent.TimeUnit;
26 |
27 | import static io.airlift.testing.EquivalenceTester.comparisonTester;
28 | import static io.airlift.units.Duration.succinctDuration;
29 | import static io.airlift.units.Duration.succinctNanos;
30 | import static java.util.concurrent.TimeUnit.DAYS;
31 | import static java.util.concurrent.TimeUnit.HOURS;
32 | import static java.util.concurrent.TimeUnit.MICROSECONDS;
33 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
34 | import static java.util.concurrent.TimeUnit.MINUTES;
35 | import static java.util.concurrent.TimeUnit.NANOSECONDS;
36 | import static java.util.concurrent.TimeUnit.SECONDS;
37 | import static org.assertj.core.api.Assertions.assertThat;
38 | import static org.assertj.core.api.Assertions.offset;
39 | import static org.assertj.core.data.Percentage.withPercentage;
40 | import static org.testng.Assert.assertEquals;
41 | import static org.testng.Assert.assertFalse;
42 | import static org.testng.Assert.assertNotEquals;
43 | import static org.testng.Assert.assertTrue;
44 | import static org.testng.Assert.fail;
45 |
46 | public class TestDuration
47 | {
48 | @Test
49 | public void testSuccinctFactories()
50 | {
51 | assertEquals(succinctNanos(123), new Duration(123, NANOSECONDS));
52 | assertEquals(succinctNanos(123456), new Duration(123.456, MICROSECONDS));
53 | assertEquals(succinctNanos(SECONDS.toNanos(300)), new Duration(5, MINUTES));
54 |
55 | assertEquals(succinctDuration(123, NANOSECONDS), new Duration(123, NANOSECONDS));
56 | assertEquals(succinctDuration(123456, NANOSECONDS), new Duration(123.456, MICROSECONDS));
57 | assertEquals(succinctDuration(300, SECONDS), new Duration(5, MINUTES));
58 | }
59 |
60 | @Test
61 | public void testGetValue()
62 | {
63 | double millis = 12346789.0d;
64 | Duration duration = new Duration(millis, MILLISECONDS);
65 | assertThat(duration.getValue(MILLISECONDS)).isEqualTo(millis);
66 | assertThat(duration.getValue(SECONDS)).isCloseTo(millis / 1000, offset(0.001));
67 | assertThat(duration.getValue(MINUTES)).isCloseTo(millis / 1000 / 60, offset(0.001));
68 | assertThat(duration.getValue(HOURS)).isCloseTo(millis / 1000 / 60 / 60, offset(0.001));
69 | assertThat(duration.getValue(DAYS)).isCloseTo(millis / 1000 / 60 / 60 / 24, offset(0.001));
70 |
71 | double days = 3.0;
72 | duration = new Duration(days, DAYS);
73 | assertThat(duration.getValue(DAYS)).isEqualTo(days);
74 | assertThat(duration.getValue(HOURS)).isCloseTo(days * 24, offset(0.001));
75 | assertThat(duration.getValue(MINUTES)).isCloseTo(days * 24 * 60, offset(0.001));
76 | assertThat(duration.getValue(SECONDS)).isCloseTo(days * 24 * 60 * 60, offset(0.001));
77 | assertThat(duration.getValue(MILLISECONDS)).isCloseTo(days * 24 * 60 * 60 * 1000, offset(0.001));
78 | }
79 |
80 | @Test(dataProvider = "conversions")
81 | public void testConversions(TimeUnit unit, TimeUnit toTimeUnit, double factor)
82 | {
83 | Duration duration = new Duration(1, unit).convertTo(toTimeUnit);
84 | assertThat(duration.getUnit()).isEqualTo(toTimeUnit);
85 | assertThat(duration.getValue()).isCloseTo(factor, withPercentage(0.1));
86 | assertThat(duration.getValue(toTimeUnit)).isCloseTo(factor, withPercentage(0.1));
87 | }
88 |
89 | @Test(dataProvider = "conversions")
90 | public void testConvertToMostSuccinctDuration(TimeUnit unit, TimeUnit toTimeUnit, double factor)
91 | {
92 | Duration duration = new Duration(factor, toTimeUnit);
93 | Duration actual = duration.convertToMostSuccinctTimeUnit();
94 | assertThat(actual.getValue(toTimeUnit)).isCloseTo(factor, withPercentage(0.1));
95 | assertThat(actual.getValue(unit)).isCloseTo(1.0, offset(0.001));
96 | assertThat(actual.getUnit()).isEqualTo(unit);
97 | }
98 |
99 | @Test
100 | public void testEquivalence()
101 | {
102 | comparisonTester()
103 | .addLesserGroup(generateTimeBucket(0))
104 | .addGreaterGroup(generateTimeBucket(1))
105 | .addGreaterGroup(generateTimeBucket(123352))
106 | .addGreaterGroup(generateTimeBucket(Long.MAX_VALUE))
107 | .check();
108 | }
109 |
110 | @Test
111 | public void testToJavaTime()
112 | {
113 | assertThat(new Duration(0, DAYS).toJavaTime())
114 | .isEqualTo(java.time.Duration.ZERO);
115 |
116 | assertThat(new Duration(1, NANOSECONDS).toJavaTime())
117 | .isEqualTo(java.time.Duration.ofNanos(1));
118 | assertThat(new Duration(1, MICROSECONDS).toJavaTime())
119 | .isEqualTo(java.time.Duration.ofNanos(1000));
120 | assertThat(new Duration(1, MILLISECONDS).toJavaTime())
121 | .isEqualTo(java.time.Duration.ofMillis(1));
122 | assertThat(new Duration(1, SECONDS).toJavaTime())
123 | .isEqualTo(java.time.Duration.ofSeconds(1));
124 | assertThat(new Duration(1, MINUTES).toJavaTime())
125 | .isEqualTo(java.time.Duration.ofMinutes(1));
126 | assertThat(new Duration(1, HOURS).toJavaTime())
127 | .isEqualTo(java.time.Duration.ofHours(1));
128 | assertThat(new Duration(1, DAYS).toJavaTime())
129 | .isEqualTo(java.time.Duration.ofDays(1));
130 |
131 | assertThat(new Duration(42, NANOSECONDS).toJavaTime())
132 | .isEqualTo(java.time.Duration.ofNanos(42));
133 | assertThat(new Duration(42, MICROSECONDS).toJavaTime())
134 | .isEqualTo(java.time.Duration.ofNanos(42000));
135 | assertThat(new Duration(42, MILLISECONDS).toJavaTime())
136 | .isEqualTo(java.time.Duration.ofMillis(42));
137 | assertThat(new Duration(42, SECONDS).toJavaTime())
138 | .isEqualTo(java.time.Duration.ofSeconds(42));
139 | assertThat(new Duration(42, MINUTES).toJavaTime())
140 | .isEqualTo(java.time.Duration.ofMinutes(42));
141 | assertThat(new Duration(42, HOURS).toJavaTime())
142 | .isEqualTo(java.time.Duration.ofHours(42));
143 | assertThat(new Duration(42, DAYS).toJavaTime())
144 | .isEqualTo(java.time.Duration.ofDays(42));
145 |
146 | assertThat(new Duration(123456789, MILLISECONDS).toJavaTime())
147 | .isEqualTo(java.time.Duration.ofMillis(123456789));
148 | assertThat(new Duration(123456789, SECONDS).toJavaTime())
149 | .isEqualTo(java.time.Duration.ofSeconds(123456789));
150 | assertThat(new Duration(123456789, DAYS).toJavaTime())
151 | .isEqualTo(java.time.Duration.ofDays(123456789));
152 |
153 | assertThat(new Duration(1234567890123456789d, MILLISECONDS).toJavaTime())
154 | .isBetween(
155 | java.time.Duration.ofMillis(1234567890123456700L),
156 | java.time.Duration.ofMillis(1234567890123456800L));
157 | assertThat(new Duration(1234567890123456789d, SECONDS).toJavaTime())
158 | .isBetween(
159 | java.time.Duration.ofSeconds(1234567890123456700L),
160 | java.time.Duration.ofSeconds(1234567890123456800L));
161 | assertThat(new Duration(1234567890123456789d, DAYS).toJavaTime())
162 | .isEqualTo(java.time.Duration.ofSeconds(Long.MAX_VALUE));
163 |
164 | assertThat(new Duration(0.01, MILLISECONDS).toJavaTime())
165 | .isEqualTo(java.time.Duration.ofNanos(10_000));
166 | assertThat(new Duration(0.01, SECONDS).toJavaTime())
167 | .isEqualTo(java.time.Duration.ofMillis(10));
168 | assertThat(new Duration(0.01, DAYS).toJavaTime())
169 | .isEqualTo(java.time.Duration.ofSeconds(86400 / 100));
170 |
171 | assertThat(new Duration(0.25, MILLISECONDS).toJavaTime())
172 | .isEqualTo(java.time.Duration.ofNanos(250_000));
173 | assertThat(new Duration(0.25, SECONDS).toJavaTime())
174 | .isEqualTo(java.time.Duration.ofNanos(250_000_000));
175 | assertThat(new Duration(0.25, DAYS).toJavaTime())
176 | .isEqualTo(java.time.Duration.ofSeconds(86400 / 4));
177 |
178 | assertThat(new Duration(0.5, MILLISECONDS).toJavaTime())
179 | .isEqualTo(java.time.Duration.ofNanos(500_000));
180 | assertThat(new Duration(0.5, SECONDS).toJavaTime())
181 | .isEqualTo(java.time.Duration.ofNanos(500_000_000));
182 | assertThat(new Duration(0.5, DAYS).toJavaTime())
183 | .isEqualTo(java.time.Duration.ofSeconds(86400 / 2));
184 | }
185 |
186 | private static List generateTimeBucket(double seconds)
187 | {
188 | List bucket = new ArrayList<>();
189 | bucket.add(new Duration(seconds * 1000 * 1000 * 1000, NANOSECONDS));
190 | bucket.add(new Duration(seconds * 1000 * 1000, MICROSECONDS));
191 | bucket.add(new Duration(seconds * 1000, MILLISECONDS));
192 | bucket.add(new Duration(seconds, SECONDS));
193 | bucket.add(new Duration(seconds / 60, MINUTES));
194 | bucket.add(new Duration(seconds / 60 / 60, HOURS));
195 | // skip days for larger values as this results in rounding errors
196 | if (seconds <= 1.0) {
197 | bucket.add(new Duration(seconds / 60 / 60 / 24, DAYS));
198 | }
199 | return bucket;
200 | }
201 |
202 | @Test(dataProvider = "printedValues")
203 | public void testToString(String expectedString, double value, TimeUnit unit)
204 | {
205 | assertEquals(new Duration(value, unit).toString(), expectedString);
206 | }
207 |
208 | @Test(dataProvider = "printedValues")
209 | public void testNonEnglishLocale(String expectedString, double value, TimeUnit unit)
210 | throws Exception
211 | {
212 | synchronized (Locale.class) {
213 | Locale previous = Locale.getDefault();
214 | Locale.setDefault(Locale.GERMAN);
215 | try {
216 | assertEquals(new Duration(value, unit).toString(), expectedString);
217 | }
218 | finally {
219 | Locale.setDefault(previous);
220 | }
221 | }
222 | }
223 |
224 | @Test(dataProvider = "parseableValues")
225 | public void testValueOf(String string, double expectedValue, TimeUnit expectedUnit)
226 | {
227 | Duration duration = Duration.valueOf(string);
228 |
229 | assertEquals(duration.getUnit(), expectedUnit);
230 | assertEquals(duration.getValue(), expectedValue);
231 | }
232 |
233 | @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "duration is null")
234 | public void testValueOfRejectsNull()
235 | {
236 | Duration.valueOf(null);
237 | }
238 |
239 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "duration is empty")
240 | public void testValueOfRejectsEmptyString()
241 | {
242 | Duration.valueOf("");
243 | }
244 |
245 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Unknown time unit: kg")
246 | public void testValueOfRejectsInvalidUnit()
247 | {
248 | Duration.valueOf("1.234 kg");
249 | }
250 |
251 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "duration is not a valid.*")
252 | public void testValueOfRejectsInvalidNumber()
253 | {
254 | Duration.valueOf("1.2x4 s");
255 | }
256 |
257 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
258 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "value is negative: -1.0")
259 | public void testConstructorRejectsNegativeValue()
260 | {
261 | new Duration(-1, SECONDS);
262 | }
263 |
264 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
265 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "value is infinite: Infinity")
266 | public void testConstructorRejectsInfiniteValue()
267 | {
268 | new Duration(Double.POSITIVE_INFINITY, SECONDS);
269 | }
270 |
271 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
272 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "value is infinite: -Infinity")
273 | public void testConstructorRejectsNegativeInfiniteValue()
274 | {
275 | new Duration(Double.NEGATIVE_INFINITY, SECONDS);
276 | }
277 |
278 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
279 | @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "value is not a number")
280 | public void testConstructorRejectsNaN()
281 | {
282 | new Duration(Double.NaN, SECONDS);
283 | }
284 |
285 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
286 | @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "unit is null")
287 | public void testConstructorRejectsNullUnit()
288 | {
289 | new Duration(1, null);
290 | }
291 |
292 | @Test
293 | public void testEquals()
294 | {
295 | assertEquals(new Duration(12359.0d, MILLISECONDS), new Duration(12359.0d, MILLISECONDS));
296 | assertNotEquals(new Duration(4444.0d, MILLISECONDS), new Duration(12359.0d, MILLISECONDS));
297 | }
298 |
299 | @Test
300 | public void testIsZero()
301 | {
302 | assertTrue(Duration.ZERO.isZero());
303 | assertTrue(new Duration(0, HOURS).isZero());
304 | assertFalse(new Duration(12359.0d, MILLISECONDS).isZero());
305 |
306 | // small values
307 | assertFalse(new Duration(1e-20, MILLISECONDS).isZero());
308 | assertFalse(new Duration(1e-20, NANOSECONDS).isZero());
309 |
310 | // very small values
311 | assertFalse(new Duration(Double.MIN_VALUE, MILLISECONDS).isZero());
312 | assertTrue(new Duration(Double.MIN_VALUE, NANOSECONDS).isZero()); // TODO incorrect, due to equals converting to MILLIS
313 | }
314 |
315 | @Test
316 | public void testHashCode()
317 | {
318 | assertEquals(new Duration(12359.0d, MILLISECONDS).hashCode(), new Duration(12359.0d, MILLISECONDS).hashCode());
319 | assertNotEquals(new Duration(4444.0d, MILLISECONDS).hashCode(), new Duration(12359.0d, MILLISECONDS).hashCode());
320 | }
321 |
322 | @Test
323 | public void testNanoConversions()
324 | {
325 | double nanos = 1.0d;
326 | Duration duration = new Duration(nanos, NANOSECONDS);
327 | assertThat(duration.getValue()).isEqualTo(nanos);
328 | assertThat(duration.getValue(NANOSECONDS)).isEqualTo(nanos);
329 | assertThat(duration.getValue(MILLISECONDS)).isEqualTo(nanos / 1000000);
330 | assertThat(duration.getValue(SECONDS)).isEqualTo(nanos / 1000000 / 1000);
331 | assertThat(duration.getValue(MINUTES)).isCloseTo(nanos / 1000000 / 1000 / 60, offset(1.0E10));
332 | assertThat(duration.getValue(HOURS)).isCloseTo(nanos / 1000000 / 1000 / 60 / 60, offset(1.0E10));
333 | assertThat(duration.getValue(DAYS)).isCloseTo(nanos / 1000000 / 1000 / 60 / 60 / 24, offset(1.0E10));
334 | }
335 |
336 | @SuppressWarnings("ResultOfMethodCallIgnored")
337 | @Test
338 | public void invalidParameters()
339 | {
340 | failDurationConstruction(Double.NEGATIVE_INFINITY, MILLISECONDS);
341 | failDurationConstruction(Double.POSITIVE_INFINITY, MILLISECONDS);
342 | failDurationConstruction(Double.NaN, MILLISECONDS);
343 | failDurationConstruction(42, null);
344 | failDurationConstruction(-42, MILLISECONDS);
345 |
346 | Duration duration = new Duration(42, MILLISECONDS);
347 | try {
348 | duration.convertTo(null);
349 | fail("Expected NullPointerException");
350 | }
351 | catch (NullPointerException e) {
352 | // ok
353 | }
354 | try {
355 | duration.toString(null);
356 | fail("Expected NullPointerException");
357 | }
358 | catch (NullPointerException e) {
359 | // ok
360 | }
361 | }
362 |
363 | @Test
364 | public void testJsonRoundTrip()
365 | throws Exception
366 | {
367 | assertJsonRoundTrip(new Duration(1.234, MILLISECONDS));
368 | assertJsonRoundTrip(new Duration(1.234, SECONDS));
369 | assertJsonRoundTrip(new Duration(1.234, MINUTES));
370 | assertJsonRoundTrip(new Duration(1.234, HOURS));
371 | assertJsonRoundTrip(new Duration(1.234, DAYS));
372 | }
373 |
374 | private static void assertJsonRoundTrip(Duration duration)
375 | {
376 | JsonCodec durationCodec = JsonCodec.jsonCodec(Duration.class);
377 | String json = durationCodec.toJson(duration);
378 | Duration durationCopy = durationCodec.fromJson(json);
379 |
380 | assertThat(durationCopy.getValue(MILLISECONDS))
381 | .isCloseTo(duration.getValue(MILLISECONDS), withPercentage(1));
382 | }
383 |
384 | @SuppressWarnings("ResultOfObjectAllocationIgnored")
385 | private static void failDurationConstruction(double value, TimeUnit timeUnit)
386 | {
387 | try {
388 | new Duration(value, timeUnit);
389 | fail("Expected NullPointerException or IllegalArgumentException");
390 | }
391 | catch (NullPointerException | IllegalArgumentException e) {
392 | // ok
393 | }
394 | }
395 |
396 | @DataProvider(name = "parseableValues", parallel = true)
397 | private Object[][] parseableValues()
398 | {
399 | return new Object[][] {
400 | // spaces
401 | new Object[] {"1234 ns", 1234, NANOSECONDS},
402 | new Object[] {"1234 ms", 1234, MILLISECONDS},
403 | new Object[] {"1234 s", 1234, SECONDS},
404 | new Object[] {"1234 m", 1234, MINUTES},
405 | new Object[] {"1234 h", 1234, HOURS},
406 | new Object[] {"1234 d", 1234, DAYS},
407 | new Object[] {"1234.567 ns", 1234.567, NANOSECONDS},
408 | new Object[] {"1234.567 ms", 1234.567, MILLISECONDS},
409 | new Object[] {"1234.567 s", 1234.567, SECONDS},
410 | new Object[] {"1234.567 m", 1234.567, MINUTES},
411 | new Object[] {"1234.567 h", 1234.567, HOURS},
412 | new Object[] {"1234.567 d", 1234.567, DAYS},
413 | // no spaces
414 | new Object[] {"1234ns", 1234, NANOSECONDS},
415 | new Object[] {"1234ms", 1234, MILLISECONDS},
416 | new Object[] {"1234s", 1234, SECONDS},
417 | new Object[] {"1234m", 1234, MINUTES},
418 | new Object[] {"1234h", 1234, HOURS},
419 | new Object[] {"1234d", 1234, DAYS},
420 | new Object[] {"1234.567ns", 1234.567, NANOSECONDS},
421 | new Object[] {"1234.567ms", 1234.567, MILLISECONDS},
422 | new Object[] {"1234.567s", 1234.567, SECONDS},
423 | new Object[] {"1234.567m", 1234.567, MINUTES},
424 | new Object[] {"1234.567h", 1234.567, HOURS},
425 | new Object[] {"1234.567d", 1234.567, DAYS}
426 | };
427 | }
428 |
429 | @DataProvider(name = "printedValues", parallel = true)
430 | private Object[][] printedValues()
431 | {
432 | return new Object[][] {
433 | new Object[] {"1234.00ns", 1234, NANOSECONDS},
434 | new Object[] {"1234.00us", 1234, MICROSECONDS},
435 | new Object[] {"1234.00ms", 1234, MILLISECONDS},
436 | new Object[] {"1234.00s", 1234, SECONDS},
437 | new Object[] {"1234.00m", 1234, MINUTES},
438 | new Object[] {"1234.00h", 1234, HOURS},
439 | new Object[] {"1234.00d", 1234, DAYS},
440 | new Object[] {"1234.57ns", 1234.567, NANOSECONDS},
441 | new Object[] {"1234.57us", 1234.567, MICROSECONDS},
442 | new Object[] {"1234.57ms", 1234.567, MILLISECONDS},
443 | new Object[] {"1234.57s", 1234.567, SECONDS},
444 | new Object[] {"1234.57m", 1234.567, MINUTES},
445 | new Object[] {"1234.57h", 1234.567, HOURS},
446 | new Object[] {"1234.57d", 1234.567, DAYS}
447 | };
448 | }
449 |
450 | @DataProvider(name = "conversions", parallel = true)
451 | private Object[][] conversions()
452 | {
453 | return new Object[][] {
454 | new Object[] {NANOSECONDS, NANOSECONDS, 1.0},
455 | new Object[] {NANOSECONDS, MILLISECONDS, 1.0 / 1000_000},
456 | new Object[] {NANOSECONDS, SECONDS, 1.0 / 1000_000 / 1000},
457 | new Object[] {NANOSECONDS, MINUTES, 1.0 / 1000_000 / 1000 / 60},
458 | new Object[] {NANOSECONDS, HOURS, 1.0 / 1000_000 / 1000 / 60 / 60},
459 | new Object[] {NANOSECONDS, DAYS, 1.0 / 1000_000 / 1000 / 60 / 60 / 24},
460 |
461 | new Object[] {MILLISECONDS, NANOSECONDS, 1000000.0},
462 | new Object[] {MILLISECONDS, MILLISECONDS, 1.0},
463 | new Object[] {MILLISECONDS, SECONDS, 1.0 / 1000},
464 | new Object[] {MILLISECONDS, MINUTES, 1.0 / 1000 / 60},
465 | new Object[] {MILLISECONDS, HOURS, 1.0 / 1000 / 60 / 60},
466 | new Object[] {MILLISECONDS, DAYS, 1.0 / 1000 / 60 / 60 / 24},
467 |
468 | new Object[] {SECONDS, NANOSECONDS, 1000000.0 * 1000},
469 | new Object[] {SECONDS, MILLISECONDS, 1000},
470 | new Object[] {SECONDS, SECONDS, 1},
471 | new Object[] {SECONDS, MINUTES, 1.0 / 60},
472 | new Object[] {SECONDS, HOURS, 1.0 / 60 / 60},
473 | new Object[] {SECONDS, DAYS, 1.0 / 60 / 60 / 24},
474 |
475 | new Object[] {MINUTES, NANOSECONDS, 1000000.0 * 1000 * 60},
476 | new Object[] {MINUTES, MILLISECONDS, 1000 * 60},
477 | new Object[] {MINUTES, SECONDS, 60},
478 | new Object[] {MINUTES, MINUTES, 1},
479 | new Object[] {MINUTES, HOURS, 1.0 / 60},
480 | new Object[] {MINUTES, DAYS, 1.0 / 60 / 24},
481 |
482 | new Object[] {HOURS, NANOSECONDS, 1000000.0 * 1000 * 60 * 60},
483 | new Object[] {HOURS, MILLISECONDS, 1000 * 60 * 60},
484 | new Object[] {HOURS, SECONDS, 60 * 60},
485 | new Object[] {HOURS, MINUTES, 60},
486 | new Object[] {HOURS, HOURS, 1},
487 | new Object[] {HOURS, DAYS, 1.0 / 24},
488 |
489 | new Object[] {DAYS, NANOSECONDS, 1000000.0 * 1000 * 60 * 60 * 24},
490 | new Object[] {DAYS, MILLISECONDS, 1000 * 60 * 60 * 24},
491 | new Object[] {DAYS, SECONDS, 60 * 60 * 24},
492 | new Object[] {DAYS, MINUTES, 60 * 24},
493 | new Object[] {DAYS, HOURS, 24},
494 | new Object[] {DAYS, DAYS, 1},
495 | };
496 | }
497 | }
498 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/TestDurationValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010 Proofpoint, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.airlift.units;
17 |
18 | import jakarta.validation.ValidationException;
19 | import org.testng.annotations.Test;
20 |
21 | import java.util.Optional;
22 | import java.util.concurrent.TimeUnit;
23 |
24 | import static io.airlift.testing.ValidationAssertions.assertFailsValidation;
25 | import static io.airlift.testing.ValidationAssertions.assertValidates;
26 | import static io.airlift.units.ConstraintValidatorAssert.assertThat;
27 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
28 |
29 | public class TestDurationValidator
30 | {
31 | @Test
32 | public void testMaxDurationValidator()
33 | {
34 | MaxDurationValidator maxValidator = new MaxDurationValidator();
35 | maxValidator.initialize(new MockMaxDuration(new Duration(5, TimeUnit.SECONDS)));
36 |
37 | assertThat(maxValidator).isValidFor(new Duration(0, TimeUnit.SECONDS));
38 | assertThat(maxValidator).isValidFor(new Duration(5, TimeUnit.SECONDS));
39 | assertThat(maxValidator).isInvalidFor(new Duration(6, TimeUnit.SECONDS));
40 | }
41 |
42 | @Test
43 | public void testMinDurationValidator()
44 | {
45 | MinDurationValidator minValidator = new MinDurationValidator();
46 | minValidator.initialize(new MockMinDuration(new Duration(5, TimeUnit.SECONDS)));
47 |
48 | assertThat(minValidator).isValidFor(new Duration(5, TimeUnit.SECONDS));
49 | assertThat(minValidator).isValidFor(new Duration(6, TimeUnit.SECONDS));
50 | assertThat(minValidator).isInvalidFor(new Duration(0, TimeUnit.SECONDS));
51 | }
52 |
53 | @Test
54 | public void testAllowsNullMinAnnotation()
55 | {
56 | assertValidates(new NullMinAnnotation());
57 | }
58 |
59 | @Test
60 | public void testAllowsNullMaxAnnotation()
61 | {
62 | assertValidates(new NullMaxAnnotation());
63 | }
64 |
65 | @Test
66 | public void testDetectsBrokenMinAnnotation()
67 | {
68 | assertThatThrownBy(() -> assertValidates(new BrokenMinAnnotation()))
69 | .isInstanceOf(ValidationException.class)
70 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MinDurationValidator.")
71 | .hasRootCauseInstanceOf(IllegalArgumentException.class)
72 | .hasRootCauseMessage("duration is not a valid data duration string: broken");
73 |
74 | assertThatThrownBy(() -> assertValidates(new MinAnnotationOnOptional()))
75 | .isInstanceOf(ValidationException.class)
76 | .hasMessageContaining("No validator could be found for constraint 'io.airlift.units.MinDuration' validating type 'java.util.Optional'. Check configuration for 'constrainedByMin'");
77 |
78 | assertThatThrownBy(() -> assertValidates(new BrokenOptionalMinAnnotation()))
79 | .isInstanceOf(ValidationException.class)
80 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MinDurationValidator.")
81 | .hasRootCauseInstanceOf(IllegalArgumentException.class)
82 | .hasRootCauseMessage("duration is not a valid data duration string: broken");
83 | }
84 |
85 | @Test
86 | public void testDetectsBrokenMaxAnnotation()
87 | {
88 | assertThatThrownBy(() -> assertValidates(new BrokenMaxAnnotation()))
89 | .isInstanceOf(ValidationException.class)
90 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MinDurationValidator.")
91 | .hasRootCauseInstanceOf(IllegalArgumentException.class)
92 | .hasRootCauseMessage("duration is not a valid data duration string: broken");
93 |
94 | assertThatThrownBy(() -> assertValidates(new MaxAnnotationOnOptional()))
95 | .isInstanceOf(ValidationException.class)
96 | .hasMessageContaining("No validator could be found for constraint 'io.airlift.units.MaxDuration' validating type 'java.util.Optional'. Check configuration for 'constrainedByMin'");
97 |
98 | assertThatThrownBy(() -> assertValidates(new BrokenOptionalMaxAnnotation()))
99 | .isInstanceOf(ValidationException.class)
100 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MaxDurationValidator.")
101 | .hasRootCauseInstanceOf(IllegalArgumentException.class)
102 | .hasRootCauseMessage("duration is not a valid data duration string: broken");
103 | }
104 |
105 | @Test
106 | public void testPassesValidation()
107 | {
108 | assertValidates(new ConstrainedDuration(new Duration(7, TimeUnit.SECONDS)));
109 | assertValidates(new ConstrainedOptionalDuration(Optional.of(new Duration(7, TimeUnit.SECONDS))));
110 | assertValidates(new ConstrainedOptionalDuration(Optional.empty()));
111 | assertValidates(new ConstrainedOptionalDuration(null));
112 | }
113 |
114 | @Test
115 | public void testFailsMaxDurationConstraint()
116 | {
117 | assertFailsValidation(new ConstrainedDuration(new Duration(11, TimeUnit.SECONDS)), "constrainedByMax", "must be less than or equal to 10s", MaxDuration.class);
118 | assertFailsValidation(new ConstrainedDuration(new Duration(11, TimeUnit.SECONDS)), "constrainedByMinAndMax", "must be less than or equal to 10000ms", MaxDuration.class);
119 |
120 | assertFailsValidation(new ConstrainedOptionalDuration(Optional.of(new Duration(11, TimeUnit.SECONDS))), "constrainedByMax", "must be less than or equal to 10s", MaxDuration.class);
121 | assertFailsValidation(new ConstrainedOptionalDuration(Optional.of(new Duration(11, TimeUnit.SECONDS))), "constrainedByMinAndMax", "must be less than or equal to 10000ms", MaxDuration.class);
122 | }
123 |
124 | @Test
125 | public void testFailsMinDurationConstraint()
126 | {
127 | assertFailsValidation(new ConstrainedDuration(new Duration(1, TimeUnit.SECONDS)), "constrainedByMin", "must be greater than or equal to 5s", MinDuration.class);
128 | assertFailsValidation(new ConstrainedDuration(new Duration(1, TimeUnit.SECONDS)), "constrainedByMinAndMax", "must be greater than or equal to 5000ms", MinDuration.class);
129 |
130 | assertFailsValidation(new ConstrainedOptionalDuration(Optional.of(new Duration(1, TimeUnit.SECONDS))), "constrainedByMin", "must be greater than or equal to 5s", MinDuration.class);
131 | assertFailsValidation(new ConstrainedOptionalDuration(Optional.of(new Duration(1, TimeUnit.SECONDS))), "constrainedByMinAndMax", "must be greater than or equal to 5000ms", MinDuration.class);
132 | }
133 |
134 | @SuppressWarnings("UnusedDeclaration")
135 | public static class ConstrainedDuration
136 | {
137 | private final Duration duration;
138 |
139 | public ConstrainedDuration(Duration duration)
140 | {
141 | this.duration = duration;
142 | }
143 |
144 | @MinDuration("5s")
145 | public Duration getConstrainedByMin()
146 | {
147 | return duration;
148 | }
149 |
150 | @MaxDuration("10s")
151 | public Duration getConstrainedByMax()
152 | {
153 | return duration;
154 | }
155 |
156 | @MinDuration("5000ms")
157 | @MaxDuration("10000ms")
158 | public Duration getConstrainedByMinAndMax()
159 | {
160 | return duration;
161 | }
162 | }
163 |
164 | @SuppressWarnings("UnusedDeclaration")
165 | public static class ConstrainedOptionalDuration
166 | {
167 | private final Optional duration;
168 |
169 | public ConstrainedOptionalDuration(Optional duration)
170 | {
171 | this.duration = duration;
172 | }
173 |
174 | public Optional<@MinDuration("5s") Duration> getConstrainedByMin()
175 | {
176 | return duration;
177 | }
178 |
179 | public Optional<@MaxDuration("10s") Duration> getConstrainedByMax()
180 | {
181 | return duration;
182 | }
183 |
184 | public Optional<@MinDuration("5000ms") @MaxDuration("10000ms") Duration> getConstrainedByMinAndMax()
185 | {
186 | return duration;
187 | }
188 | }
189 |
190 | public static class NullMinAnnotation
191 | {
192 | @SuppressWarnings("UnusedDeclaration")
193 | @MinDuration("1s")
194 | public Duration getConstrainedByMin()
195 | {
196 | return null;
197 | }
198 | }
199 |
200 | public static class NullMaxAnnotation
201 | {
202 | @SuppressWarnings("UnusedDeclaration")
203 | @MaxDuration("1s")
204 | public Duration getConstrainedByMin()
205 | {
206 | return null;
207 | }
208 | }
209 |
210 | public static class BrokenMinAnnotation
211 | {
212 | @SuppressWarnings("UnusedDeclaration")
213 | @MinDuration("broken")
214 | public Duration getConstrainedByMin()
215 | {
216 | return new Duration(10, TimeUnit.SECONDS);
217 | }
218 | }
219 |
220 | public static class BrokenMaxAnnotation
221 | {
222 | @SuppressWarnings("UnusedDeclaration")
223 | @MinDuration("broken")
224 | public Duration getConstrainedByMin()
225 | {
226 | return new Duration(10, TimeUnit.SECONDS);
227 | }
228 | }
229 |
230 | public static class MinAnnotationOnOptional
231 | {
232 | @SuppressWarnings("UnusedDeclaration")
233 | @MinDuration("1s")
234 | public Optional getConstrainedByMin()
235 | {
236 | return Optional.empty();
237 | }
238 | }
239 |
240 | public static class MaxAnnotationOnOptional
241 | {
242 | @SuppressWarnings("UnusedDeclaration")
243 | @MaxDuration("1s")
244 | public Optional getConstrainedByMin()
245 | {
246 | return Optional.empty();
247 | }
248 | }
249 |
250 | public static class BrokenOptionalMinAnnotation
251 | {
252 | @SuppressWarnings("UnusedDeclaration")
253 | public Optional<@MinDuration("broken") Duration> getConstrainedByMin()
254 | {
255 | return Optional.empty();
256 | }
257 | }
258 |
259 | public static class BrokenOptionalMaxAnnotation
260 | {
261 | @SuppressWarnings("UnusedDeclaration")
262 | public Optional<@MaxDuration("broken") Duration> getConstrainedByMax()
263 | {
264 | return Optional.empty();
265 | }
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/TestThreadCountValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import jakarta.validation.ValidationException;
17 | import org.testng.annotations.Test;
18 |
19 | import java.util.Optional;
20 |
21 | import static io.airlift.testing.ValidationAssertions.assertFailsValidation;
22 | import static io.airlift.testing.ValidationAssertions.assertValidates;
23 | import static io.airlift.units.ConstraintValidatorAssert.assertThat;
24 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
25 |
26 | public class TestThreadCountValidator
27 | {
28 | @Test
29 | public void testMaxThreadCountValidator()
30 | {
31 | MaxThreadCountValidator maxValidator = new MaxThreadCountValidator();
32 | maxValidator.initialize(new MockMaxThreadCount(new ThreadCount(8)));
33 |
34 | assertThat(maxValidator).isValidFor(new ThreadCount(0));
35 | assertThat(maxValidator).isValidFor(new ThreadCount(5));
36 | assertThat(maxValidator).isValidFor(new ThreadCount(8));
37 | }
38 |
39 | @Test
40 | public void testMinThreadCountValidator()
41 | {
42 | MinThreadCountValidator minValidator = new MinThreadCountValidator();
43 | minValidator.initialize(new MockMinThreadCount(new ThreadCount(4)));
44 |
45 | assertThat(minValidator).isValidFor(new ThreadCount(4));
46 | assertThat(minValidator).isValidFor(new ThreadCount(5));
47 | }
48 |
49 | @Test
50 | public void testAllowsNullMinAnnotation()
51 | {
52 | assertValidates(new TestThreadCountValidator.NullMinAnnotation());
53 | }
54 |
55 | @Test
56 | public void testAllowsNullMaxAnnotation()
57 | {
58 | assertValidates(new TestThreadCountValidator.NullMaxAnnotation());
59 | }
60 |
61 | @Test
62 | public void testDetectsBrokenMinAnnotation()
63 | {
64 | assertThatThrownBy(() -> assertValidates(new TestThreadCountValidator.BrokenMinAnnotation()))
65 | .isInstanceOf(ValidationException.class)
66 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MinThreadCountValidator.")
67 | .hasRootCauseInstanceOf(NumberFormatException.class)
68 | .hasRootCauseMessage("For input string: \"broken\"");
69 |
70 | assertThatThrownBy(() -> assertValidates(new TestThreadCountValidator.MinAnnotationOnOptional()))
71 | .isInstanceOf(ValidationException.class)
72 | .hasMessageContaining("No validator could be found for constraint 'io.airlift.units.MinThreadCount' validating type 'java.util.Optional'. Check configuration for 'constrainedByMin'");
73 |
74 | assertThatThrownBy(() -> assertValidates(new TestThreadCountValidator.BrokenOptionalMinAnnotation()))
75 | .isInstanceOf(ValidationException.class)
76 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MinThreadCountValidator.")
77 | .hasRootCauseInstanceOf(NumberFormatException.class)
78 | .hasRootCauseMessage("For input string: \"broken\"");
79 | }
80 |
81 | @Test
82 | public void testDetectsBrokenMaxAnnotation()
83 | {
84 | assertThatThrownBy(() -> assertValidates(new TestThreadCountValidator.BrokenMaxAnnotation()))
85 | .isInstanceOf(ValidationException.class)
86 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MaxThreadCountValidator.")
87 | .hasRootCauseInstanceOf(NumberFormatException.class)
88 | .hasRootCauseMessage("For input string: \"broken\"");
89 |
90 | assertThatThrownBy(() -> assertValidates(new TestThreadCountValidator.MaxAnnotationOnOptional()))
91 | .isInstanceOf(ValidationException.class)
92 | .hasMessageContaining("No validator could be found for constraint 'io.airlift.units.MaxThreadCount' validating type 'java.util.Optional'. Check configuration for 'constrainedByMin'");
93 |
94 | assertThatThrownBy(() -> assertValidates(new TestThreadCountValidator.BrokenOptionalMaxAnnotation()))
95 | .isInstanceOf(ValidationException.class)
96 | .hasMessage("HV000032: Unable to initialize io.airlift.units.MaxThreadCountValidator.")
97 | .hasRootCauseInstanceOf(NumberFormatException.class)
98 | .hasRootCauseMessage("For input string: \"broken\"");
99 | }
100 |
101 | @Test
102 | public void testPassesValidation()
103 | {
104 | assertValidates(new TestThreadCountValidator.ConstrainedThreadCount(new ThreadCount(7)));
105 | assertValidates(new TestThreadCountValidator.ConstrainedOptionalThreadCount(Optional.of(new ThreadCount(7))));
106 | assertValidates(new TestThreadCountValidator.ConstrainedOptionalThreadCount(Optional.empty()));
107 | assertValidates(new TestThreadCountValidator.ConstrainedOptionalThreadCount(null));
108 | }
109 |
110 | @Test
111 | public void testFailsMaxThreadCountConstraint()
112 | {
113 | assertFailsValidation(new TestThreadCountValidator.ConstrainedThreadCount(new ThreadCount(11)), "constrainedByMinAndMax", "must be less than or equal to 10", MaxThreadCount.class);
114 | assertFailsValidation(new TestThreadCountValidator.ConstrainedThreadCount(new ThreadCount(11)), "constrainedByMax", "must be less than or equal to 10", MaxThreadCount.class);
115 |
116 | assertFailsValidation(new TestThreadCountValidator.ConstrainedOptionalThreadCount(Optional.of(new ThreadCount(11))), "constrainedByMinAndMax", "must be less than or equal to 10", MaxThreadCount.class);
117 | assertFailsValidation(new TestThreadCountValidator.ConstrainedOptionalThreadCount(Optional.of(new ThreadCount(11))), "constrainedByMax", "must be less than or equal to 10", MaxThreadCount.class);
118 | }
119 |
120 | @Test
121 | public void testFailsMinThreadCountConstraint()
122 | {
123 | assertFailsValidation(new TestThreadCountValidator.ConstrainedThreadCount(new ThreadCount(1)), "constrainedByMin", "must be greater than or equal to 5", MinThreadCount.class);
124 | assertFailsValidation(new TestThreadCountValidator.ConstrainedThreadCount(new ThreadCount(1)), "constrainedByMinAndMax", "must be greater than or equal to 5", MinThreadCount.class);
125 |
126 | assertFailsValidation(new TestThreadCountValidator.ConstrainedOptionalThreadCount(Optional.of(new ThreadCount(1))), "constrainedByMin", "must be greater than or equal to 5", MinThreadCount.class);
127 | assertFailsValidation(new TestThreadCountValidator.ConstrainedOptionalThreadCount(Optional.of(new ThreadCount(1))), "constrainedByMinAndMax", "must be greater than or equal to 5", MinThreadCount.class);
128 | }
129 |
130 | @SuppressWarnings("UnusedDeclaration")
131 | public static class ConstrainedThreadCount
132 | {
133 | private final ThreadCount threadCount;
134 |
135 | public ConstrainedThreadCount(ThreadCount threadCount)
136 | {
137 | this.threadCount = threadCount;
138 | }
139 |
140 | @MinThreadCount("5")
141 | public ThreadCount getConstrainedByMin()
142 | {
143 | return threadCount;
144 | }
145 |
146 | @MaxThreadCount("10")
147 | public ThreadCount getConstrainedByMax()
148 | {
149 | return threadCount;
150 | }
151 |
152 | @MinThreadCount("5")
153 | @MaxThreadCount("10")
154 | public ThreadCount getConstrainedByMinAndMax()
155 | {
156 | return threadCount;
157 | }
158 | }
159 |
160 | @SuppressWarnings("UnusedDeclaration")
161 | public static class ConstrainedOptionalThreadCount
162 | {
163 | private final Optional threadCount;
164 |
165 | public ConstrainedOptionalThreadCount(Optional threadCount)
166 | {
167 | this.threadCount = threadCount;
168 | }
169 |
170 | public Optional<@MinThreadCount("5") ThreadCount> getConstrainedByMin()
171 | {
172 | return threadCount;
173 | }
174 |
175 | public Optional<@MaxThreadCount("10") ThreadCount> getConstrainedByMax()
176 | {
177 | return threadCount;
178 | }
179 |
180 | public Optional<@MinThreadCount("5") @MaxThreadCount("10") ThreadCount> getConstrainedByMinAndMax()
181 | {
182 | return threadCount;
183 | }
184 | }
185 |
186 | public static class NullMinAnnotation
187 | {
188 | @SuppressWarnings("UnusedDeclaration")
189 | @MinThreadCount("16")
190 | public ThreadCount getConstrainedByMin()
191 | {
192 | return null;
193 | }
194 | }
195 |
196 | public static class NullMaxAnnotation
197 | {
198 | @SuppressWarnings("UnusedDeclaration")
199 | @MaxThreadCount("16")
200 | public ThreadCount getConstrainedByMin()
201 | {
202 | return null;
203 | }
204 | }
205 |
206 | public static class BrokenMinAnnotation
207 | {
208 | @SuppressWarnings("UnusedDeclaration")
209 | @MinThreadCount("broken")
210 | public ThreadCount getConstrainedByMin()
211 | {
212 | return new ThreadCount(32);
213 | }
214 | }
215 |
216 | public static class BrokenMaxAnnotation
217 | {
218 | @SuppressWarnings("UnusedDeclaration")
219 | @MaxThreadCount("broken")
220 | public ThreadCount getConstrainedByMin()
221 | {
222 | return new ThreadCount(32);
223 | }
224 | }
225 |
226 | public static class MinAnnotationOnOptional
227 | {
228 | @SuppressWarnings("UnusedDeclaration")
229 | @MinThreadCount("16")
230 | public Optional getConstrainedByMin()
231 | {
232 | return Optional.empty();
233 | }
234 | }
235 |
236 | public static class MaxAnnotationOnOptional
237 | {
238 | @SuppressWarnings("UnusedDeclaration")
239 | @MaxThreadCount("16")
240 | public Optional getConstrainedByMin()
241 | {
242 | return Optional.empty();
243 | }
244 | }
245 |
246 | public static class BrokenOptionalMinAnnotation
247 | {
248 | @SuppressWarnings("UnusedDeclaration")
249 | public Optional<@MinThreadCount("broken") ThreadCount> getConstrainedByMin()
250 | {
251 | return Optional.empty();
252 | }
253 | }
254 |
255 | public static class BrokenOptionalMaxAnnotation
256 | {
257 | @SuppressWarnings("UnusedDeclaration")
258 | public Optional<@MaxThreadCount("broken") ThreadCount> getConstrainedByMax()
259 | {
260 | return Optional.empty();
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/units/TestThreadsCount.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.units;
15 |
16 | import org.testng.annotations.Test;
17 |
18 | import static io.airlift.units.ThreadCount.MachineInfo.getAvailablePhysicalProcessorCount;
19 | import static java.lang.Math.round;
20 | import static org.assertj.core.api.Assertions.assertThat;
21 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
22 |
23 | public class TestThreadsCount
24 | {
25 | public static final int AVAILABLE_PROCESSORS = getAvailablePhysicalProcessorCount();
26 |
27 | @Test
28 | public void ensureRequiredNumberOfProcessors()
29 | {
30 | // For tests to work properly we need at least two threads
31 | assertThat(AVAILABLE_PROCESSORS).isGreaterThan(1);
32 | }
33 |
34 | @Test
35 | public void testParsingIntegerValues()
36 | {
37 | assertThreadsCount("1", 1);
38 | assertThreadsCount("2", 2);
39 | assertThreadsCount("67", 67);
40 | assertThreadsCount("0", 0);
41 | assertThreadsCount(Integer.valueOf(Integer.MAX_VALUE).toString(), Integer.MAX_VALUE);
42 | assertInvalidValue("-1", "Thread count cannot be negative");
43 | assertInvalidValue("67.0", "Cannot parse value '67.0' as integer");
44 | assertInvalidValue(Long.valueOf(((long) Integer.MAX_VALUE) + 1).toString(), "Thread count is greater than 2^32 - 1");
45 | }
46 |
47 | @Test
48 | public void testParsingMultiplierPerCore()
49 | {
50 | assertThreadsCount("1C", AVAILABLE_PROCESSORS);
51 | assertThreadsCount("0.5 C", AVAILABLE_PROCESSORS / 2);
52 | assertThreadsCount("0.2 C", round(AVAILABLE_PROCESSORS / 5.0f));
53 | assertThreadsCount("1.5C", round(AVAILABLE_PROCESSORS * 1.5f));
54 | assertThreadsCount("2 C", AVAILABLE_PROCESSORS * 2);
55 | assertThreadsCount("0.0001 C", 0);
56 | assertInvalidValue("-0.0001 C", "Thread multiplier cannot be negative");
57 | assertInvalidValue(-1, "Thread count cannot be negative");
58 | assertInvalidValue("-1C", "Thread multiplier cannot be negative");
59 | assertInvalidValue("-1SC", "Cannot parse value '-1S' as float");
60 | assertInvalidValue("2147483647C", "Thread count is greater than 2^32 - 1");
61 | assertInvalidValue("3147483648C", "Thread count is greater than 2^32 - 1");
62 | }
63 |
64 | @Test
65 | public void testParsingBoundedValue()
66 | {
67 | assertBoundedThreadsCount("3", "1", "1", 1);
68 | assertBoundedThreadsCount("256C", "4", "16", 16);
69 | assertBoundedThreadsCount("3", "4", "16", 4);
70 | }
71 |
72 | private void assertThreadsCount(String value, int expected)
73 | {
74 | ThreadCount threadCount = ThreadCount.valueOf(value);
75 | assertThat(threadCount).isEqualTo(ThreadCount.exactValueOf(expected));
76 | assertThat(threadCount.getThreadCount()).isEqualTo(expected);
77 | }
78 |
79 | private void assertBoundedThreadsCount(String value, String min, String max, int expected)
80 | {
81 | ThreadCount threadCount = ThreadCount.boundedValueOf(value, min, max);
82 | assertThat(threadCount).isEqualTo(new ThreadCount(expected));
83 | }
84 |
85 | private void assertInvalidValue(String value, String expectedMessage)
86 | {
87 | assertThatThrownBy(() -> ThreadCount.valueOf(value))
88 | .isInstanceOf(IllegalArgumentException.class)
89 | .hasMessage(expectedMessage);
90 | }
91 |
92 | private void assertInvalidValue(int value, String expectedMessage)
93 | {
94 | assertThatThrownBy(() -> new ThreadCount(value))
95 | .isInstanceOf(IllegalArgumentException.class)
96 | .hasMessage(expectedMessage);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------