diff --git a/checker-qual-shim/README.md b/checker-qual-shim/README.md
new file mode 100644
index 0000000..97c3d89
--- /dev/null
+++ b/checker-qual-shim/README.md
@@ -0,0 +1,4 @@
+This is here purely to workaround https://github.com/typetools/checker-framework/issues/6420
+
+We keep a compiletime-only copy of `DefaultQualifier`, with the only change being its @Retention, so that
+usages of `DefaultQualifier` within util-core are kept in the compiled jar.
diff --git a/checker-qual-shim/build.gradle b/checker-qual-shim/build.gradle
new file mode 100644
index 0000000..1718238
--- /dev/null
+++ b/checker-qual-shim/build.gradle
@@ -0,0 +1,9 @@
+plugins {
+ id 'java'
+}
+
+dependencies {
+ compileOnly libs.checkerQual
+}
+
+indeedOss.activateFeature 'java'
diff --git a/checker-qual-shim/src/main/java/org/checkerframework/framework/qual/DefaultQualifier.java b/checker-qual-shim/src/main/java/org/checkerframework/framework/qual/DefaultQualifier.java
new file mode 100644
index 0000000..e8a52ea
--- /dev/null
+++ b/checker-qual-shim/src/main/java/org/checkerframework/framework/qual/DefaultQualifier.java
@@ -0,0 +1,111 @@
+/**
+ * Checker Framework qualifiers Copyright 2004-present by the Checker Framework developers
+ *
+ *
MIT License:
+ *
+ *
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ *
The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ *
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package org.checkerframework.framework.qual;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Applied to a declaration of a package, type, method, variable, etc., specifies that the given
+ * annotation should be the default. The default is applied to type uses within the declaration for
+ * which no other annotation is explicitly written. (The default is not applied to the "parametric
+ * locations": class declarations, type parameter declarations, and type parameter uses.) If
+ * multiple {@code DefaultQualifier} annotations are in scope, the innermost one takes precedence.
+ * DefaultQualifier takes precedence over {@link DefaultQualifierInHierarchy}.
+ *
+ *
You may write multiple {@code @DefaultQualifier} annotations (for unrelated type systems, or
+ * with different {@code locations} fields) at the same location. For example:
+ *
+ *
+ * @DefaultQualifier(NonNull.class)
+ * @DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.IMPLICIT_UPPER_BOUND)
+ * @DefaultQualifier(Tainted.class)
+ * class MyClass { ... }
+ *
+ *
+ * This annotation currently has no effect in stub files.
+ *
+ * @see org.checkerframework.framework.qual.TypeUseLocation
+ * @see DefaultQualifierInHierarchy
+ * @see DefaultFor
+ * @checker_framework.manual #defaults Default qualifier for unannotated types
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({
+ ElementType.PACKAGE,
+ ElementType.TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.METHOD,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.PARAMETER
+})
+@Repeatable(DefaultQualifier.List.class)
+public @interface DefaultQualifier {
+
+ /**
+ * The Class for the default annotation.
+ *
+ *
To prevent affecting other type systems, always specify an annotation in your own type
+ * hierarchy. (For example, do not set {@link
+ * org.checkerframework.common.subtyping.qual.Unqualified} as the default.)
+ */
+ Class extends Annotation> value();
+
+ /**
+ * Returns the locations to which the annotation should be applied.
+ *
+ * @return the locations to which the annotation should be applied
+ */
+ TypeUseLocation[] locations() default {TypeUseLocation.ALL};
+
+ /**
+ * A wrapper annotation that makes the {@link DefaultQualifier} annotation repeatable.
+ *
+ *
Programmers generally do not need to write this. It is created by Java when a programmer
+ * writes more than one {@link DefaultQualifier} annotation at the same location.
+ */
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({
+ ElementType.PACKAGE,
+ ElementType.TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.METHOD,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.PARAMETER
+ })
+ public static @interface List {
+ /**
+ * Return the repeatable annotations.
+ *
+ * @return the repeatable annotations
+ */
+ DefaultQualifier[] value();
+ }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d5b1f53..fadce4a 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,9 +2,9 @@
slf4jApi = "org.slf4j:slf4j-api:1.7.32"
junit = "junit:junit:4.8.2"
guava = "com.google.guava:guava:20.0"
-jsr305 = "com.google.code.findbugs:jsr305:1.3.9"
servletApi = "org.apache.tomcat:tomcat-servlet-api:7.0.8"
freemarker = "org.freemarker:freemarker:2.3.16"
easymock = "org.easymock:easymock:4.1"
commonsLang = "commons-lang:commons-lang:2.6"
hamcrest = "org.hamcrest:hamcrest-core:2.2"
+checkerQual = "org.checkerframework:checker-qual:3.42.0"
diff --git a/io/build.gradle b/io/build.gradle
index c067948..c124491 100644
--- a/io/build.gradle
+++ b/io/build.gradle
@@ -6,13 +6,12 @@ dependencies {
implementation project(':util-core')
implementation libs.guava
implementation libs.slf4jApi
+ api libs.checkerQual
// This is a fake copy of the log4j1 Logger, which allows us to continue
// accepting it in our deprecated method signatures without accidentially using
// any of its functionality.
compileOnly project(':log4j1dummyshim')
- compileOnly libs.jsr305
-
testImplementation libs.junit
}
diff --git a/io/src/main/java/com/indeed/util/io/Directories.java b/io/src/main/java/com/indeed/util/io/Directories.java
index f3cdaf9..7dffeee 100644
--- a/io/src/main/java/com/indeed/util/io/Directories.java
+++ b/io/src/main/java/com/indeed/util/io/Directories.java
@@ -2,9 +2,8 @@
package com.indeed.util.io;
import com.google.common.collect.Iterables;
+import org.checkerframework.checker.nullness.qual.NonNull;
-import javax.annotation.Nonnegative;
-import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
@@ -26,8 +25,7 @@ public final class Directories {
* @return number of inodes under it.
* @throws IOException
*/
- @Nonnegative
- public static int count(@Nonnull final Path dir) throws IOException {
+ public static int count(@NonNull final Path dir) throws IOException {
try (final DirectoryStream stream = Files.newDirectoryStream(dir)) {
return Iterables.size(stream);
} catch (DirectoryIteratorException ex) {
@@ -48,8 +46,8 @@ public static int count(@Nonnull final Path dir) throws IOException {
* @return all files in that directory
* @throws IOException
*/
- @Nonnull
- public static List list(@Nonnull final Path dir) throws IOException {
+ @NonNull
+ public static List list(@NonNull final Path dir) throws IOException {
final List contents = new ArrayList<>();
try (final DirectoryStream stream = Files.newDirectoryStream(dir)) {
for (final Path entry : stream) {
diff --git a/io/src/main/java/com/indeed/util/io/Files.java b/io/src/main/java/com/indeed/util/io/Files.java
index 62f9c3b..5b83316 100644
--- a/io/src/main/java/com/indeed/util/io/Files.java
+++ b/io/src/main/java/com/indeed/util/io/Files.java
@@ -5,10 +5,10 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.hash.Hashing;
+import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nonnull;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
@@ -67,7 +67,7 @@ public static String buildPath(String... parts) {
* be written, flushed, synced, or closed
*/
public static void writeObjectToFileOrDie2(
- @Nonnull final Object obj, @Nonnull final String file) throws IOException {
+ @NonNull final Object obj, @NonNull final String file) throws IOException {
Preconditions.checkNotNull(file, "file argument is required!");
Preconditions.checkArgument(!file.isEmpty(), "file argument is required!");
@@ -106,18 +106,18 @@ public static void writeObjectToFileOrDie2(
/** @deprecated Use {@link #writeObjectToFileOrDie2(java.lang.Object, java.lang.String)} */
@Deprecated
public static void writeObjectToFileOrDie(
- @Nonnull final Object obj,
- @Nonnull final String file,
- @Nonnull final org.apache.log4j.Logger log)
+ @NonNull final Object obj,
+ @NonNull final String file,
+ final org.apache.log4j.@NonNull Logger log)
throws IOException {
writeObjectToFileOrDie2(obj, file);
}
private static class ObjectOutputStreamCallback implements OutputStreamCallback {
private long checksumForWrittenData = 0L;
- @Nonnull private final Object obj;
+ @NonNull private final Object obj;
- private ObjectOutputStreamCallback(@Nonnull Object obj) {
+ private ObjectOutputStreamCallback(@NonNull Object obj) {
this.obj = obj;
}
@@ -126,7 +126,7 @@ public long getChecksumValue() {
}
@Override
- public void writeAndFlushData(@Nonnull OutputStream outputStream) throws IOException {
+ public void writeAndFlushData(@NonNull OutputStream outputStream) throws IOException {
final ChecksummingOutputStream checksummingOutputStream =
new ChecksummingOutputStream(new BufferedOutputStream(outputStream));
final ObjectOutputStream out = new ObjectOutputStream(checksummingOutputStream);
@@ -141,7 +141,7 @@ public void writeAndFlushData(@Nonnull OutputStream outputStream) throws IOExcep
}
private static class ChecksummingOutputStream extends FilterOutputStream {
- @Nonnull private final Checksum checksummer;
+ @NonNull private final Checksum checksummer;
private ChecksummingOutputStream(OutputStream out) {
super(out);
@@ -177,13 +177,13 @@ public long getChecksumValue() {
}
private static interface OutputStreamCallback {
- void writeAndFlushData(@Nonnull final OutputStream outputStream) throws IOException;
+ void writeAndFlushData(@NonNull final OutputStream outputStream) throws IOException;
}
// return a reference to a temp file that contains the written + flushed + fsynced + closed data
- @Nonnull
+ @NonNull
private static File writeDataToTempFileOrDie2(
- @Nonnull final OutputStreamCallback callback, @Nonnull final File targetFile)
+ @NonNull final OutputStreamCallback callback, @NonNull final File targetFile)
throws IOException {
Preconditions.checkNotNull(callback, "callback argument is required!");
Preconditions.checkNotNull(targetFile, "targetFile argument is required!");
@@ -229,18 +229,18 @@ private static File writeDataToTempFileOrDie2(
* java.io.File)}
*/
@Deprecated
- @Nonnull
+ @NonNull
private static File writeDataToTempFileOrDie(
- @Nonnull final OutputStreamCallback callback,
- @Nonnull final File targetFile,
- @Nonnull final org.apache.log4j.Logger log)
+ @NonNull final OutputStreamCallback callback,
+ @NonNull final File targetFile,
+ final org.apache.log4j.@NonNull Logger log)
throws IOException {
return writeDataToTempFileOrDie2(callback, targetFile);
}
- @Nonnull
+ @NonNull
private static File writeTextToTempFileOrDie2(
- @Nonnull final String[] text, @Nonnull final File targetFile) throws IOException {
+ @NonNull final String[] text, @NonNull final File targetFile) throws IOException {
Preconditions.checkNotNull(text, "callback argument is required!");
Preconditions.checkNotNull(targetFile, "targetFile argument is required!");
@@ -275,11 +275,11 @@ private static File writeTextToTempFileOrDie2(
/** @deprecated Use {@link #writeTextToTempFileOrDie2(java.lang.String[], java.io.File)} */
@Deprecated
- @Nonnull
+ @NonNull
private static File writeTextToTempFileOrDie(
- @Nonnull final String[] text,
- @Nonnull final File targetFile,
- @Nonnull final org.apache.log4j.Logger log)
+ @NonNull final String[] text,
+ @NonNull final File targetFile,
+ final org.apache.log4j.@NonNull Logger log)
throws IOException {
return writeTextToTempFileOrDie2(text, targetFile);
}
@@ -297,7 +297,7 @@ private static File writeTextToTempFileOrDie(
* synced, or closed
*/
public static boolean writeObjectIfChangedOrDie2(
- @Nonnull final Object obj, @Nonnull final String file) throws IOException {
+ @NonNull final Object obj, @NonNull final String file) throws IOException {
Preconditions.checkNotNull(file, "file argument is required!");
Preconditions.checkArgument(!file.isEmpty(), "file argument is required!");
@@ -364,15 +364,15 @@ public static boolean writeObjectIfChangedOrDie2(
/** @deprecated Use {@link #writeObjectIfChangedOrDie2(java.lang.Object, java.lang.String)} */
@Deprecated
public static boolean writeObjectIfChangedOrDie(
- @Nonnull final Object obj,
- @Nonnull final String file,
- @Nonnull final org.apache.log4j.Logger log)
+ @NonNull final Object obj,
+ @NonNull final String file,
+ final org.apache.log4j.@NonNull Logger log)
throws IOException {
return writeObjectIfChangedOrDie2(obj, file);
}
public static long computeFileChecksum(
- @Nonnull final File file, @Nonnull final Checksum checksum) throws IOException {
+ @NonNull final File file, @NonNull final Checksum checksum) throws IOException {
return com.google.common.io.Files.asByteSource(file).hash(Hashing.crc32()).padToLong();
}
@@ -694,7 +694,7 @@ public static void writeToTextFile(String[] lines, String file) {
}
public static void writeToTextFileOrDie(
- @Nonnull final String[] lines, @Nonnull final String file) throws IOException {
+ @NonNull final String[] lines, @NonNull final String file) throws IOException {
// Write out a temp file (or die)
final File f = new File(file);
final File temp = writeTextToTempFileOrDie2(lines, f);
@@ -778,7 +778,7 @@ public static boolean delete(String file) {
* @return true if all deletions were successful, false if file did not exist
* @throws IOException if deletion fails and the file still exists at the end
*/
- public static boolean deleteOrDie(@Nonnull final String file) throws IOException {
+ public static boolean deleteOrDie(@NonNull final String file) throws IOException {
// this returns true if the file was actually deleted
// and false for 2 cases:
// 1. file did not exist to start with
@@ -893,8 +893,8 @@ public static String getFileHash(final String file, final String algorithm)
* Converts a byte array to a hex string. The String returned will be of length exactly {@code
* bytes.length * 2}.
*/
- @Nonnull
- public static String toHex(@Nonnull final byte[] bytes) {
+ @NonNull
+ public static String toHex(@NonNull final byte[] bytes) {
StringBuilder buf = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
String hexDigits = Integer.toHexString((int) b & 0x00ff);
diff --git a/io/src/main/java/com/indeed/util/io/SafeFiles.java b/io/src/main/java/com/indeed/util/io/SafeFiles.java
index 5a68941..04fc69e 100644
--- a/io/src/main/java/com/indeed/util/io/SafeFiles.java
+++ b/io/src/main/java/com/indeed/util/io/SafeFiles.java
@@ -4,13 +4,11 @@
import com.google.common.base.Charsets;
import com.google.common.base.Throwables;
import com.indeed.util.core.io.Closeables2;
+import com.indeed.util.core.nullsafety.ParametersAreNonnullByDefault;
+import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nonnegative;
-import javax.annotation.Nonnull;
-import javax.annotation.ParametersAreNonnullByDefault;
-import javax.annotation.concurrent.NotThreadSafe;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
@@ -93,7 +91,6 @@ public static void ensureDirectoryExists(final Path path) throws IOException {
* @return number of NormalFiles fsynced (not including directories).
* @throws IOException in the event that we could not fsync the provided directory.
*/
- @Nonnegative
public static int fsyncRecursive(final Path root) throws IOException {
final FsyncingSimpleFileVisitor visitor = new FsyncingSimpleFileVisitor();
Files.walkFileTree(root, visitor);
@@ -101,9 +98,8 @@ public static int fsyncRecursive(final Path root) throws IOException {
}
private static class FsyncingSimpleFileVisitor extends SimpleFileVisitor {
- @Nonnegative private int fileCount = 0;
+ private int fileCount = 0;
- @Nonnegative
public int getFileCount() {
return fileCount;
}
@@ -214,7 +210,7 @@ public static void write(final byte[] data, final Path path) throws IOException
* @return handle to opened temp file
* @throws IOException in the event that the file could not be created.
*/
- @Nonnull
+ @NonNull
public static SafeOutputStream createAtomicFile(final Path path) throws IOException {
final Path dir = path.getParent();
final Path name = path.getFileName();
@@ -264,15 +260,17 @@ public static void deleteIfExistsQuietly(final Path path) {
}
}
- /** @see SafeFiles#createAtomicFile(Path) */
+ /**
+ * @see SafeFiles#createAtomicFile(Path)
+ * NotThreadSafe
+ */
@ParametersAreNonnullByDefault
- @NotThreadSafe
private static class SafeFileOutputStream extends SafeOutputStream {
- @Nonnull private final Path path;
- @Nonnull private final Path tempFile;
+ @NonNull private final Path path;
+ @NonNull private final Path tempFile;
- @Nonnull private final OutputStream out; // do not close this
- @Nonnull private final FileChannel fileChannel; // only close this
+ @NonNull private final OutputStream out; // do not close this
+ @NonNull private final FileChannel fileChannel; // only close this
private boolean closed = false;
diff --git a/io/src/main/java/com/indeed/util/io/SafeOutputStream.java b/io/src/main/java/com/indeed/util/io/SafeOutputStream.java
index 78f7bdc..042681b 100644
--- a/io/src/main/java/com/indeed/util/io/SafeOutputStream.java
+++ b/io/src/main/java/com/indeed/util/io/SafeOutputStream.java
@@ -1,8 +1,8 @@
// Copyright 2015 Indeed
package com.indeed.util.io;
-import javax.annotation.Nonnull;
-import javax.annotation.concurrent.NotThreadSafe;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
@@ -10,8 +10,10 @@
import java.nio.channels.WritableByteChannel;
import java.nio.file.Path;
-/** @see SafeFiles#createAtomicFile(Path) */
-@NotThreadSafe
+/**
+ * @see SafeFiles#createAtomicFile(Path)
+ *
Not Thread Safe
+ */
public abstract class SafeOutputStream extends OutputStream
implements WritableByteChannel, Closeable {
/**
@@ -25,7 +27,7 @@ public abstract class SafeOutputStream extends OutputStream
* @throws IOException in the event that the buffer could not be written.
*/
@Override
- public abstract int write(@Nonnull final ByteBuffer src) throws IOException;
+ public abstract int write(@NonNull final ByteBuffer src) throws IOException;
/**
* Commit causes the current atomic file writing operation to conclude and the current temp file
diff --git a/io/src/main/java/com/indeed/util/io/checkpointer/FileBasedCheckpointer.java b/io/src/main/java/com/indeed/util/io/checkpointer/FileBasedCheckpointer.java
index c5ba276..7966811 100644
--- a/io/src/main/java/com/indeed/util/io/checkpointer/FileBasedCheckpointer.java
+++ b/io/src/main/java/com/indeed/util/io/checkpointer/FileBasedCheckpointer.java
@@ -4,8 +4,8 @@
import com.google.common.base.Preconditions;
import com.indeed.util.io.BufferedFileDataOutputStream;
import com.indeed.util.serialization.Stringifier;
+import org.checkerframework.checker.nullness.qual.NonNull;
-import javax.annotation.Nonnull;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -23,17 +23,17 @@ public class FileBasedCheckpointer implements Checkpointer {
private volatile T value;
public FileBasedCheckpointer(
- @Nonnull final File checkpointFile,
- @Nonnull Stringifier stringifier,
- @Nonnull T defaultValue)
+ @NonNull final File checkpointFile,
+ @NonNull Stringifier stringifier,
+ @NonNull T defaultValue)
throws IOException {
this(checkpointFile.toPath(), stringifier, defaultValue);
}
public FileBasedCheckpointer(
- @Nonnull final Path checkpointFilePath,
- @Nonnull Stringifier stringifier,
- @Nonnull T defaultValue)
+ @NonNull final Path checkpointFilePath,
+ @NonNull Stringifier stringifier,
+ @NonNull T defaultValue)
throws IOException {
this.checkpointFilePath =
Preconditions.checkNotNull(checkpointFilePath, "no checkpoint file");
diff --git a/log4j1dummyshim/build.gradle b/log4j1dummyshim/build.gradle
index 075ba3d..15b6148 100644
--- a/log4j1dummyshim/build.gradle
+++ b/log4j1dummyshim/build.gradle
@@ -1,3 +1,5 @@
plugins {
id 'java'
}
+
+indeedOss.activateFeature 'java'
diff --git a/settings.gradle b/settings.gradle
index cd634bf..da9848c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -9,3 +9,4 @@ include 'util-core'
include 'varexport'
include 'zk'
include 'log4j1dummyshim'
+include 'checker-qual-shim'
diff --git a/util-core/build.gradle b/util-core/build.gradle
index 1337bd3..1de1dbf 100644
--- a/util-core/build.gradle
+++ b/util-core/build.gradle
@@ -1,18 +1,22 @@
indeedOss.activateFeature 'library'
indeedLibrary.name = 'util-core'
+configurations {
+ checkerQualPatch
+}
+sourceSets.main.compileClasspath = configurations.checkerQualPatch + sourceSets.main.compileClasspath
+
dependencies {
+ checkerQualPatch project(':checker-qual-shim')
implementation project(':varexport')
implementation libs.guava
implementation libs.slf4jApi
+ api libs.checkerQual
// This is a fake copy of the log4j1 Logger, which allows us to continue
// accepting it in our deprecated method signatures without accidentially using
// any of its functionality.
compileOnly project(':log4j1dummyshim')
- compileOnly libs.jsr305
- testCompileOnly libs.jsr305
-
testImplementation libs.junit
}
diff --git a/util-core/src/main/java/com/indeed/util/core/NetUtils.java b/util-core/src/main/java/com/indeed/util/core/NetUtils.java
index ccb5b01..3261a74 100644
--- a/util-core/src/main/java/com/indeed/util/core/NetUtils.java
+++ b/util-core/src/main/java/com/indeed/util/core/NetUtils.java
@@ -2,11 +2,11 @@
import com.google.common.base.Optional;
import com.google.common.base.Strings;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -33,7 +33,7 @@ public class NetUtils {
* @return hostname
* @throws java.net.UnknownHostException unable to lookup a host name correctly
*/
- @Nonnull
+ @NonNull
public static String determineHostName() throws UnknownHostException {
if (!OPT_HOSTNAME.isPresent()) {
final String hostName = InetAddress.getLocalHost().getHostName();
@@ -58,8 +58,8 @@ public static String determineHostName() throws UnknownHostException {
* @param defaultValue The default hostname to use in the event one is not preset.
* @return The detected hostname if present, or the default value.
*/
- @Nonnull
- public static String determineHostName(@Nonnull final String defaultValue) {
+ @NonNull
+ public static String determineHostName(@NonNull final String defaultValue) {
checkNotNull(defaultValue, "Unable to use default value of null for hostname");
if (!OPT_HOSTNAME.isPresent()) {
try {
diff --git a/util-core/src/main/java/com/indeed/util/core/Pair.java b/util-core/src/main/java/com/indeed/util/core/Pair.java
index 17662e5..432ff04 100644
--- a/util-core/src/main/java/com/indeed/util/core/Pair.java
+++ b/util-core/src/main/java/com/indeed/util/core/Pair.java
@@ -1,9 +1,9 @@
package com.indeed.util.core;
import com.google.common.base.Function;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.Comparator;
@@ -82,10 +82,10 @@ public int compare(Pair o1, Pair o2) {
* @return a Function that returns null or the first value of the Pair, which also may be null
* @deprecated use {@link #fst()}.
*/
- @Nonnull
+ @NonNull
@Deprecated
public static Function, ? extends T1> fst(
- @Nonnull final Class clazz) {
+ @NonNull final Class clazz) {
return new First<>();
}
@@ -108,7 +108,7 @@ public T1 apply(@Nullable Pair extends T1, ? extends T2> input) {
* @param The expected data type of {@link Pair#a}
* @return a Function that returns null or the first value of the Pair, which also may be null
*/
- @Nonnull
+ @NonNull
public static Function, ? extends T> fst() {
return new Function, T>() {
@Nullable
@@ -131,10 +131,10 @@ public T apply(@Nullable Pair extends T, ?> input) {
* @param Expected data type for {@link Pair#b}
* @return A function that returns null or the second value of the Pair, which also may be null
*/
- @Nonnull
+ @NonNull
@Deprecated
public static Function, ? extends T2> snd(
- @Nonnull final Class clazz) {
+ @NonNull final Class clazz) {
return new Second<>();
}
@@ -157,7 +157,7 @@ public T2 apply(@Nullable Pair extends T1, ? extends T2> input) {
* @param The expected data type for {@link Pair#b}.
* @return a Function that returns null or the second value of the Pair, which also may be null
*/
- @Nonnull
+ @NonNull
public static Function, ? extends T> snd() {
return new Function, T>() {
@Nullable
diff --git a/util-core/src/main/java/com/indeed/util/core/ReleaseVersion.java b/util-core/src/main/java/com/indeed/util/core/ReleaseVersion.java
index feb8968..918f8c0 100644
--- a/util-core/src/main/java/com/indeed/util/core/ReleaseVersion.java
+++ b/util-core/src/main/java/com/indeed/util/core/ReleaseVersion.java
@@ -1,6 +1,7 @@
package com.indeed.util.core;
-import javax.annotation.Nullable;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
import java.util.Arrays;
/**
diff --git a/util-core/src/main/java/com/indeed/util/core/TreeTimer.java b/util-core/src/main/java/com/indeed/util/core/TreeTimer.java
index ddc7093..84e7d70 100644
--- a/util-core/src/main/java/com/indeed/util/core/TreeTimer.java
+++ b/util-core/src/main/java/com/indeed/util/core/TreeTimer.java
@@ -1,6 +1,7 @@
package com.indeed.util.core;
-import javax.annotation.Nonnull;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Stack;
@@ -79,8 +80,8 @@ private static void printNode(int indent, Node n, StringBuilder ret) {
* Left-pads a String with spaces so it is length n
. If the String is already at
* least length n, no padding is done.
*/
- @Nonnull
- private static String leftpad(@Nonnull String s, int n) {
+ @NonNull
+ private static String leftpad(@NonNull String s, int n) {
return leftpad(s, n, ' ');
}
@@ -88,8 +89,8 @@ private static String leftpad(@Nonnull String s, int n) {
* Left-pads a String with the specific padChar so it is length n
. If the String is
* already at least length n, no padding is done.
*/
- @Nonnull
- private static String leftpad(@Nonnull String s, int n, char padChar) {
+ @NonNull
+ private static String leftpad(@NonNull String s, int n, char padChar) {
int diff = n - s.length();
if (diff <= 0) {
return s;
diff --git a/util-core/src/main/java/com/indeed/util/core/io/Closeables2.java b/util-core/src/main/java/com/indeed/util/core/io/Closeables2.java
index 6dc7be6..7eb6eed 100644
--- a/util-core/src/main/java/com/indeed/util/core/io/Closeables2.java
+++ b/util-core/src/main/java/com/indeed/util/core/io/Closeables2.java
@@ -1,11 +1,11 @@
package com.indeed.util.core.io;
import com.google.common.base.Throwables;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import java.io.Closeable;
import java.util.Arrays;
@@ -36,7 +36,7 @@ public static void close(@Nullable final Closeable closeable) {
/** @deprecated Use {@link #close(java.io.Closeable)} */
@Deprecated
public static void closeQuietly(
- @Nullable final Closeable closeable, @Nonnull final org.apache.log4j.Logger log) {
+ @Nullable final Closeable closeable, final org.apache.log4j.@NonNull Logger log) {
close(closeable);
}
@@ -46,7 +46,7 @@ public static void closeQuietly(
*
* @param closeables The closeables that we want to close.
*/
- public static void close(@Nonnull final Iterable extends Closeable> closeables) {
+ public static void close(@NonNull final Iterable extends Closeable> closeables) {
Throwable throwable = null;
for (Closeable closeable : closeables) {
try {
@@ -67,8 +67,8 @@ public static void close(@Nonnull final Iterable extends Closeable> closeables
/** @deprecated Use {@link #close(java.lang.Iterable)} */
@Deprecated
public static void closeAll(
- @Nonnull final Iterable extends Closeable> closeables,
- @Nonnull final org.apache.log4j.Logger log) {
+ @NonNull final Iterable extends Closeable> closeables,
+ final org.apache.log4j.@NonNull Logger log) {
close(closeables);
}
@@ -78,22 +78,22 @@ public static void closeAll(
*
* @param closeables The closeables that we want to close.
*/
- public static void close(@Nonnull final Closeable... closeables) {
+ public static void close(@NonNull final Closeable... closeables) {
close(Arrays.asList(closeables));
}
/** @deprecated Use {@link #close(java.io.Closeable...)} */
@Deprecated
public static void closeAll(
- @Nonnull final org.apache.log4j.Logger log, @Nonnull final Closeable... closeables) {
+ final org.apache.log4j.@NonNull Logger log, @NonNull final Closeable... closeables) {
close(closeables);
}
/** @deprecated Use {@link #close(java.io.Closeable...)} */
@Deprecated
public static void closeAll(
- @Nonnull final org.apache.log4j.Logger log,
- @Nonnull final Iterable extends Closeable> closeables) {
+ final org.apache.log4j.@NonNull Logger log,
+ @NonNull final Iterable extends Closeable> closeables) {
close(closeables);
}
@@ -111,7 +111,7 @@ public static void closeAll(
*/
@Deprecated
public static Closeable forIterable(
- @Nonnull final org.apache.log4j.Logger log, @Nonnull final Iterable closeables) {
+ final org.apache.log4j.@NonNull Logger log, @NonNull final Iterable closeables) {
return () -> close(closeables);
}
@@ -128,7 +128,7 @@ public static Closeable forIterable(
*/
@Deprecated
public static Closeable forArray(
- @Nonnull final org.apache.log4j.Logger log, @Nonnull final Closeable... closeables) {
+ final org.apache.log4j.@NonNull Logger log, @NonNull final Closeable... closeables) {
return () -> close(closeables);
}
}
diff --git a/util-core/src/main/java/com/indeed/util/core/nullsafety/FieldsAreNonnullByDefault.java b/util-core/src/main/java/com/indeed/util/core/nullsafety/FieldsAreNonnullByDefault.java
index 49c60af..347e3d7 100644
--- a/util-core/src/main/java/com/indeed/util/core/nullsafety/FieldsAreNonnullByDefault.java
+++ b/util-core/src/main/java/com/indeed/util/core/nullsafety/FieldsAreNonnullByDefault.java
@@ -1,20 +1,21 @@
package com.indeed.util.core.nullsafety;
-import javax.annotation.Nonnull;
-import javax.annotation.meta.TypeQualifierDefault;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.checkerframework.framework.qual.TypeUseLocation;
+
import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Annotation for types or packages which assumes all member variables are @Nonnull unless indicated
- * otherwise with @Nullable
+ * otherwise with {@link org.checkerframework.checker.nullness.qual.Nullable}
*
- * @author rboyer
+ * @deprecated Use {@link NonnullByDefault}
*/
+@Deprecated
@Documented
-@Nonnull
-@TypeQualifierDefault({ElementType.FIELD})
+@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldsAreNonnullByDefault {}
diff --git a/util-core/src/main/java/com/indeed/util/core/nullsafety/NonnullByDefault.java b/util-core/src/main/java/com/indeed/util/core/nullsafety/NonnullByDefault.java
new file mode 100644
index 0000000..306e672
--- /dev/null
+++ b/util-core/src/main/java/com/indeed/util/core/nullsafety/NonnullByDefault.java
@@ -0,0 +1,21 @@
+package com.indeed.util.core.nullsafety;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.checkerframework.framework.qual.TypeUseLocation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Annotation for types or packages which assumes all parameters, member variables, and return
+ * values are @Nonnull unless indicated otherwise with {@link
+ * org.checkerframework.checker.nullness.qual.Nullable}
+ */
+@Documented
+@DefaultQualifier(
+ value = NonNull.class,
+ locations = {TypeUseLocation.FIELD, TypeUseLocation.RETURN, TypeUseLocation.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NonnullByDefault {}
diff --git a/util-core/src/main/java/com/indeed/util/core/nullsafety/ParametersAreNonnullByDefault.java b/util-core/src/main/java/com/indeed/util/core/nullsafety/ParametersAreNonnullByDefault.java
new file mode 100644
index 0000000..d79629a
--- /dev/null
+++ b/util-core/src/main/java/com/indeed/util/core/nullsafety/ParametersAreNonnullByDefault.java
@@ -0,0 +1,21 @@
+package com.indeed.util.core.nullsafety;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.checkerframework.framework.qual.TypeUseLocation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Annotation for types or packages which warns if null is passed as a parameter to a method, if the
+ * parameter is not annotated with {@link org.checkerframework.checker.nullness.qual.Nullable}
+ *
+ * @deprecated Use {@link NonnullByDefault}
+ */
+@Deprecated
+@Documented
+@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ParametersAreNonnullByDefault {}
diff --git a/util-core/src/main/java/com/indeed/util/core/nullsafety/ReturnValuesAreNonnullByDefault.java b/util-core/src/main/java/com/indeed/util/core/nullsafety/ReturnValuesAreNonnullByDefault.java
index 90ea5a3..feb27c3 100644
--- a/util-core/src/main/java/com/indeed/util/core/nullsafety/ReturnValuesAreNonnullByDefault.java
+++ b/util-core/src/main/java/com/indeed/util/core/nullsafety/ReturnValuesAreNonnullByDefault.java
@@ -1,18 +1,21 @@
package com.indeed.util.core.nullsafety;
-import javax.annotation.Nonnull;
-import javax.annotation.meta.TypeQualifierDefault;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.checkerframework.framework.qual.TypeUseLocation;
+
import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * @author jplaisance annotation for types or packages which warns if null is returned from a method
- * not annotated with @Nullable
+ * Annotation for types or packages which warns if null is returned from a method not annotated with
+ * {@link org.checkerframework.checker.nullness.qual.Nullable}
+ *
+ * @deprecated Use {@link NonnullByDefault}
*/
+@Deprecated
@Documented
-@Nonnull
-@TypeQualifierDefault(ElementType.METHOD)
+@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.RETURN)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReturnValuesAreNonnullByDefault {}
diff --git a/util-core/src/main/java/com/indeed/util/core/reference/AtomicSharedReference.java b/util-core/src/main/java/com/indeed/util/core/reference/AtomicSharedReference.java
index 64fe170..fba003d 100644
--- a/util-core/src/main/java/com/indeed/util/core/reference/AtomicSharedReference.java
+++ b/util-core/src/main/java/com/indeed/util/core/reference/AtomicSharedReference.java
@@ -2,10 +2,10 @@
import com.google.common.base.Function;
import com.indeed.util.core.io.Closeables2;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
diff --git a/util-core/src/main/java/com/indeed/util/core/reference/ReloadableSharedReference.java b/util-core/src/main/java/com/indeed/util/core/reference/ReloadableSharedReference.java
index 426e846..b8b00d1 100644
--- a/util-core/src/main/java/com/indeed/util/core/reference/ReloadableSharedReference.java
+++ b/util-core/src/main/java/com/indeed/util/core/reference/ReloadableSharedReference.java
@@ -2,10 +2,10 @@
import com.google.common.base.Supplier;
import com.indeed.util.core.io.Closeables2;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
diff --git a/util-core/src/main/java/com/indeed/util/core/reference/SharedReference.java b/util-core/src/main/java/com/indeed/util/core/reference/SharedReference.java
index d01da62..9e3a604 100644
--- a/util-core/src/main/java/com/indeed/util/core/reference/SharedReference.java
+++ b/util-core/src/main/java/com/indeed/util/core/reference/SharedReference.java
@@ -1,9 +1,9 @@
package com.indeed.util.core.reference;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
diff --git a/util-core/src/main/java/com/indeed/util/core/sort/Quicksortables.java b/util-core/src/main/java/com/indeed/util/core/sort/Quicksortables.java
index 85098a6..d064e2e 100644
--- a/util-core/src/main/java/com/indeed/util/core/sort/Quicksortables.java
+++ b/util-core/src/main/java/com/indeed/util/core/sort/Quicksortables.java
@@ -2,8 +2,8 @@
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Ints;
+import org.checkerframework.checker.nullness.qual.NonNull;
-import javax.annotation.Nonnull;
import java.util.Random;
/** @author ahudson */
@@ -51,7 +51,7 @@ public int compare(int a, int b) {
}
public static Quicksortable getQuicksortableParallelArrays(
- @Nonnull final long[] array1, @Nonnull final int[] array2) {
+ final long @NonNull [] array1, final int @NonNull [] array2) {
return new Quicksortable() {
public void swap(int i, int j) {
Quicksortables.swap(array1, i, j);
diff --git a/util-core/src/main/java/com/indeed/util/core/threads/ThreadSafeBitSet.java b/util-core/src/main/java/com/indeed/util/core/threads/ThreadSafeBitSet.java
index 4ae275c..ea44e36 100644
--- a/util-core/src/main/java/com/indeed/util/core/threads/ThreadSafeBitSet.java
+++ b/util-core/src/main/java/com/indeed/util/core/threads/ThreadSafeBitSet.java
@@ -1,6 +1,7 @@
package com.indeed.util.core.threads;
-import javax.annotation.Nullable;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
import java.io.Serializable;
import java.util.Arrays;
diff --git a/util-core/src/main/java/com/indeed/util/core/time/DefaultWallClock.java b/util-core/src/main/java/com/indeed/util/core/time/DefaultWallClock.java
index a67bf73..4c8d545 100644
--- a/util-core/src/main/java/com/indeed/util/core/time/DefaultWallClock.java
+++ b/util-core/src/main/java/com/indeed/util/core/time/DefaultWallClock.java
@@ -1,14 +1,12 @@
package com.indeed.util.core.time;
-import javax.annotation.concurrent.ThreadSafe;
-
/**
* Default {@link WallClock} implementation using system current time.
*
* @deprecated with {@link WallClock}. Use an instance of {@link java.time.Clock} instead.
* @author patrick@indeed.com
+ * ThreadSafe
*/
-@ThreadSafe
public class DefaultWallClock implements WallClock {
@Override
public long currentTimeMillis() {
diff --git a/util-core/src/main/java/com/indeed/util/core/time/StoppedClock.java b/util-core/src/main/java/com/indeed/util/core/time/StoppedClock.java
index 86c1178..01d3dfc 100644
--- a/util-core/src/main/java/com/indeed/util/core/time/StoppedClock.java
+++ b/util-core/src/main/java/com/indeed/util/core/time/StoppedClock.java
@@ -1,6 +1,7 @@
package com.indeed.util.core.time;
-import javax.annotation.Nonnull;
+import org.checkerframework.checker.nullness.qual.NonNull;
+
import java.time.Instant;
import java.time.ZoneId;
import java.util.concurrent.TimeUnit;
@@ -14,7 +15,7 @@
*/
@Deprecated
public final class StoppedClock implements WallClock {
- @Nonnull private final AtomicLong millis;
+ @NonNull private final AtomicLong millis;
/** Creates a new stopped clock frozen at the current moment in time. */
public StoppedClock() {
@@ -47,7 +48,7 @@ public final void set(final long millis) {
* @param timeUnit The time unit that {@code value} is measured in.
* @return The time after being adjusted by the provided offset.
*/
- public final long plus(final long value, @Nonnull final TimeUnit timeUnit) {
+ public final long plus(final long value, @NonNull final TimeUnit timeUnit) {
return this.millis.addAndGet(timeUnit.toMillis(value));
}
diff --git a/util-core/src/test/java/com/indeed/util/core/reference/TestSharedReference.java b/util-core/src/test/java/com/indeed/util/core/reference/TestSharedReference.java
index dba6c83..6e36fc2 100644
--- a/util-core/src/test/java/com/indeed/util/core/reference/TestSharedReference.java
+++ b/util-core/src/test/java/com/indeed/util/core/reference/TestSharedReference.java
@@ -1,8 +1,8 @@
package com.indeed.util.core.reference;
import junit.framework.TestCase;
+import org.checkerframework.checker.nullness.qual.Nullable;
-import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
diff --git a/varexport/build.gradle b/varexport/build.gradle
index 4884e86..df1b854 100644
--- a/varexport/build.gradle
+++ b/varexport/build.gradle
@@ -5,7 +5,7 @@ dependencies {
implementation libs.guava
implementation libs.slf4jApi
- compileOnly libs.jsr305
+ api libs.checkerQual
compileOnly libs.servletApi
testImplementation libs.junit
diff --git a/varexport/src/main/java/com/indeed/util/varexport/VarExporter.java b/varexport/src/main/java/com/indeed/util/varexport/VarExporter.java
index 675d5a4..7118efc 100644
--- a/varexport/src/main/java/com/indeed/util/varexport/VarExporter.java
+++ b/varexport/src/main/java/com/indeed/util/varexport/VarExporter.java
@@ -13,11 +13,11 @@
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.AnnotatedElement;
@@ -109,7 +109,7 @@ public static synchronized VarExporter forNamespace(String namespace) {
* namespace does not exist
*/
public static synchronized Optional forNamespaceIfExists(
- @Nonnull final String namespace) {
+ @NonNull final String namespace) {
return Optional.ofNullable(namespaces.get(namespace));
}
@@ -122,7 +122,7 @@ public static synchronized Optional forNamespaceIfExists(
* @return exporter for the given class will be created if never before accessed.
*/
public static synchronized VarExporter forNamespace(
- @Nonnull final Class> clazz, final boolean declaredFieldsOnly) {
+ @NonNull final Class> clazz, final boolean declaredFieldsOnly) {
return getInstance(clazz.getSimpleName(), clazz, declaredFieldsOnly);
}
diff --git a/varexport/src/main/java/com/indeed/util/varexport/servlet/ViewExportedVariablesServlet.java b/varexport/src/main/java/com/indeed/util/varexport/servlet/ViewExportedVariablesServlet.java
index c8600e3..2f73bc1 100644
--- a/varexport/src/main/java/com/indeed/util/varexport/servlet/ViewExportedVariablesServlet.java
+++ b/varexport/src/main/java/com/indeed/util/varexport/servlet/ViewExportedVariablesServlet.java
@@ -16,10 +16,10 @@
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;