diff --git a/.classpath b/.classpath deleted file mode 100644 index 746962e..0000000 --- a/.classpath +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore index 9ec8e66..52c2b82 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,8 @@ hs_err_pid* /target/ /repository/ + +# IDE files +/.project +/.classpath +/.settings/ \ No newline at end of file diff --git a/.project b/.project deleted file mode 100644 index dcd6357..0000000 --- a/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - Akiwrapper - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - - diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index cdfe4f1..0000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding//src/test/java=UTF-8 -encoding//src/test/resources=UTF-8 -encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 76e3512..0000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,133 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.builder.annotationPath.allLocations=disabled -org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled -org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnull=javax.annotation.Nonnull -org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=,edu.umd.cs.findbugs.annotations.NonNull,org.eclipse.jdt.annotation.NonNull -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= -org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable -org.eclipse.jdt.core.compiler.annotation.nullable.secondary=,edu.umd.cs.findbugs.annotations.Nullable,org.eclipse.jdt.annotation.Nullable -org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=11 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.doc.comment.support=enabled -org.eclipse.jdt.core.compiler.problem.APILeak=warning -org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=enabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=info -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=info -org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=info -org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error -org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private -org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=info -org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning -org.eclipse.jdt.core.compiler.problem.missingJavadocComments=info -org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public -org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag -org.eclipse.jdt.core.compiler.problem.missingJavadocTags=info -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=info -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning -org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.problem.nullReference=error -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.parameterAssignment=info -org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning -org.eclipse.jdt.core.compiler.problem.potentialNullReference=error -org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=error -org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=info -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=info -org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=info -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning -org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning -org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning -org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning -org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=error -org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled -org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=error -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=info -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=info -org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=info -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedImport=info -org.eclipse.jdt.core.compiler.problem.unusedLabel=info -org.eclipse.jdt.core.compiler.problem.unusedLocal=info -org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning -org.eclipse.jdt.core.compiler.problem.unusedParameter=info -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=info -org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=info -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=11 -org.eclipse.objectteams.otdt.compiler.option.pure_java=enabled diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f..0000000 --- a/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/.settings/org.sonarlint.eclipse.core.prefs b/.settings/org.sonarlint.eclipse.core.prefs deleted file mode 100644 index 55537ed..0000000 --- a/.settings/org.sonarlint.eclipse.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -autoEnabled=true -eclipse.preferences.version=1 -extraProperties= -fileExclusions=DIRECTORY\:target\r\nGLOB\:src/main/java/com/markozajc/akiwrapper/listbuilder/*.*\r\nDIRECTORY\:example diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c0327db..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: java -jdk: - - openjdk11 -os: linux -arch: - - arm64 - - ppc64le - - s390x -script: mvn clean verify -Dmaven.javadoc.skip=true -dist: xenial -cache: - directories: - - "$HOME/.m2/repository" - - "$HOME/apache-maven-3.8.1" -before_install: -- export M2_HOME=$HOME/apache-maven-3.8.1 -- if [ ! -d $M2_HOME/bin ]; then curl https://archive.apache.org/dist/maven/maven-3/3.8.1/binaries/apache-maven-3.8.1-bin.tar.gz | tar zxf - -C $HOME; fi -- export PATH=$M2_HOME/bin:$PATH diff --git a/pom.xml b/pom.xml index 2081446..9b50ef3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.github.markozajc akiwrapper - 1.5.2 + 1.5.2-2 Akiwrapper A Java API wrapper for Akinator @@ -35,8 +35,8 @@ UTF-8 UTF-8 - 5.8.2 - 1.7.36 + 5.9.3 + 2.0.7 11 11 @@ -47,7 +47,7 @@ com.jcabi jcabi-xml - 0.23.2 + 0.27.2 @@ -61,7 +61,7 @@ com.konghq unirest-java - 3.13.8 + 3.14.2 @@ -101,7 +101,7 @@ com.github.spotbugs spotbugs-annotations - 4.6.0 + 4.7.3 provided true @@ -119,6 +119,28 @@ + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.1.0 + + + enforce-maven + + enforce + + + + + 3.2.5 + + + + + + + maven-javadoc-plugin @@ -154,7 +176,7 @@ org.codehaus.mojo versions-maven-plugin - 2.10.0 + 2.11.0 file://${project.basedir}/versions-ruleset.xml @@ -218,4 +240,4 @@ - \ No newline at end of file + diff --git a/src/main/java/com/github/markozajc/akiwrapper/AkiwrapperBuilder.java b/src/main/java/com/github/markozajc/akiwrapper/AkiwrapperBuilder.java index d11fe05..e13b31a 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/AkiwrapperBuilder.java +++ b/src/main/java/com/github/markozajc/akiwrapper/AkiwrapperBuilder.java @@ -32,15 +32,11 @@ public class AkiwrapperBuilder { private static final Logger LOG = LoggerFactory.getLogger(AkiwrapperBuilder.class); - @Nullable - private UnirestInstance unirest; - @Nullable - private Server server; + @Nullable private UnirestInstance unirest; + @Nullable private Server server; private boolean filterProfanity; - @Nonnull - private Language language; - @Nonnull - private GuessType guessType; + @Nonnull private Language language; + @Nonnull private GuessType guessType; /** * The default profanity filter preference for new {@link Akiwrapper} instances. @@ -50,14 +46,12 @@ public class AkiwrapperBuilder { /** * The default {@link Language} for new {@link Akiwrapper} instances. */ - @Nonnull - public static final Language DEFAULT_LOCALIZATION = ENGLISH; + @Nonnull public static final Language DEFAULT_LOCALIZATION = ENGLISH; /** * The default {@link GuessType} for new {@link Akiwrapper} instances. */ - @Nonnull - public static final GuessType DEFAULT_GUESS_TYPE = CHARACTER; + @Nonnull public static final GuessType DEFAULT_GUESS_TYPE = CHARACTER; private AkiwrapperBuilder(@Nullable UnirestInstance unirest, @Nullable Server server, boolean filterProfanity, @Nonnull Language language, @Nonnull GuessType guessType) { diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/Route.java b/src/main/java/com/github/markozajc/akiwrapper/core/Route.java index 5b64def..309e297 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/Route.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/Route.java @@ -34,8 +34,7 @@ public final class Route { * to false may result in unpredicted exceptions! You usually don't need to alter * this value */ - @SuppressFBWarnings("MS_SHOULD_BE_FINAL") - public static boolean defaultRunChecks = true; // NOSONAR + @SuppressFBWarnings("MS_SHOULD_BE_FINAL") public static boolean defaultRunChecks = true; // NOSONAR /** * Creates a new session for further gameplay.
@@ -81,10 +80,8 @@ public final class Route { */ public static final Route LIST = new Route(1, "/list?mode_question=0&step=%s"); - @Nonnull - private final String path; - @Nonnull - private final String[] filterArguments; + @Nonnull private final String path; + @Nonnull private final String[] filterArguments; private final int parametersQuantity; @@ -182,10 +179,8 @@ public static class Request { private static final Logger LOG = LoggerFactory.getLogger(Route.Request.class); - @Nonnull - private final UnirestInstance unirest; - @Nonnull - private final String url; + @Nonnull private final UnirestInstance unirest; + @Nonnull private final String url; private final int jQueryCallbackLength; Request(@Nonnull UnirestInstance unirest, @Nonnull String url, int jQueryCallbackLength) { diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ApiKey.java b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ApiKey.java index d7af25e..5447ab4 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ApiKey.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ApiKey.java @@ -24,10 +24,8 @@ public class ApiKey { private static final String FORMAT = "frontaddr=%s&uid_ext_session=%s"; - @Nonnull - private final String sessionUid; - @Nonnull - private final String frontAddress; + @Nonnull private final String sessionUid; + @Nonnull private final String frontAddress; ApiKey(@Nonnull String sessionUid, @Nonnull String frontAddress) { this.sessionUid = sessionUid; diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/GuessImpl.java b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/GuessImpl.java index d6a9012..f2afa77 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/GuessImpl.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/GuessImpl.java @@ -13,16 +13,11 @@ public class GuessImpl implements Guess { - @Nonnull - private final String id; - @Nonnull - private final String name; - @Nullable - private final String description; - @Nullable - private final URL image; - @Nonnegative - private final double probability; + @Nonnull private final String id; + @Nonnull private final String name; + @Nullable private final String description; + @Nullable private final URL image; + @Nonnegative private final double probability; public GuessImpl(@Nonnull String id, @Nonnull String name, @Nullable String description, @Nullable URL image, @Nonnegative double probability) { diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/QuestionImpl.java b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/QuestionImpl.java index abea006..c5b77cf 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/QuestionImpl.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/QuestionImpl.java @@ -14,16 +14,11 @@ public class QuestionImpl implements Question { private static final String REASON_OUT_OF_QUESTIONS = "no question"; - @Nonnull - private final String id; - @Nonnull - private final String question; - @Nonnegative - private final int step; - @Nonnegative - private final double gain; - @Nonnegative - private final double progression; + @Nonnull private final String id; + @Nonnull private final String question; + @Nonnegative private final int step; + @Nonnegative private final double gain; + @Nonnegative private final double progression; public QuestionImpl(@Nonnull String id, @Nonnull String question, @Nonnegative int step, @Nonnegative double gain, @Nonnegative double progression, @Nonnull Status status) { diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ServerImpl.java b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ServerImpl.java index 3354706..7af6db8 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ServerImpl.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ServerImpl.java @@ -13,12 +13,9 @@ public class ServerImpl implements Server { private static final String LANGUAGE_ID_XPATH = "LANGUAGE/LANG_ID/text()"; // NOSONAR not a URL private static final String SUBJECT_ID_XPATH = "SUBJECT/SUBJ_ID/text()"; // NOSONAR not a URL private static final String CANDIDATE_URLS_XPATH = "CANDIDATS/*/text()"; // sic - @Nonnull - private final String url; - @Nonnull - private final Language localization; - @Nonnull - private final GuessType guessType; + @Nonnull private final String url; + @Nonnull private final Language localization; + @Nonnull private final GuessType guessType; public ServerImpl(@Nonnull String url, @Nonnull Language localization, @Nonnull GuessType guessType) { this.url = url; diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ServerListImpl.java b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ServerListImpl.java index cd4f540..2a3114c 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ServerListImpl.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/ServerListImpl.java @@ -10,10 +10,8 @@ public class ServerListImpl implements ServerList { - @Nonnull - private Server currentServer; - @Nonnull - private final Queue candidateServers; + @Nonnull private Server currentServer; + @Nonnull private final Queue candidateServers; @SuppressWarnings("null") public ServerListImpl(@Nonnull Server first, @Nonnull Server... candidates) { diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/StatusImpl.java b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/StatusImpl.java index 0f3e520..b3c2381 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/StatusImpl.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/entities/impl/immutable/StatusImpl.java @@ -15,10 +15,8 @@ public class StatusImpl implements Status { public static final StatusImpl STATUS_OK = new StatusImpl(Level.OK, null); - @Nullable - private final String reason; - @Nonnull - private final Level level; + @Nullable private final String reason; + @Nonnull private final Level level; private StatusImpl(@Nonnull Level level, @Nullable String reason) { this.level = level; diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/impl/AkiwrapperImpl.java b/src/main/java/com/github/markozajc/akiwrapper/core/impl/AkiwrapperImpl.java index 6c85422..7609cdb 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/impl/AkiwrapperImpl.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/impl/AkiwrapperImpl.java @@ -54,17 +54,12 @@ public String querystring() { } } - @Nonnull - private final Server server; - @Nonnull - private final UnirestInstance unirest; + @Nonnull private final Server server; + @Nonnull private final UnirestInstance unirest; private final boolean filterProfanity; - @Nonnull - private final Session session; - @Nonnegative - private int currentStep; - @Nullable - private Question question; + @Nonnull private final Session session; + @Nonnegative private int currentStep; + @Nullable private Question question; private List guessCache; @SuppressWarnings("null") @@ -115,7 +110,7 @@ public Question undoAnswer() { this.guessCache = null; Question current = getQuestion(); - if ((current == null) || (current.getStep() < 1)) + if (current == null || current.getStep() < 1) return null; var questionJson = CANCEL_ANSWER diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/utils/Servers.java b/src/main/java/com/github/markozajc/akiwrapper/core/utils/Servers.java index fb8d8c4..6f74a9b 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/utils/Servers.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/utils/Servers.java @@ -24,9 +24,8 @@ @SuppressFBWarnings("REC_CATCH_EXCEPTION") public final class Servers { - private static final String FOOTPRINT = "cd8e6509f3420878e18d75b9831b317f"; private static final String LIST_URL = - "https://global3.akinator.com/ws/instances_v2.php?media_id=14" + "&mode=https&footprint=" + FOOTPRINT; + "https://global3.akinator.com/ws/instances_v2.php?media_id=14&mode=https&footprint=cd8e6509f3420878e18d75b9831b317f"; private Servers() {} diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/utils/UnirestUtils.java b/src/main/java/com/github/markozajc/akiwrapper/core/utils/UnirestUtils.java index f361249..f418cb9 100644 --- a/src/main/java/com/github/markozajc/akiwrapper/core/utils/UnirestUtils.java +++ b/src/main/java/com/github/markozajc/akiwrapper/core/utils/UnirestUtils.java @@ -1,5 +1,6 @@ package com.github.markozajc.akiwrapper.core.utils; +import static com.github.markozajc.akiwrapper.core.utils.WorkaroundUtils.workaroundIncompleteChain; import static kong.unirest.Unirest.spawnInstance; import javax.annotation.Nonnull; @@ -50,7 +51,9 @@ public static synchronized void shutdownInstance() { * Configures a new {@link UnirestInstance} for use by Akiwrapper. Akinator's API * servers are quite picky about the headers you send to them so if you supply * {@link AkiwrapperBuilder} with your own {@link UnirestInstance} you should either - * pass it through this or configure it accordingly yourself.
+ * pass it through this or configure it accordingly yourself. This also applies the + * workaround to Akinator's incomplete SSL chain from + * {@link WorkaroundUtils#workaroundIncompleteChain(kong.unirest.Config)} .
* Note: even though this method returns a {@link UnirestInstance}, the instance you * pass to it is itself mutated and returned. The return value is only there for ease * of chaining. @@ -61,6 +64,7 @@ public static synchronized void shutdownInstance() { * @return the {@link UnirestInstance} you passed, used for chaining */ @Nonnull + @SuppressWarnings("null") public static UnirestInstance configureInstance(@Nonnull UnirestInstance unirest) { unirest.config() .addDefaultHeader("Accept", @@ -76,6 +80,8 @@ public static UnirestInstance configureInstance(@Nonnull UnirestInstance unirest "(KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36") .addDefaultHeader("Referer", "https://en.akinator.com/game") .cookieSpec("ignore"); + workaroundIncompleteChain(unirest.config()); + return unirest; } diff --git a/src/main/java/com/github/markozajc/akiwrapper/core/utils/WorkaroundUtils.java b/src/main/java/com/github/markozajc/akiwrapper/core/utils/WorkaroundUtils.java new file mode 100644 index 0000000..0cc1427 --- /dev/null +++ b/src/main/java/com/github/markozajc/akiwrapper/core/utils/WorkaroundUtils.java @@ -0,0 +1,149 @@ +package com.github.markozajc.akiwrapper.core.utils; + +import static java.util.Arrays.stream; + +import java.io.IOException; +import java.security.*; +import java.security.cert.*; + +import javax.annotation.Nonnull; +import javax.net.ssl.*; + +import kong.unirest.Config; + +/** + * A utility class with workarounds for problems with Akinator's infrastructure. + * + * @author Marko Zajc + */ +public class WorkaroundUtils { + + /** + * Applies a workaround for the {@code PKIX path building failed} exception to a + * Unirest {@link Config}.
+ * Note: even though this method returns a {@link Config}, the instance you pass to + * it is itself mutated and returned. The return value is only there for ease of + * chaining. + * + * @param config + * the {@link Config} to apply the workaround to + * + * @return the {@link Config} for chaining + * + * @implNote it seems that Akinator sysadmins have misconfigured the en.akinator.com + * server such that it fails to send the intermediate SSL certificate. + * Because we only trust the root certificate, the local certificate chain + * resolution fails and an exception is thrown. This workaround manually + * trusts the intermediate certificate such that the resolution succeeds + * without having to completely disable certificate validation. You can get + * the modified {@link SSLContext} to apply yourself with + * {@link #getIncompleteChainWorkaroundSSLContext()} or if you're using + * your own {@link SSLContext} and you want to add the + * {@link TrustManager}, you can get it from + * {@link #getIncompleteChainWorkaroundCustomTrustManager()}. + */ + @Nonnull + @SuppressWarnings("null") + public static Config workaroundIncompleteChain(@Nonnull Config config) { + return config.sslContext(getIncompleteChainWorkaroundSSLContext()); + } + + /** + * @return the {@link SSLContext} used to workaround the + * {@code PKIX path building failed} exception. + * + * @see #workaroundIncompleteChain(Config) + */ + @Nonnull + public static SSLContext getIncompleteChainWorkaroundSSLContext() { + try { + var defaultTrust = getDefaultTrustManager(); + var customTrust = getIncompleteChainWorkaroundCustomTrustManager(); + var combinedTrust = getCombinedTrust(defaultTrust, customTrust); + + var sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] { combinedTrust }, null); + return sslContext; + } catch (GeneralSecurityException | IOException e) { + throw new RuntimeException("Could not create a workaround SSLContext", e); + } + } + + @Nonnull + private static X509TrustManager getCombinedTrust(@Nonnull X509TrustManager defaultTrust, + @Nonnull X509TrustManager customTrust) { + return new X509TrustManager() { + + @Override + public X509Certificate[] getAcceptedIssuers() { + // merging isn't necessary + return defaultTrust.getAcceptedIssuers(); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + try { + defaultTrust.checkServerTrusted(chain, authType); + } catch (CertificateException e) { + customTrust.checkServerTrusted(chain, authType); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + // merging isn't necessary + defaultTrust.checkClientTrusted(chain, authType); + } + }; + } + + /** + * @return the {@link X509TrustManager} used to workaround the + * {@code PKIX path building failed} exception. + * + * @throws KeyStoreException + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws CertificateException + * + * @see #workaroundIncompleteChain(Config) + */ + @Nonnull + @SuppressWarnings("null") + public static X509TrustManager getIncompleteChainWorkaroundCustomTrustManager() throws KeyStoreException, + IOException, + NoSuchAlgorithmException, + CertificateException { + KeyStore store; + try (var is = WorkaroundUtils.class.getResourceAsStream("/intermediate.jks")) { + store = KeyStore.getInstance(KeyStore.getDefaultType()); + store.load(is, "thingamabob".toCharArray()); // NOSONAR not a secure credential + } + + if (store.size() != 1) + throw new IOException("The intermediate keystore does not contain exactly one entry (did loading fail?)"); + + var tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(store); + + return stream(tmf.getTrustManagers()).filter(X509TrustManager.class::isInstance) + .map(X509TrustManager.class::cast) + .findAny() + .orElseThrow(); + } + + @Nonnull + @SuppressWarnings("null") + private static X509TrustManager getDefaultTrustManager() throws NoSuchAlgorithmException, KeyStoreException { + var tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore) null); + + return stream(tmf.getTrustManagers()).filter(X509TrustManager.class::isInstance) + .map(X509TrustManager.class::cast) + .findAny() + .orElseThrow(); + } + + private WorkaroundUtils() {} + +} diff --git a/src/main/resources/intermediate.jks b/src/main/resources/intermediate.jks new file mode 100644 index 0000000..b29b21b Binary files /dev/null and b/src/main/resources/intermediate.jks differ diff --git a/src/main/resources/intermediate.jks.txt b/src/main/resources/intermediate.jks.txt new file mode 100644 index 0000000..a272934 --- /dev/null +++ b/src/main/resources/intermediate.jks.txt @@ -0,0 +1,2 @@ +This keystore contains the intermediate "Go Daddy Secure Certificate Authority - G2" key used by Akinator's server (because they're too incompetent to send it) +Password: thingamabob (because JKS requires a password) diff --git a/src/test/java/com/github/markozajc/akiwrapper/IntegrationTest.java b/src/test/java/com/github/markozajc/akiwrapper/IntegrationTest.java index 5c09f6d..0bd9ec9 100644 --- a/src/test/java/com/github/markozajc/akiwrapper/IntegrationTest.java +++ b/src/test/java/com/github/markozajc/akiwrapper/IntegrationTest.java @@ -1,6 +1,7 @@ package com.github.markozajc.akiwrapper; import static java.lang.String.format; +import static org.junit.jupiter.api.Assumptions.abort; import static org.slf4j.LoggerFactory.getLogger; import java.util.List; @@ -41,8 +42,7 @@ void testAkiwrapper(@Nonnull Language language, @Nonnull GuessType guessType) { try { api = new AkiwrapperBuilder().setLanguage(language).setGuessType(guessType).build(); } catch (ServerNotFoundException e) { - log.warn("Current combination not supported, server wasn't found."); - log.trace("", e); + abort("Current combination not supported, server wasn't found."); return; }